强制绑定到DataSource的复选框在尚未查看时进行更新

时间:2010-03-26 14:01:10

标签: c# winforms controls deferred-loading

这是一个测试框架,用于展示我在做什么:

  1. 创建新项目
  2. 添加标签式控件
  3. 标签1上的
  4. 按了一个按钮
  5. 选项卡2上的
  6. 放置一个复选框
  7. 将此代码粘贴为其代码
  8. (使用控件的默认名称)

    public partial class Form1 : Form
    {
        private List<bool> boolList = new List<bool>();
        BindingSource bs = new BindingSource();
        public Form1()
        {
            InitializeComponent();
            boolList.Add(false);
            bs.DataSource = boolList;
            checkBox1.DataBindings.Add("Checked", bs, "");
            this.button1.Click += new System.EventHandler(this.button1_Click);
            this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
    
        }
        bool updating = false;
        private void button1_Click(object sender, EventArgs e)
        {
            updating = true;
            boolList[0] = true;
            bs.ResetBindings(false);
            Application.DoEvents();
            updating = false;
        }
    
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (!updating)
                MessageBox.Show("CheckChanged fired outside of updating");
        }
    }
    

    问题是如果您运行程序并查看选项卡2然后按选项卡1上的按钮程序按预期工作,但是如果您按下选项卡1上的按钮然后查看选项卡2该复选框的事件将不会火,直到看到标签2。

    原因是选项卡2上的控件未处于“已创建”状态,因此在控件被“创建”之后,它的绑定才会将复选框从未选中状态更改为已选中状态。

    checkbox1.CreateControl()不执行任何操作,因为根据MSDN

      

    CreateControl不会创建   如果控件的控制句柄   可见财产是假的。您可以   要么调用CreateHandle方法,要么   访问Handle属性来创建   控件的手柄无论如何   控制的可见性,但在此   case,没有创建窗口句柄   对于控制的孩子们。

    我尝试获取Handle的值(CheckBox没有 public CreateHandle()),但结果仍然相同。

    除了让程序在第一次加载时快速刷新所有带有数据绑定复选框的选项卡以外的任何建议?

    编辑 - 根据Jaxidian的建议,我创建了一个新类

    public class newcheckbox : CheckBox
    {
        public new void CreateHandle()
        {
            base.CreateHandle();
        }
    }
    

    我在updating = true之后立即调用CreateHandle()。

1 个答案:

答案 0 :(得分:12)

我想我有一个解决方案。问题不在于您无法创建句柄。您只需访问Control上的Handle get访问器即可。问题是WinForms不会创建控件,因为它不可见。事实证明,在幕后,System.Windows.Forms.Control有两个CreateControl重载。第一个是public,它不带参数,它调用第二个internal,它接受​​一个boolean参数:ignoreVisible,顾名思义允许调用代码创建控件,即使它不可见。没有参数的CreateControl方法将false传递给此内部方法,这意味着如果控件不可见,则不会创建它。因此,诀窍是使用Reflection来调用内部方法。首先,我创建了两种创建控件的方法:

private static void CreateControls( Control control )
{
    CreateControl( control );
    foreach ( Control subcontrol in control.Controls )
    {
        CreateControl( subcontrol );
    }
}
private static void CreateControl( Control control )
{
    var method = control.GetType().GetMethod( "CreateControl", BindingFlags.Instance | BindingFlags.NonPublic );
    var parameters = method.GetParameters();
    Debug.Assert( parameters.Length == 1, "Looking only for the method with a single parameter" );
    Debug.Assert( parameters[0].ParameterType == typeof ( bool ), "Single parameter is not of type boolean" );

    method.Invoke( control, new object[] { true } );
}

现在,我们为第二个标签添加了对CreateControls的调用:

public Form1()
{
    InitializeComponent();
    boolList.Add( false );
    bs.DataSource = boolList;
    checkBox1.DataBindings.Add( "Checked", bs, "" );
    this.button1.Click += this.button1_Click;
    this.checkBox1.CheckedChanged += this.checkBox1_CheckedChanged;

    CreateControls( this.tabPage2 );
}

另外,我添加了一些调试消息,以便我可以看到事件是否被触发:

private void button1_Click( object sender, EventArgs e )
{
    Debug.WriteLine( "button1_Click" );
    updating = true;
    boolList[0] = true;
    bs.ResetBindings( false );
    Application.DoEvents();
    updating = false;
}

private void checkBox1_CheckedChanged( object sender, EventArgs e )
{
    Debug.WriteLine( "checkBox1_CheckedChanged" );
    if ( !updating )
    {
        Debug.WriteLine( "!updating" );
        MessageBox.Show( "CheckChanged fired outside of updating" );
    }
}

现在,无论您是否导航到第二个标签,点击第一个标签上的按钮都会触发checkbox1_Changed事件过程。根据您提供的设计,如果单击按钮,它将不会显示MessageBox,因为updating将为true。但是,Debug.WriteLine将显示它在“输出”窗口中触发。