Принудительный флажок привязан к DataSource для обновления, если он еще не был просмотрен

Вот тестовая среда, показывающая, что я делаю:

  • создать новый проект
  • добавить элемент управления с вкладками
  • на вкладке 1 нажмите кнопку
  • на вкладке 2 установите флажок
  • вставьте этот код для своего кода

(используйте имена по умолчанию для элементов управления)

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 (нет public CreateHandle() для CheckBox), но все тот же результат.

Любые предложения, кроме того, что программа быстро мигает всеми моими вкладками, которые имеют флаги привязки к данным при первой загрузке?

РЕДАКТИРОВАНИЕ - за предложение Ясидиан Я создал новый класс

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

Я вызываю CreateHandle() сразу после updating = true тех же результатов, что и раньше.

Ответ 1

Я думаю, что у меня есть решение. Проблема заключается не в том, что вы не можете создать Handle. Вы можете сделать это, просто обратившись к Access Access на Control. Проблема в том, что WinForms не создает элемент управления, потому что он не отображается. Как оказалось, за кулисами System.Windows.Forms.Control имеет две перегрузки для CreateControl. Первый, который является общедоступным, не принимает никаких параметров и вызывает вторую, которая 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 будет правдой. Тем не менее, Debug.WriteLine покажет, что он запускается в окне вывода.