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

Sco*_*ain 12 c# controls deferred-loading winforms

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

  1. 创建一个新项目
  2. 添加选项卡式控件
  3. 在标签1上放一个按钮
  4. 在选项卡2上放置一个复选框
  5. 将此代码粘贴到其代码中

(使用控件的默认名称)

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

这样做的原因是选项卡2上的控件未处于"已创建"状态,因此直到控件被"创建"之后才会将其从未选中状态更改为已选中的复选框.

checkbox1.CreateControl()没有做任何事情,因为根据MSDN

如果控件的Visible属性为false,CreateControl不会创建控件句柄.无论控件的可见性如何,您都可以调用CreateHandle方法或访问Handle属性来创建控件的句柄,但在这种情况下,不会为控件的子项创建窗口句柄.

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

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

编辑 - 按照Jaxidian的建议,我创建了一个新课程

public class newcheckbox : CheckBox
{
    public new void CreateHandle()
    {
        base.CreateHandle();
    }
}
Run Code Online (Sandbox Code Playgroud)

我在调用updating = true与之前相同的结果后立即调用CreateHandle().

Tho*_*mas 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 } );
}
Run Code Online (Sandbox Code Playgroud)

现在,我们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 );
}
Run Code Online (Sandbox Code Playgroud)

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

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" );
    }
}
Run Code Online (Sandbox Code Playgroud)

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