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

Posted

技术标签:

【中文标题】处理 GUI 应用程序中的复杂规则(C++ 或 C#)【英文标题】:Handling Complex Rules in GUI applications (C++ or C#) 【发布时间】:2011-01-31 17:23:10 【问题描述】:

我正在处理一个对话框,其中必须满足几个规则才能启用“确定”按钮。

当前页面上的任何操作,例如输入数据或从下拉列表中选择一个项目(除其他外)都会调用一个名为 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;


            

         

【问题讨论】:

【参考方案1】:

我会将您的 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。只有当所有条件都为真时,它才会在最后为真。

【讨论】:

看起来不错。现在我只需要弄清楚 "&=" 到底是做什么的。 啊是的。 &= 是一个逻辑快捷操作符。 enableNext &= Condition1() 与 enableNext = enableNext && Condition() 相同 好吧,从技术上讲,它是 enableNext = enableNext & Condition1(),但对于布尔值来说,它实际上是一回事。请参阅编辑后发布。 小提示:可能需要注意没有 &&= 运算符,所以 Condition1() 总是用 &= 调用。如果函数没有副作用,效果很好,但如果它改变状态,你可能会得到一些意想不到的东西。【参考方案2】:

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

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

这样你可以有以下声明:

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


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

然后,在创建您的 GUI 组件时,您可以让它们中的每一个通过侦听器相互连接。

这样,您就可以为每个组件所属的每个组件制定规则,并且您没有大量的 if 语句。

【讨论】:

似乎确实适合Observer 模式。处理该事件,然后使用对象的notify 方法通知所有为它注册的Observers【参考方案3】:

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

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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

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 总是被调用。

【讨论】:

好点。我建议的一件事是仍然使用本地 enableNext 和单独的 &=,就像在我的示例中一样,但条件是内联的。通过这种方式在调试器中你可以知道是哪个条件导致它被禁用。 这有时对调试很有用。不过要小心 &=,它不是逻辑运算符,而是按位运算符。【参考方案6】:

虽然解决方案可能比您想要的要“重”一些,但您可能需要查看 Adob​​e 的 Adam and Eve 库。 Eve 处理小部件布局,Adam 获取一组关于小部件逻辑的语句,并将它们放在一个控制器中,该控制器基于该逻辑启用和禁用小部件,以及处理初始化并将结果放入适当的变量中(例如, 当用户点击“确定”时)。

【讨论】:

链接已过期。新的:stlab.adobe.com/group__asl__overview.html @Xeverous:谢谢。

以上是关于处理 GUI 应用程序中的复杂规则(C++ 或 C#)的主要内容,如果未能解决你的问题,请参考以下文章

耦合 - C++ Web 或 GUI 桌面应用程序

C++ GUI 中的拖放事件 (WM_DROPFILES)

C++并发编程(C++11到C++17)

C++ 并发编程(C++11 到 C++17 )

Visual Studio 中用于复杂表单的 C++ 可扩展 GUI 窗口

C语言中,宏替换的替换规则