65.9K
CodeProject 正在变化。 阅读更多。
Home

禁用和重新启用控件集合

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (6投票s)

2011年1月1日

CPOL

4分钟阅读

viewsIcon

42562

downloadIcon

370

禁用一个控件集合,并在重新启用该集合时,将所有子控件恢复到各自的启用状态

引言

这不是一篇复杂的文章或主题,只是尝试解决我遇到的一个问题,并认为其他人可能也会遇到。 在大家最喜欢的搜索引擎上快速搜索了一下,没有找到任何结果,所以我在这里输入我自己的解决方案。

问题非常简单 - 假设您需要在对话框或窗体中禁用它,因为您正在等待另一个线程上发生的事情,并且不希望用户在此期间与任何内容进行交互。

一个很好的例子是登录对话框,它要求提供 SQL Server 数据库的凭据,并在单击“确定”后尝试登录。 例如,如果指定的服务器名称错误(并且您使用的是默认的 30 秒连接超时),则用户必须等待 30 秒才能显示错误消息。

在建立连接时,您需要确保用户不会继续与您的屏幕进行交互,并且在此期间所有控件都有效地禁用。 简单的方法是将窗体的Enabled标志设置为false,但这会很糟糕。

问题是 - 如果您的应用程序是正确的多线程,并且连接尝试在后台发生,则您的对话框将在此期间保持解锁状态,用户应该能够移动它。 如果您禁用整个屏幕,您将无法再移动窗体,这会导致用户认为事情已冻结。

解决方案

这个问题的解决方案非常简单。 只需逐个禁用屏幕上的所有控件,然后就完成了。 对吧? 嗯,差不多。 您还必须考虑到屏幕上某些控件可能已被禁用,并且在禁用并随后重新启用屏幕后,您希望这些控件保持禁用状态。 回到登录示例,如果您使用 NT 身份验证,则禁用的控件可能是用户名和密码字段。

然后还有一些控件可能包含其他控件(例如分组框、面板等)的问题,并且它们的子控件也必须考虑在内。

为了实现这一点,需要一组动态方法。 一种方法,用于编目屏幕上的所有控件(存储它们的名称和启用状态)并禁用它们,另一种方法,使用第一种方法构建的目录将所有内容恢复到原来的状态。

UIHelper类具有这两个方法,它们非常简单。

GetControlStatesAndDisable()方法将您要禁用的Controls集合作为单个参数(例如Form.Controls)。 它编目并返回其中所有控件的名称和Enabled状态(并且递归调用自身,以便向下遍历任何子控件列表树,例如分组框或面板),然后禁用此列表中的所有控件。

您的工作完成后,将调用RestoreControlStates()方法,并将之前使用的相同 Controls 集合以及第一种方法创建的目录作为参数。

例如,假设您当前位于Form类中

SortedList<string, bool> controlStates = 
	UIHelper.GetControlStatesAndDisable(this.Controls); 
// do your lengthy operations here
UIHelper.RestoreControlStates(mForm1.Controls, controlStates); 

使用示例代码

附加的示例代码显示了此辅助类的工作方式。 它由两个窗体组成 - 一个包含几个测试控件和容器控件(带有它们自己的子控件),并且每个控件旁边都有一个按钮,允许您启用或禁用它。

第二个窗体用于使用辅助类和方法启用或禁用form1的所有控件。 请注意,无论您如何启用或禁用form1上的单个控件,通过form2上的按钮(因此通过辅助类)启用或禁用它们始终会将它们恢复到其原始的启用状态。

这两个方法如下(可以在本文附件中的UIHelper.cs中找到)

/// <summary>
/// Get a list of all the controls in the ControlCollection 
/// along with their Enabled state. Disables all those controls after generating
/// a list of their current Enabled state.
/// </summary>
/// <param name="controlCollection">The ControlCollection to scan for controls and 
/// Enabled flags.</param>
/// <returns>List of control names and Enabled states.</returns>
public static SortedList<string, bool> GetControlStatesAndDisable
			(Control.ControlCollection controlCollection)
{
    SortedList<string, bool> controlStates = new SortedList<string, bool>();
 
    // Loop through the controls under the current ControlCollection
    for (int ctr = 0; ctr < controlCollection.Count; ctr++)
    {
        // Store the current control name and its Enabled state
        controlStates.Add(controlCollection[ctr].Name, controlCollection[ctr].Enabled);
 
        // If the current control also has its own ControlCollection 
        // (such as for a group box, panel, etc), loop through
        // its own child controls and extract the same info.
        foreach (KeyValuePair<string, bool> pair in GetControlStatesAndDisable
					(controlCollection[ctr].Controls))
        {
            controlStates.Add(pair.Key, pair.Value);
        }
 
        // Set the current control's Enabled state to false
        controlCollection[ctr].Enabled = false;
    }
    return controlStates;
}
 
/// <summary>
/// Restore each control in the ControlCollection to the original 
/// Enabled state that existed before calling GetControlStatesAndDisable().
/// </summary>
/// <param name="controlCollection">The ControlCollection to scan for controls 
/// and restore their Enabled states.</param>
/// <param name="controlStates">The original Enabled states as retrieved 
/// from a call to GetControlStatesAndDisable().</param>
public static void RestoreControlStates
  (Control.ControlCollection controlCollection, SortedList<string, bool> controlStates)
{
    // Loop through the controls under the current ControlCollection
    for (int ctr = 0; ctr < controlCollection.Count; ctr++)
    {
        if (controlStates.ContainsKey(controlCollection[ctr].Name))
        {
            // Set the current control's Enabled state to what it was 
            // before the call to GetControlStatesAndDisable().
            controlCollection[ctr].Enabled = controlStates[controlCollection[ctr].Name];
        }
 
        // If the current control has its own ControlCollection 
        // (e.g. for a group box, panel, etc) perform the same job
        // on its own child controls.
        RestoreControlStates(controlCollection[ctr].Controls, controlStates);
    }
} 

关注点

但是,我确实注意到,禁用和重新启用容器控件实际上会保留其子控件的所有单独的 Enabled 状态,因此可能不需要遍历控件集合并存储(或恢复)它们各自的 Enabled 状态。 这在启用或禁用整个窗体时也有效,但这会冻结窗体并使其看起来像应用程序已冻结或崩溃。 我仍然将递归代码留在示例中,以防它在我想不到的另一种情况下派上用场。

我希望这个小技巧对其他人有所帮助,并且一如既往,欢迎提出意见和建议。

历史

  • 2010 年 1 月 1 日 - 首次发布
  • 2010 年 1 月 2 日 - 将UIHelper代码添加到文章中
© . All rights reserved.