将责任委托给WinForm控件 - 控件应该意识到对方的行为?

我在窗体上有一些控件:

  • 一个复选框,负责根据选中的状态启用/禁用页面上的所有其他控件。
  • 一些单选按钮,负责根据选中的状态启用/禁用页面上的特定控件。
  • 上述控件操纵的其他控件。

出现一些情况:

  • 当表单初始化时,我加载复选框的状态。然后,它启用或禁用窗体上的其他控件。
  • 当表单继续初始化时,我加载单选按钮的状态。如果单选按钮被选中但被禁用,这有机会撤销先前的要求。因此,我会检查以确保首先启用单选按钮。
  • 表单加载后,用户可以选中或取消选中单选按钮。这是一个微不足道的例子,我只是运行符合最后要求的代码。另一种情况是,用户可以选中/取消选中复选框。当复选框变为启用时,它希望重新启用页面上的所有控件,因为它禁用了它们。不过这样做会破坏单选按钮的要求。

这种情况是非常微不足道的蛮力处理。我创建了几个方法来突出显示:

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

    foreach (Control control in grpBxSNMPv3.Controls)
    {
        if (control != sender)
            control.Enabled = ((CheckBox)sender).Checked;
    }
}

private void rdBtnAuthNoPriv_CheckedChanged(object sender, EventArgs e)
{
    RadioButton authNoPrivRadioButton = ((RadioButton)sender);

    if (authNoPrivRadioButton.Enabled)
    {
        bool isChecked = authNoPrivRadioButton.Checked;

        SetControlState(cmbBxAuthProtocol, isChecked);
        SetControlState(mskdTxtBxAuthPassword, isChecked);
        SetControlState(mskdTxtBxAuthPasswordConfirm, isChecked);

        SetControlState(cmbBxPrivacyProtocol, !isChecked);
        SetControlState(mskdTxtBxPrivacyPassword, !isChecked);
        SetControlState(mskdTxtBxPrivacyPasswordConfirm, !isChecked);
    }
}
//More methods for other checkedChange and also for when rdBtn's enable.

布局的粗糙想法:

enter image description here

尽管如此,我的问题是'简单':

  • 方法应该没有其他方法存在的假设。然而,如果我保持rdBtn的chkBx的存在逻辑,那么我将拥有相互对抗的代码。

我可以这样写我的代码:

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

    txtBxEngineID = ((CheckBox)sender).Checked;
    rdBtnAuthNoPriv = ((CheckBox)sender).Checked;
    rdBtnAuthPriv = ((CheckBox)sender).Checked;
    rdBtnNoAuthNoPriv = ((CheckBox)sender).Checked;

    //Pass work for enabling Auth and Priv fields to rdBtn events.
}

此解决方案更高效,并保证我不会看到任何闪烁。然而,这也意味着为了成功完成页面上的所有控件,我的chkBx现在必须依赖rdBtn的逻辑。这是不错的编程习惯吗?

3
额外 编辑
意见: 1
嘿,对不起。我同意 - 我最初的帖子没有足够的意义。今天我会编辑它,当我有一点时间让它更容易理解为传统目的。不过,我很困惑你的第二点。如果涉及单个课程的情况下不会使用事件,那么呢?你有阅读我支持吗?代码位于加载到表单上的用户控件上。
额外 作者 Sean Anderson,
这个问题没有多大意义,这些已经存在于表单中的代码片段。或者UserControl,不知道。没有代码在控件类中。事件就是这样,让某种其他代码知道发生了一些有趣的事情。这个代码非常属于这种形式,它是唯一知道它所托管的控件集合的任何类。
额外 作者 Hans Passant,

2 答案

我认为这是明智的代码,以保持形式,但我会提出一些建议;

