c# - 强制绑定(bind)到数据源的复选框在尚未查看时更新

标签 c# winforms controls deferred-loading

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

  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 上的控件未处于“已创建”状态,因此在控件“已创建”之后才会发生将复选框从未选中更改为已选中的绑定(bind)。

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

CreateControl does not create a control handle if the control's Visible property is false. You can either call the CreateHandle method or access the Handle property to create the control's handle regardless of the control's visibility, but in this case, no window handles are created for the control's children.

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

除了让程序在首次加载时快速闪烁所有具有数据绑定(bind)复选框的选项卡外,还有其他建议吗?

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

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

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

最佳答案

我想我有一个解决方案。问题不在于您不能创建句柄。您可以通过简单地访问控件上的 Handle get 访问器来做到这一点。问题是 WinForms 不创建控件,因为它不可见。事实证明,在幕后,System.Windows.Forms.Control 有两个 CreateControl 重载。第一个是公共(public)的,不带任何参数,它调用第二个 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 将显示它在输出窗口中触发。

关于c# - 强制绑定(bind)到数据源的复选框在尚未查看时更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2523778/

相关文章:

c# - 可以在 View 中显示 RelayCommand 吗?

delphi - 如何在其自身事件中释放控件?

c# - 如何将标准控件属性固定为所需值?

c# - BitConverter.ToString() 反过来?

c# - 结果集存储在模型上 - C# .NET 2.0

c# - Windows 服务启动 winforms 应用程序

c# - 为什么我的线程在显示 Windows 窗体后立即终止?

c# - 模拟拖动窗口标题栏的控件

c# - 如何中断正在运行的线程并使其运行另一个方法?

c# - 如何在 MVP 中设置控件状态