【问题标题】:How to bind a custom control check box如何绑定自定义控件复选框
【发布时间】:2011-10-28 19:31:52
【问题描述】:

一段时间以来,我一直遇到自定义复选框控件的问题,并发布了此Question... 看来问题与事件无关。

自定义控件是一个带有复选框的组框。当复选框为 false 时,应禁用所有组控件。听起来很简单,但问题是当内部控件失去焦点时,会触发检查的更改事件。

经过更多的挖掘,我想我已经将其范围缩小到将选中的属性绑定到另一个对象。包含绑定时,它工作不正确,但删除绑定后,一切都按预期工作。

问题已找到,我不知道如何解决。

谁能帮忙?

这是我的测试应用程序中的一些代码--

形式:

表格代码:

public partial class Form1 : Form
{
    TestProperties m_TP;

    public Form1()
    {
        // Create instance of TestProperties
        m_TP = new TestProperties();

        InitializeComponent();

        BindControls();
    }

    private void BindControls()
    {
        // Bind the values to from the controls in the group to the properties class

        // When the line below is commented out the control behaves normally
        //this.checkedGroupBox1.DataBindings.Add("Checked", m_TP, "GroupChecked");
        this.numericUpDown1.DataBindings.Add("Value", m_TP, "SomeNumber");
        this.textBox1.DataBindings.Add("Text", m_TP, "SomeText");
        this.checkBox1.DataBindings.Add("Checked", m_TP, "BoxChecked");

        // Bind the values of the properties to the lables
        this.label4.DataBindings.Add("Text", m_TP, "GroupChecked");
        this.label5.DataBindings.Add("Text", m_TP, "SomeNumber");
        this.label6.DataBindings.Add("Text", m_TP, "SomeText");
        this.label8.DataBindings.Add("Text", m_TP, "BoxChecked");
    }
}

一个属性存储类:

class TestProperties
{
    public bool GroupChecked { get; set; }
    public decimal SomeNumber { get; set; }
    public string SomeText { get; set; }
    public bool BoxChecked { get; set; }

    public TestProperties()
    {
        GroupChecked = false;
        SomeNumber = 0;
        SomeText = string.Empty;
        BoxChecked = true;
    }
}

编辑: 这是我的自定义控件的来源:

/// <summary>
/// Custom control to create a checked group box.
/// Enables / disables all controls within the group depending upon the state of the check box
/// </summary>
public class CheckedGroupBox : System.Windows.Forms.GroupBox
{
    #region Private Member Variables
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Checkbox to enable / disable the controls within the group
    /// </summary>
    private CheckBox chkBox;

    /// <summary>
    /// Label for the group box which is set to autosize
    /// </summary>
    private Label lblDisplay;
    #endregion

    /// <summary>
    ///  Default constructor for the control
    /// </summary>
    public CheckedGroupBox()
    {
        // This call is required by the Windows Form Designer.
        InitializeComponent();

        // The text of these controls should always be empty.
        this.Text = "";
        this.chkBox.Text = "";
    }

    #region Events & Delegates

    /// <summary>
    /// Event to forward the change in checked flag
    /// </summary>
    public event EventHandler CheckedChanged;

    /// <summary>
    /// Event to forward the change in checked state of the checkbox
    /// </summary>
    public event EventHandler CheckStateChanged;

    private void chkBox_CheckedChanged(object sender, EventArgs e)
    {
        // Disable the controls within the group
        foreach (Control ctrl in this.Controls)
        {
            if (ctrl.Name != "chkBox" && ctrl.Name != "lblDisplay")
            {
                ctrl.Enabled = this.chkBox.Checked;
            }
        }

        // Now forward the Event from the checkbox
        if (this.CheckedChanged != null)
        {
            this.CheckedChanged(sender, e);
        }
    }

