zoukankan      html  css  js  c++  java
  • 对话框控制管理

    介绍 对话框控制管理是一个永恒的问题。你想要有一定的控制启用或禁用,可见或不可见,基于其他控件的值。与窗户,没有ON_UPDATE_COMMAND_UI这样的机制。大多数书显示如何出错的例子。因为他们中的大多数说明相当简单的例子,简单的方法很容易编写和易于维护。不幸的是,在现实世界中,对话框通常是相当复杂的,和支持条件是重要的。介绍性教科书中所描绘的简单方法大大低于适当的在这种情况下。十年的Windows编程允许我开发一个方法来处理这个。 保罗DiLascia技术发表在他的c++ Q&一列显示技术导致ON_UPDATE_COMMAND_UI消息处理在一个对话框。不幸的是,我已经试过几次,不能让它工作。它出现在MSDN光盘;通过搜索找到它WM_KICKIDLE看着c++ Q&一个列。然而,这项技术的唯一区别我要描述和他描述的技术是如何分区的代码。在他的解决方案中,每个控制ON_UPDATE_COMMAND_UI处理程序,使控制进入处理程序的代码。我的解决方案,是在一个函数的所有代码。但是这两种技术的重要特征是控制操作代码存在于程序中的一个地方。 在介绍性教科书通常给出的例子(有一个例外我确信:看到Win32编程,布伦特校长和约瑟夫·m .新人)显示对话框启用控制按钮按下,列表框选择,等等,在响应事件的代码。这是一个错误的地方。 为什么这个错误的地方?一个很好的例子是一段代码我继承了(我是一个自由顾问)。它有二十三个单独的地方,它使一个控制。像往常一样,这导致了每个变更的情况(如添加一个新的控制)创建了一个新的条件使控制问题,而不是每个网站持续更新。事实上,我发现了五个不同的算法来决定启用。和他们的顺序执行结果非常奇怪。此外,至少其他两个地方政府改变使情况改变不费心去做计算。保持这样的代码是一个噩梦。得到它是不可能的。 当然,你可能会想“显然正确的做法是让一个子程序启用和调用它正确的地方”。这假定你知道正确的地方。虽然在这方面我的解决方案并不完美,这是很多比正常分布式更新机制。 我所做的就是把启用状态为一组约束方程。每个控件都有最多的一个实例“EnableWindow”和“显示窗口”的一个实例。时期。 我的每一个对话框,有什么有趣的事情发生在控制有一个方法叫做“updateControls”。所有控件的状态改变计算方法。这个方案的缺陷是,我必须打电话给updateControls状态改变,这常常意味着我有控制处理程序(例如,对于复选框),什么都不做但updateControls打电话。另一种方法是简单地从OnIdle处理程序调用该方法,我选择不做。 此外,当一个控制的状态依赖于一个或多个其他控件,控件状态的影响是直接访问时计算完成。没有布尔变量设置神奇地从其他函数。每个变量都可以影响国家在需要计算,而不是瞬间。做一些聪明的不喜欢预先执行基于一些控制状态组合并存储在一个布尔供updateControls使用。总是从第一原理计算,每一次(如果需要访问数据库,您可以缓存数据库状态,但在这种情况下,条件应该计算直接从记录的字段(s),而不是从预先计算的信息被存储)。 一个潜在的反对这种方法,分布式方法只计算的状态控制正在处理状态变化的影响,而我的方法需要再计算所有控件的状态。这是“低效”。这个论点是根本毫无意义。它是基于早期训练的程序员,强调执行指令的最少数量可能达到的目标是度量一个好计划。这是个错误的培训。如果你相信这一点,重新考虑这个问题。效率问题只有当它很重要。其余的时间,简单起见,正确性,和可维护性占主导地位。高效的代码几乎总是难写,更难调试,比简单的代码难以维护。 除非你是做计算密集型算法,效率是第三、四阶的效果。记住这些规则是发达的时代,机器非常缓慢。30 MIPS ?那是一个中档奔腾。第一台机器我在大约0.003 MIPS程序执行。这是一个在36年10000倍的性能改进。那时,每个指令数。今天,唯一的标准是响应能力,开发一个程序的成本(最大的程序,机器可以持有约1800汇编代码指令;比较,在一个中型的Windows应用程序可以运行60000到120000行C代码)。 为什么不效率问题当你更新控制?看看人类的因素。一只老鼠从耳朵举行大约2英尺。声音在传播大约1100英尺/秒。这意味着它大约需要2 ms鼠标点击的声音进入耳朵。从指尖到大脑的神经路径的一个成年人大约3英尺。神经冲动的传播是大约300英尺/秒,意义鼠标单击大约需要10 ms的感觉直冲大脑。知觉延迟在大脑中可以添加在50到250 ms。 现在,有多少奔腾指令可以执行2毫秒,10 ms,或100毫秒?在2女士,500 mhz机器1000000个时钟周期,所以你可以执行的指令。即使在now-clunky 120 mhz奔腾没有明显的延迟在处理控制。 因此,核心思想是使生活尽可能简单,编码和维护。下面是一个示例处理更新的OK按钮根据空虚c_Text控制。 这是正式的规范: 好是禁用如果c_Text是空的 选择好的禁用如果c_Option c_Count是0 只有在启用c_Count c_Option检查隐藏,复制Code

    void CMyDialog::OnChangeText()
    {
        updateControls();
    }
    
    void CMyDialog::updateControls()
    {
        BOOL enable;
    
        // c_OK =========================================
        CString s;
        c_Text.GetWindowText(s);
        s.TrimLeft(); // ignore leading spaces
        enable = s.GetLength() != 0 && (c_Option.GetCheck == BST_UNCHECKED 
                                                          || c_Count != 0);
        c_OK.EnableWindow(enable);
    
        // c_Count =======================================
        enable = c_Option.GetCheck();
        c_Count.EnableWindow(enable);
        x_Count.EnableWindow(enable);
        //================================================
    }

    有时,复杂的布尔表达式获得难以编写和调试。另一个替代方法是重写使测试为好:隐藏,复制Code

    enable = s.GetLength() != 0;
    if(c_Option.GetCheck == BST_CHECKED && c_Count == 0)
        enable = FALSE;

    这项技术是建立“启用”的价值,然后所有后续修改变量使用,=”或简单的设置值FALSE。的单调性变化必须始终支持走向错误的。值设置后,在没有任何一点是计算完成,可以显式地设置正确。这门学科可以确保你不会不小心把它真正在其他条件(正确地)错误。您可以显式地设置它为FALSE,或者使用,=,但从来没有做任何事,它可能会增加假为真。类似的规则是用于计算可见性。我倾向于使用一个布尔变量的可见性,运用同样的技术的单调递减计算,然后执行一个语句formHide,复制Code

    c_Control.ShowWindow(visible ? SW_SHOW : SW_HIDE);

    通常你想启用/禁用一群控制基于条件。我倾向于调用一个函数的一组首先计算所需的状态,然后调用一个方法如所示。实现控件的代码是互补的,基于相同的条件: 如果条件为真,c_Something启用。 如果启用了c_Something, c_OtherThing是禁用的,反之亦然。 如果启用了c_Something, c_ThisThing是可见的,否则它是无形的。 如果启用了c_OtherThing, c_ThatThing是可见的,否则它是无形的。隐藏,复制Code

    void CMyDialog::changeGroup(BOOL mode)
    {
        c_Something.EnableWindow(mode);
        c_OtherThing.EnableWindow(!mode);
        c_ThisThing.ShowWindow(mode ? SW_SHOW : SW_HIDE);
        c_ThatThing.ShowWindow(mode ? SW_HIDE : SW_SHOW);
    }

    因此,尽管并不是所有的代码都是不内联在updateControls changeGroup方法不是从updateControls以外的任何地方。 不,这不是完美的。完美的是,ClassWizard ON_UPDATE_COMMAND_UI处理程序提供对每一个控制和微软工作需要看到他们。持续的缺陷在我的方法是,你必须记得叫updateControls当状态改变。好消息是,如果你叫它在每个控制处理程序,并没有什么变化,不需要做任何事情。注意,您通常会叫updateControls OnInitDialog年底。 是的,有一个额外的问题。如果正在计算是一个控制的标题,你会发现这种技术产生不良的闪烁。这是容易修复。考虑以下,从而改变一个按钮标题从“停止”到“运行”:隐藏,复制Code

    BOOL isrunning;  // protected class member; set FALSE in constructor
    
    void CMyDialog::OnStopRun( )
    {
        isrunning = !isrunning;
        updateControls( );
    }
    
    void CMyDialog::UpdateControls()
    {
        CString caption;
        caption.LoadString(isrunning ? IDS_STOP : IDS_RUN);
        CString oldcaption;
        c_StopRun.GetWindowText(oldcaption);
        if(oldcaption != caption)
            c_StopRun.SetWindowText(caption);
    }

    应该是显而易见的,如果你有不止一个按钮你这样做,有一个高回报在定义一个新的CDCButton类(动态标题按钮)覆盖SetWindowText:隐藏,复制Code

    void CDCButton::SetWindowText(CString caption)
    {
        CString old;
        CButton::GetWindowText(old);
        if(old == caption)
            return;
        CButton::SetWindowText(caption);
    }

    当控件没有:断言失败 在对话框启动有问题。例如,如果您有一个编辑的OnChange或OnUpdate处理程序控制,和您希望操作另一个控制。即使你总是不相信我的方法做在一个单独的例程中,你仍然会遇到严重的问题。考虑在文本变为非空时启用按钮的情况。测试看起来就像复制Code

    CString s;
    c_Edit.GetWindowText(s);
    s.TrimLeft();
    c_DoSomething.EnableWindow(s.GetLength() > 0);

    您会发现自己正处于ASSERT语句的中间,如果您回溯的话,该语句来自EnableWindow调用。你看c_DoSomething按钮的m_hWnd成员,你会发现它是0。如果您正在使用GetDlgItem,情况会更糟,因为您需要编写类似于hide的内容。复制Code

    CButton * doSomething = (CButton *)GetDlgItem(IDC_DO_SOMETHING);

    而您尝试使用它将采取一个访问错误,因为指针是空的。(不要使用GetDlgItem;参见我的文章《正确的做法》)。 这是因为MFC和底层Windows机制不匹配。发生的情况是,DDX机制已经创建了编辑控件,这意味着它可以开始生成MESSAGE_MAP将开始调度的消息,但还没有将IDC_DO_SOMETHING控件分配给相应的CButton。因此,即使控件已经存在,在尝试使用它时,ASSERT也会失败。 GetDlgItem失败的最常见原因是标签顺序序列是创建编辑控件,创建它的旋转控制伙计,然后生成一个消息对话框,它拦截,但创建的对话框创建代码尚未IDC_DO_SOMETHING按钮。GetDlgItem会返回一个空指针。 我解决这个问题的方法是在对话框类中创建一个成员变量Hide。复制Code

    BOOL initialized;

    在类构造函数中,我简单地设置复制Code

    initialized = FALSE;

    在OnInitDialog的最后,我做了以下事情:隐藏复制Code

    initialized = TRUE;
    updateControls();

    我修改了updateControls(),使其具有statementHide作为第一个可执行代码。复制Code

    if(!initialized)
        return;

    我偶尔需要在其他地方执行这个测试。通常你会从经验中发现这些。 有时候你可以用隐式测试来代替这个测试。复制Code

    if(!::IsWindow(c_Button.m_hWnd))
        return;

    例如,它测试控制变量c_Button,以查看窗口是否已经创建。由于我通常必须引入初始化的变量,所以我发现这个变量在我的代码中不太常见。 总结 在对话框中控制管理的各种技术中,十年的Windows编程使我确信,这种方法的核心哲学是唯一有意义的哲学。无论它是通过显式调用(如我所做的)完成的,还是从OnIdle完成的,还是分布到ON_UPDATE_COMMAND_UI处理程序中,每个控件必须不超过一个ShowWindow和一个EnableWindow。不这样做简直是疯了。它生成的代码很难编写,调试很难到不可能,而且完全不可能维护。这是不可接受的。 在这些文章中表达的观点是作者的观点,不代表,也不支持,微软。 发送邮件到纽科梅@比目德。com关于这个网站的问题或评论。版权所有:龙名公司 本文转载于:http://www.diyabc.com/frontweb/news6835.html

  • 相关阅读:
    从零开始入门 K8s | 有状态应用编排
    OAM 深入解读:OAM 为云原生应用带来哪些价值?
    你不得不了解 Helm 3 中的 5 个关键新特性
    CNCF 公布 2020 年 TOC 选举结果 | 云原生生态周报 Vol. 36
    调度系统设计精要
    Spring的IOC容器第一辑
    JavaScript工作体系中不可或缺的函数
    教你五步制作精美的HTML时钟
    web前端vertical-align的作用及对象详解
    JavaScript中常见的10个BUG及其修复方法
  • 原文地址:https://www.cnblogs.com/Dincat/p/13467448.html
Copyright © 2011-2022 走看看