1)铸造的加工成本很小,所以你应该避免在一个循环内铸造。事实上,作为一般概念,当结果保证保持不变时,应该避免在循环内执行任何重复操作。所以你可以像这样改进你的第一种方法;

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

   //cast the sender once only
    CheckBox cb = sender as CheckBox;
    if (null == cb) return;

    foreach (Control control in grpBxSNMPv3.Controls)
    {
        if (control != sender)
            control.Enabled = cb.Checked;
    }
}

2)我建议将启用/禁用逻辑移出一个单独的方法,然后从您的控件事件处理程序调用它。这将使您能够重新使用相同的逻辑,如果您决定使用其他控件。紧密耦合行为来控制事件,我发现导致重复的代码。像这样;

private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
{
    snmpSettingsErrorProvider.Clear();

   //cast the sender once only
    CheckBox cb = sender as CheckBox;
    if (null == cb) return;

    SetEnabled(grpBxSNMPv3, cb.Checked, new[] { cb });
}

private void SetEnabled(Control parent, bool isEnabled, Control[] exludeControls)
{
    if (null == parent) return;

    foreach (Control control in parent.Controls)
    {
        if (!excludeControls.Contains(control))
            control.Enabled = isEnabled;
    }
}

你现在有了一个可重用的方法来启用/禁用另一个包含的所有控件。

3)关于你最后的问题,是的,我认为这种方法很好。减少耦合总是一件好事。想想如何设计你的方法更加可重用,我想你会想出一个干净的解决方案。

1
额外
+1的建议。事实上,在更新我的主题之前,我确实遵循了所有这些建议 - 所以伟大的思想家都会这么想。我继续为未来的人发布一个干净的解决方案。让我知道你是否看到任何可怜的东西。 :)
额外 作者 Sean Anderson,

这就是我最终的目标。除了我正在初始化的两个列表之外,我还好。他们可能应该在自己的控制下,但我不能让自己做到这一点。

public partial class DeviceSnmpSettings : UserControl, INotifyPropertyChanged
{
    private readonly List AuthenticationControls = new List(6);
    private readonly List PrivacyControls = new List(6);
    public event PropertyChangedEventHandler PropertyChanged;

    public DeviceSnmpSettings()
    {
        InitializeComponent();
        InitializeAuthControls();
        InitializePrivacyControls();
    }

    public DeviceSnmpSettings(Point location)
        : this()
    {
        Location = location;
    }

    //TODO: Move out into sub-user control?
    private void InitializeAuthControls()
    {
        AuthenticationControls.Add(lblAuthPassword);
        AuthenticationControls.Add(mskdTxtBxAuthPassword);
        AuthenticationControls.Add(lblAuthProtocol);
        AuthenticationControls.Add(cmbBxAuthProtocol);
        AuthenticationControls.Add(lblAuthPasswordConfirm);
        AuthenticationControls.Add(mskdTxtBxAuthPasswordConfirm);
    }
    //TODO: Move out into sub-user control?
    private void InitializePrivacyControls()
    {
        PrivacyControls.Add(lblPrivacyPassword);
        PrivacyControls.Add(mskdTxtBxPrivacyPassword);
        PrivacyControls.Add(lblPrivacyProtocol);
        PrivacyControls.Add(cmbBxPrivacyProtocol);
        PrivacyControls.Add(lblPrivacyPasswordConfirm);
        PrivacyControls.Add(mskdTxtBxPrivacyPasswordConfirm);
    }

    private bool SNMPv3Enabled
    {
        get { return chkBxSNMPv3.Checked; }
        set { chkBxSNMPv3.Checked = value; }
    }

    private SNMPV3Mode SecurityMode
    {
        get
        {
            SNMPV3Mode mode = SNMPV3Mode.NoAuthNoPriv;

            if (rdBtnAuthNoPriv.Checked)
                mode = SNMPV3Mode.AuthNoPriv;
            else if(rdBtnAuthPriv.Checked)
                mode = SNMPV3Mode.AuthPriv;

            return mode;
        }
        set
        {
            switch (value)
            {
                case SNMPV3Mode.NoAuthNoPriv:
                    rdBtnNoAuthNoPriv.Checked = true;
                    break;
                case SNMPV3Mode.AuthNoPriv:
                    rdBtnAuthNoPriv.Checked = true;
                    break;
                default:
                    rdBtnAuthPriv.Checked = true;
                    break;
            }

            OnSecurityModeChanged();
        }
    }