    private void chkBox_CheckStateChanged(object sender, EventArgs e)
    {
        // Forward the Event from the checkbox
        if (this.CheckStateChanged != null)
        {
            this.CheckStateChanged(sender, e);
        }
    }

    #endregion

    #region Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.chkBox = new System.Windows.Forms.CheckBox();
        this.lblDisplay = new System.Windows.Forms.Label();
        this.SuspendLayout();
        // 
        // chkBox
        // 
        this.chkBox.Location = new System.Drawing.Point(8, 0);
        this.chkBox.Name = "chkBox";
        this.chkBox.Size = new System.Drawing.Size(16, 16);
        this.chkBox.TabIndex = 0;
        this.chkBox.CheckStateChanged += new System.EventHandler(this.chkBox_CheckStateChanged);
        this.chkBox.CheckedChanged += new System.EventHandler(this.chkBox_CheckedChanged);
        // 
        // lblDisplay
        // 
        this.lblDisplay.AutoSize = true;
        this.lblDisplay.Location = new System.Drawing.Point(24, 0);
        this.lblDisplay.Name = "lblDisplay";
        this.lblDisplay.Size = new System.Drawing.Size(97, 13);
        this.lblDisplay.TabIndex = 1;
        this.lblDisplay.Text = "CheckedGroupBox";
        // 
        // CheckedGroupBox
        // 
        this.BackColor = System.Drawing.Color.AliceBlue;
        this.Controls.Add(this.chkBox);
        this.Controls.Add(this.lblDisplay);
        this.Size = new System.Drawing.Size(100, 100);
        this.ResumeLayout(false);
        this.PerformLayout();

    }
    #endregion

    #region Public properties

    [Bindable(true), Category("Appearance"), DefaultValue("Check Group Text")]
    public override string Text
    {
        get{ return this.lblDisplay.Text; }
        set{ this.lblDisplay.Text = value; }
    }

    [Bindable(true), Category("Appearance"), DefaultValue("Checked")]
    public System.Windows.Forms.CheckState CheckState
    {
        get{ return this.chkBox.CheckState; }
        set
        { 
            this.chkBox.CheckState = value; 

            foreach( Control ctrl in this.Controls )
            {
                if( ctrl.Name != "chkBox" && ctrl.Name != "lblDisplay" )
                {
                    ctrl.Enabled = this.chkBox.Checked;
                }
            }
        }
    }

    [Bindable(true), Category("Appearance"), DefaultValue("True")]
    public bool Checked
    {
        get{ return this.chkBox.Checked; }
        set
        { 
            this.chkBox.Checked = value; 

            foreach( Control ctrl in this.Controls )
            {
                if( ctrl.Name != "chkBox" && ctrl.Name != "lblDisplay" )
                {
                    ctrl.Enabled = this.chkBox.Checked;
                }
            }
        }
    }

    [Bindable(true), Category("Behavior"), DefaultValue("False")]
    public bool ThreeState
    {
        get{ return this.chkBox.ThreeState; }
        set{ this.chkBox.ThreeState = value; }
    }

    #endregion

}

【问题讨论】:

  • "...当内部控件失去焦点时,会触发选中的更改事件。"我认为您现在只需要专注于这一点,而不必担心数据绑定。
  • 当控件中的值发生变化时,您用来更新 m_TP 的代码是什么样的?
  • @Veldmuis - BindControls 方法显示了我如何更新属性。为了清楚起见,我刚刚添加了自定义控件的代码。

标签: c# winforms data-binding checkbox groupbox


【解决方案1】:

您需要按如下方式更改 BindControls 方法。这样做的原因是,默认情况下,控件正在更新以反映 m_TP 的属性,但是当它们的值发生更改时,更改不会反映在 m_TP 中(我猜 Leave 事件会导致控件重新绑定到它们的数据源)。通过添加 DataSourceUpdateMode 参数,将双向进行更改。

