处理 GUI 应用程序中的复杂规则(C++ 或 C#)

发布于 2024-08-26 04:23:24 字数 2395 浏览 4 评论 0原文

我正在处理一个对话框,在启用“确定”按钮之前必须满足多个规则。

目前,页面上的任何操作(例如输入数据或从下拉列表中选择项目(除其他外))都会调用一个名为 ProcessEvent() 的函数 - 该函数处理所有逻辑并启用或禁用“确定”按钮。

我的问题是我发现很难使规则变得简洁易懂。

有些规则可以通过对话框中的另一个操作来否定,现在我已经得到了 if else 语句遍布各处,或者难以阅读和遵循的语句。延长。

下面的代码是问题的简化,但很好地演示了它。我如何更好地处理这个问题(如果可能的话)

bool CWorkstation::ProcessEvent(void)
    {   
        UpdateData();

        CharCount = GetDlgItemInt(IDC_CharCount, NULL, FALSE); //get latest

        if ( IsDlgButtonChecked(IDC_USEDBNAME))
            {   
                if (!IsDlgButtonChecked(IDC_MAXDBNAME))
                    {
                        EnableNext(TRUE);
                    }
            }

        if (IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount)
            {                   
                if (IsDlgButtonChecked(IDC_USEXMLNAME))
                    {
                        if ( PrefixName.IsEmpty() ) 
                            {
                                EnableNext(FALSE);
                            }
                        else
                            {
                                EnableNext(TRUE);
                            }
            }



            }   


        if (IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1)
            {
                EnableNext(TRUE);
            }



        if  ( IsDlgButtonChecked(IDC_WSAUTONAME) || IsDlgButtonChecked(IDC_RENAMEIFDUP))
            {

            // TRACE("IDC_WSAUTONAME is Checked\n");

            if ( IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1 ) 

                {   


                if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) ) 

                    {

                    EnableNext(TRUE);
                    }

                else if ( IsDlgButtonChecked(IDC_USELONGNAME) )

                    {

                    EnableNext(TRUE);

                    }

                else

                    {
                    EnableNext(FALSE);
                    }



                }


            if ( !IsDlgButtonChecked(IDC_USEPREFIX) )

                {


                if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) ||  IsDlgButtonChecked(IDC_USELONGNAME) )

                    {
                    EnableNext(TRUE);
                    }

                }


            return false;


            }

        } 

Im working on a dialog box in which several rules must be satisfied before the OK button is enabled.

Currently any action on the page such as entering data or selecting an item from a drop down list (amongst other things) calls a single function called ProcessEvent() - this function handles all logic and either enables or disables the OK button.

My problem is I finding it difficult making the rules concise and understandable.

Some of the rules can be negated by another action on the dialog and I have now ended up with if else statements all over the place or which are difficult to read and follow & extend.

The code below is a simplification of the problem but demonstrates it well. How do I handle this problem better (If its Possible)

bool CWorkstation::ProcessEvent(void)
    {   
        UpdateData();

        CharCount = GetDlgItemInt(IDC_CharCount, NULL, FALSE); //get latest

        if ( IsDlgButtonChecked(IDC_USEDBNAME))
            {   
                if (!IsDlgButtonChecked(IDC_MAXDBNAME))
                    {
                        EnableNext(TRUE);
                    }
            }

        if (IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount)
            {                   
                if (IsDlgButtonChecked(IDC_USEXMLNAME))
                    {
                        if ( PrefixName.IsEmpty() ) 
                            {
                                EnableNext(FALSE);
                            }
                        else
                            {
                                EnableNext(TRUE);
                            }
            }



            }   


        if (IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1)
            {
                EnableNext(TRUE);
            }



        if  ( IsDlgButtonChecked(IDC_WSAUTONAME) || IsDlgButtonChecked(IDC_RENAMEIFDUP))
            {

            // TRACE("IDC_WSAUTONAME is Checked\n");

            if ( IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1 ) 

                {   


                if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) ) 

                    {

                    EnableNext(TRUE);
                    }

                else if ( IsDlgButtonChecked(IDC_USELONGNAME) )

                    {

                    EnableNext(TRUE);

                    }

                else

                    {
                    EnableNext(FALSE);
                    }



                }


            if ( !IsDlgButtonChecked(IDC_USEPREFIX) )

                {


                if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) ||  IsDlgButtonChecked(IDC_USELONGNAME) )

                    {
                    EnableNext(TRUE);
                    }

                }


            return false;


            }

        } 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

壹場煙雨 2024-09-02 04:23:24

我会将您的 if/else 语句拆分为多个函数,并对发送到 EnableNext 的参数执行 &= 操作。您应该只调用一次 EnableNext。

因此,例如:

// in CWorkStation::ProcessEvent
bool enableNext = true; // start with true

enableNext &= Condition1(); // of course pick better names than Condition1
enableNext &= Condition2(); // this is just for an example

EnableNext(enableNext);

其中 Condition1() 可能是:

bool Condition1()
{
    return (IsDlgButtonChecked(IDC_USEDBNAME) 
         && !IsDlgButtonChecked(IDC_MAXDBNAME));
}

等等。

这里发生的情况是,enableNext 变量以 true 开头。然后,您执行的每个 &= 都意味着如果任何 ConditionX() 函数返回 false,enableNext 将最终返回 false。只有当所有条件都为真时,它才会在最后为真。

