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 のコントロールが「作成済み」状態にないためです。そのため、コントロールが「作成済み」になるまでチェックボックスをオフからオンに変更するバインディングは行われません。

MSDNによると、checkbox1.CreateControl()は何もしません

コントロールの Visible プロパティが false の場合、CreateControl はコントロール ハンドルを作成しません。CreateHandle メソッドを呼び出すか、Handle プロパティにアクセスして、コントロールの可視性に関係なくコントロールのハンドルを作成できますが、この場合、コントロールの子に対してウィンドウ ハンドルは作成されません。

私はHandleの値を取得しようとしました(CheckBoxのパブリックCreateHandle()はありません)が、それでも同じ結果です。

プログラムが最初に読み込まれたときに、データにバインドされたチェックボックスを持つすべてのタブをプログラムがすばやくフラッシュする以外の提案はありますか?

EDIT - Jaxidianの提案に従って、新しいクラスを作成しました

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

updating = true前と同じ結果の直後に CreateHandle() を呼び出します。

4

1 に答える 1

12

解決策があると思います。問題は、Handle を作成できないことではありません。これは、Control の Handle get アクセサーにアクセスするだけで実行できます。問題は、コントロールが表示されないため、WinForms がコントロールを作成しないことです。結局のところ、舞台裏で、 aSystem.Windows.Forms.Controlには の 2 つのオーバーロードがありCreateControlます。最初の public はパラメーターをとらずinternal、1 つのパラメーターを取る2 番目のパラメーターを呼び出しbooleanます: ignoreVisible は、その名前が示すように、呼び出し元のコードが表示されていなくてもコントロールを作成できるようにします。引数のCreateControlないメソッドは、この内部メソッドに false を渡します。これは、コントロールが表示されていない場合は作成されないことを意味します。そのため、リフレクションを使用して内部メソッドを呼び出すのがコツです。最初に、コントロールを作成するための 2 つのメソッドを作成しました。

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次に、 2 番目のタブの呼び出しを追加します。

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

これで、2 番目のタブに移動するかどうかに関係なく、最初のタブのボタンをクリックすると、checkbox1_Changedイベント プロシージャが起動されます。あなたが提供したデザインを考えると、ボタンをクリックしても、updatingtrue になるため MessageBox は表示されません。ただし、Debug.WriteLine出力ウィンドウで起動したことが表示されます。

于 2010-04-03T03:36:40.200 に答える