    protected virtual void OnSecurityModeChanged()
    {
        AuthenticationControls.ForEach(control => SetControlEnabledState(control, AuthenticationEnabled));
        PrivacyControls.ForEach(control => SetControlEnabledState(control, PrivacyEnabled));
        NotifyPropertyChanged("SecurityMode");
    }

    private void NotifyPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    private bool AuthenticationEnabled
    {
        get
        {
            return SNMPv3Enabled && (SecurityMode == SNMPV3Mode.AuthPriv || SecurityMode == SNMPV3Mode.AuthNoPriv);
        }
    }

    private bool PrivacyEnabled
    {
        get { return SNMPv3Enabled && SecurityMode == SNMPV3Mode.AuthPriv; }
    }

    private void ChkBxSnmPv3OnCheckedChanged(object sender, EventArgs eventArgs)
    {
        SetControlEnabledStates();
    }

    private void SetControlEnabledStates()
    {
        snmpSettingsErrorProvider.Clear();

        foreach (Control control in grpBxSNMPv3.Controls)
        {
            //Check each of the lists for the control to prevent flickering.
            if (control != chkBxSNMPv3 && !AuthenticationControls.Contains(control) && !PrivacyControls.Contains(control))
                control.Enabled = SNMPv3Enabled;
        }

        //Need to validate that our radio button's checked state is reflected properly.
        AuthenticationControls.ForEach(control => SetControlEnabledState(control, AuthenticationEnabled));
        PrivacyControls.ForEach(control => SetControlEnabledState(control, PrivacyEnabled));
    }

    public void LoadFields(NetworkDiscovery networkDiscovery)
    {
        SNMPv3Enabled = networkDiscovery.Snmpv3Enabled;
        SecurityMode = networkDiscovery.SecurityMode;
        txtBxSNMPv3Username.Text = networkDiscovery.Username;
        mskdTxtBxAuthPassword.Text = networkDiscovery.AuthPassword;
        mskdTxtBxAuthPasswordConfirm.Text = networkDiscovery.AuthPassword;
        cmbBxAuthProtocol.SelectedItem = networkDiscovery.AuthProtocol.ToString();
        mskdTxtBxPrivacyPassword.Text = networkDiscovery.PrivacyPassword;
        mskdTxtBxPrivacyPasswordConfirm.Text = networkDiscovery.PrivacyPassword;
        cmbBxPrivacyProtocol.SelectedItem = networkDiscovery.PrivacyProtocol.ToString();

        SetControlEnabledStates();
    }

    private void SetControlEnabledState(Control control, bool enabled)
    {
        control.Enabled = enabled;
                    //Clear errors set on errorProvider when control is disabled.
        if (!control.Enabled)
            snmpSettingsErrorProvider.SetError(control, string.Empty);
    }

    private void rdBtnNoAuthNoPriv_CheckedChanged(object sender, EventArgs e)
    {
        if (((RadioButton)sender).Checked)
            SecurityMode = SNMPV3Mode.NoAuthNoPriv;
    }

    private void rdBtnAuthNoPriv_CheckedChanged(object sender, EventArgs e)
    {
        if (((RadioButton)sender).Checked)
            SecurityMode = SNMPV3Mode.AuthNoPriv;
    }

    private void rdBtnAuthPriv_CheckedChanged(object sender, EventArgs e)
    {
        if (((RadioButton)sender).Checked)
            SecurityMode = SNMPV3Mode.AuthPriv;
    }
}
0
额外