I would split your if/else statements into multiple functions, and do an &= on the parameter you send to EnableNext. You should be calling EnableNext only once.

So, for example:

// in CWorkStation::ProcessEvent
bool enableNext = true; // start with true

enableNext &= Condition1(); // of course pick better names than Condition1
enableNext &= Condition2(); // this is just for an example

EnableNext(enableNext);

Where Condition1() might be:

bool Condition1()
{
    return (IsDlgButtonChecked(IDC_USEDBNAME) 
         && !IsDlgButtonChecked(IDC_MAXDBNAME));
}

And so on.

What's happening here is that the enableNext variable starts with true. Then, each &= you do means that if any of the ConditionX() functions returns false, enableNext will end up false. It will only be true at the end if ALL of the conditions are true.

呢古 2024-09-02 04:23:24

这个问题可以通过听众的概念来解决。

您可以使每个 GUI 组件都有一个 isEnabled() 方法,该方法根据某些条件检查其条件。当调用任何更改任何组件状态的操作时,每个 GUI 组件都会调用 isEnabled()

这样,您就可以进行以下声明:

bool CheckBoxComponent::isValid() {
   return isNameFilled() && isEmailChecked();
}

bool OkButton::canSend() {
   return checkBoxName->isValid() && isEmailChecked();
}

然后,在创建 GUI 组件时,您可以使每个组件通过侦听器相互连接。

这样,您就拥有了每个组件所属的规则,并且没有大量的 if 语句。

That problem can be solved with the concept of listeners.

You can make each of your GUI components have a isEnabled() method, which checks its conditions based on some conditions. The isEnabled() is called on each GUI component when any action that changes the state of any component is called.

This way you can have the following declarations:

bool CheckBoxComponent::isValid() {
   return isNameFilled() && isEmailChecked();
}

bool OkButton::canSend() {
   return checkBoxName->isValid() && isEmailChecked();
}

Then, when creating your GUI components you make each of them connect to each other via listener.

This way you have the rules for each component where they belong and you don't have tons of if statements.

梦言归人 2024-09-02 04:23:24

尝试将规则制定为状态机可能会有所帮助,但是否可行取决于其性质。在这种方法中,每当用户填写对话框中的某些字段,或选中复选框或其他任何内容时,您都会相应地更新状态机的状态。如果你有,你可以使用 Boost.Statechart< /a> 来实现它。

It may help to try to formulate the rules as a state-machine, but if that is practical depends on their nature. In that approach, whenever the user fills out some field in the dialog, or checks a checkbox or whatever, you update the state of your sate-machine accordingly. If you have that, you can use Boost.Statechart to implement it.

独木成林 2024-09-02 04:23:24

在这种情况下,我倾向于通过(例如)默认启用该按钮来使其尽可能简单,如果设置(或未设置)任何其他条件,则禁用它;这限制了“if”条件与“else”中的不同情况。

In cases like that, I tend to make it as simple as possible by (for example) enabling the button by default, and if any other condition is set (or not), disable it; this limit the different cases in "if" conditions with "else".

感情废物 2024-09-02 04:23:24

将您的条件重述为正确的布尔语句,正确缩进所有条件并添加一些注释。恕我直言,您不应该在一次性方法中隐藏真正的检查。如果您想注释代码,请对其进行注释,但不要为此目的创建方法,它只会混淆事物,并且您的条件不会变得更简单:

EnableNext( 
        // condition 1 
        IsDlgButtonChecked(IDC_USEDBNAME) && !IsDlgButtonChecked(IDC_MAXDBNAME)
        // condition 2
    ||  IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount 
        && IsDlgButtonChecked(IDC_USEXMLNAME) && !PrefixName.IsEmpty()
        // condition 3
    ||  IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1
        // and so on
)

这样,您似乎会检查相同的条件两次USEXMLNAME && !PrefixName().IsEmpty()。现在也很明显,EnableNext 总是被调用。

Restate your condition as a proper boolean statement, properly indent all conditions and add some comments. IMHO, you shouldn't hide the real checks in single-use methods. If you want to comment code, comment it but don't create methods for that purpose, it only obfuscates things and your conditions don't get any simpler:

EnableNext( 
        // condition 1 
        IsDlgButtonChecked(IDC_USEDBNAME) && !IsDlgButtonChecked(IDC_MAXDBNAME)
        // condition 2
    ||  IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount 
        && IsDlgButtonChecked(IDC_USEXMLNAME) && !PrefixName.IsEmpty()
        // condition 3
    ||  IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1
        // and so on
)

This way it becomes immediately obvious that you seem to check the same condition twice USEXMLNAME && !PrefixName().IsEmpty(). It is also obvious now, that EnableNext is always called.

风为裳 2024-09-02 04:23:24

虽然它可能比您想要的解决方案“重”一些,但您可能想看看 Adob​​e 的 亚当和夏娃图书馆。 Eve 处理小部件布局,Adam 采用一组有关小部件逻辑的语句,并将它们组合到一个控制器中,该控制器根据该逻辑启用和禁用小部件,以及处理初始化并将结果放入适当的变量中(例如,当用户单击“确定”时)。

Though it might be a bit "heavier" of a solution than you'd like, you might want to look at Adobe's Adam and Eve libraries. Eve deals with widget layout, and Adam takes a set of statements about the logic of the widgets and puts them together into a controller that enables and disables widgets based on that logic, as well as handling initialization and putting results into the proper variables (e.g., when the user clicks "Ok").

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文