12

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

  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");
    }
}

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

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

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

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

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

除了让程序在第一次加载时快速闪烁我所有具有数据绑定复选框的选项卡之外的任何建议?

编辑——根据 Jaxidian 的建议,我创建了一个新课程

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

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

4

1 回答 1

12

我想我有一个解决办法。问题不在于您不能创建句柄。您可以通过简单地访问 Control 上的 Handle get 访问器来做到这一点。问题是 WinForms 不创建控件,因为它不可见。事实证明,在幕后, aSystem.Windows.Forms.Control有两个重载CreateControl。第一个是公共的,不带参数,它调用第二个,它只internal带一个boolean参数:ignoreVisible,顾名思义,它允许调用代码创建控件,即使它不可见。不带参数的CreateControl方法将 false 传递给此内部方法,这意味着如果控件不可见,则不会创建它。所以,诀窍是使用反射来调用内部方法。首先,我创建了两种创建控件的方法:

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它将是真实的。但是,Debug.WriteLine将显示它在“输出”窗口中触发。

于 2010-04-03T03:36:40.200 回答