private void BindControls()
{
    // Bind the values to from the controls in the group to the properties class

    // When the line below is commented out the control behaves normally
    this.checkedGroupBox1.DataBindings.Add("Checked", m_TP, "GroupChecked", false, DataSourceUpdateMode.OnPropertyChanged);
    this.numericUpDown1.DataBindings.Add("Value", m_TP, "SomeNumber", false, DataSourceUpdateMode.OnPropertyChanged);
    this.textBox1.DataBindings.Add("Text", m_TP, "SomeText", false, DataSourceUpdateMode.OnPropertyChanged);
    this.checkBox1.DataBindings.Add("Checked", m_TP, "BoxChecked", false, DataSourceUpdateMode.OnPropertyChanged);

    // Bind the values of the properties to the lables
    this.label4.DataBindings.Add("Text", m_TP, "GroupChecked");
    this.label5.DataBindings.Add("Text", m_TP, "SomeNumber");
    this.label6.DataBindings.Add("Text", m_TP, "SomeText");
    this.label8.DataBindings.Add("Text", m_TP, "BoxChecked");
}

【讨论】:

  • 太棒了。就这样破解了。
【解决方案2】:

首先,为了让您的绑定正常工作,您需要在属性存储类上实现 INotifyPropertyChanged。请看例子here

在您的 Visual Studio 环境中创建一个指向类型“Object”的新数据源。选择您的 TestProperties 类作为源对象(如果该类未显示在列表中,请确保您已构建包含程序集并且您的 WinForm 项目具有对它的引用)。

现在,将 BindingSource 从您的工具箱拖到您的自定义控件上。将 BindingSource 的 DataSource 属性设置为您在上一步中创建的 TestProperties 数据源。

现在,选择自定义控件组框并转到 (DataBindings) 部分。单击高级字段中的“...”。将自定义网格控件的“启用”属性绑定到 BindingSource 的“BoxChecked”属性。这一切都可以在设计器中完成(或者如果您愿意,也可以通过后面的代码)

通过 BindingSource 将自定义控件上的其他 WinForms 元素绑定到 TestProperties 类的相应元素。

现在,在自定义控件上公开一个属性,以便您可以在“父”表单中绑定到它。确保将 BindingSource 的“DataSource”设置为新的引用属性。

    private TestProperties _properties;
    public TestProperties Properties
    {
        get { return _properties; }
        set
        {
            _properties = value;
            this.bindingSource1.DataSource = _properties;
        }
    }

    public UserControl1()
    {
        InitializeComponent();
    }

在托管控件的父窗体中,您有几个选项。

1) 您可以直接绑定到 UserControl 公开的 TestProperties 类,就像您在上面的示例中所做的那样

2) 您可以重复用于在父控件上设置用户控件绑定的 BindingSource 练习,并将新 BindingSource(在父窗体上)的 DataSource 属性设置为等于 UserControl 的公开 TestProperties 属性。

【讨论】:

  • 我想我已经正确地关注了你,但我仍然遇到同样的问题。 TestProperties 是否应该在自定义控件中公开?我不能这样做,因为控件需要根据父窗体的要求处理各种不同的属性类。
  • 您可以公开由所有“Property”类或您的自定义 PropertyClasses 继承的“PropertyBase”类实现的接口,而不是公开 TestProperties 类型的属性
【解决方案3】:

像这样的东西是我能得到的最接近的东西:

我显然没有 groupbox 的代码(包括复选框),所以我尽我所能。

这是通过将其添加到顶部复选框来实现的:

private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
   groupBox1.Enabled = ((CheckBox)(sender)).Checked;
}

我也重新启用了这一行:

this.checkBox1.DataBindings.Add("Checked", m_TP, "GroupChecked");

【讨论】:

  • 我已经添加了自定义控件的源代码。我认为问题与绑定而不是事件有关。移除绑定后,控件按要求工作。
猜你喜欢
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 2010-12-20
  • 2015-06-03
相关资源
最近更新 更多