C# 在尚未查看数据源时强制更新绑定到该数据源的复选框

C# 在尚未查看数据源时强制更新绑定到该数据源的复选框,c#,winforms,controls,deferred-loading,C#,Winforms,Controls,Deferred Loading,下面是一个测试框架,展示了我正在做的事情: 创建新项目 添加选项卡式控件 在选项卡1上,放置一个按钮 在选项卡2上,选中一个复选框 粘贴此代码作为其代码 (使用控件的默认名称) 我在update=true之后立即调用CreateHandle()。我想我有一个解决方案。问题不是不能创建句柄。您可以通过访问控件上的句柄get访问器来实现这一点。问题是WinForms不创建控件,因为它不可见。事实证明,在幕后,System.Windows.Forms.Control有两个重载用于CreateContr

下面是一个测试框架,展示了我正在做的事情:

  • 创建新项目
  • 添加选项卡式控件
  • 在选项卡1上,放置一个按钮
  • 在选项卡2上,选中一个复选框
  • 粘贴此代码作为其代码
  • (使用控件的默认名称)


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

    我想我有一个解决方案。问题不是不能创建句柄。您可以通过访问控件上的句柄get访问器来实现这一点。问题是WinForms不创建控件,因为它不可见。事实证明,在幕后,
    System.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" );
        }
    }
    

    现在,无论您是否导航到第二个选项卡,单击第一个选项卡上的按钮都将触发
    复选框1\u Changed
    事件过程。根据您提供的设计,如果单击按钮,它将不会显示消息框,因为
    更新将为真。但是,
    Debug.WriteLine
    将显示它在输出窗口中被激发。

    那么,按照MSDN文档的建议使用CreateHandle有什么错呢?CreateHandle是一个受保护的方法,除非我创建一个新的继承类,否则不能从代码中调用它。根据文档,调用CreateHandle的等效方法是简单地访问控件上的Handle属性。但是,即使这样做也没有帮助,因为WinForms仍然无法“查看”控件并触发CheckedChanged事件。
    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" );
        }
    }