2010-03-26 8 views
11

Ecco un framework di test per mostrare quello che sto facendo:Forzare una casella di controllo associato a un DataSource per aggiornare quando non è stato visto ancora

  1. creare un nuovo progetto
  2. aggiungere un controllo a schede
  3. sulla scheda 1 mettere un pulsante
  4. sulla scheda 2 mettere una casella di controllo
  5. incollare questo codice per il suo codice

(nomi predefiniti di utilizzo per i controlli)

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

Il problema è che se si esegue il programma e guardare scheda 2, quindi premere il pulsante sul scheda 1 Il programma funziona come previsto, se si preme il pulsante sulla scheda 1 poi guardate la scheda 2 l'evento per la casella di controllo non sparerà finché non guardate la scheda 2.

Il motivo per questo è il controllo sulla scheda 2 non è nello stato "creato", quindi il suo legame per cambiare il la casella di controllo da deselezionata a selezionata non si verifica fino a quando il controllo non è stato "Creato".

checkbox1.CreateControl() non fa nulla perché secondo MSDN

CreateControl non crea una maniglia controllo, se proprietà Visible del controllo è falsa. È possibile o chiamare il metodo CreateHandle o accesso alla proprietà Handle per creare maniglia del controllo indipendentemente dalla visibilità del controllo , ma in questo caso , senza maniglie delle finestre sono creati per i bambini del controllo.

ho cercato di ottenere il valore di Maniglia (non c'è pubblico CreateHandle() per casella di controllo), ma ancora lo stesso risultato.

Qualche suggerimento diverso dal fatto che il programma flash rapidamente tutte le mie schede con caselle di controllo associate ai dati quando vengono caricate per la prima volta?

EDIT-- per suggerimento di Jaxidian Ho creato una nuova classe

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

chiamo CreateHandle() subito dopo updating = true stessi risultati di prima.

+2

Quindi cosa c'è di sbagliato nell'usare CreateHandle come suggerisce la documentazione MSDN? – Jaxidian

+0

CreateHandle è un metodo protetto, non può essere chiamato dal codice a meno che non crei una nuova classe ereditata. –

+1

Secondo i documenti, l'equivalente di chiamare CreateHandle è semplicemente accedere alla proprietà Handle sul controllo. Tuttavia, anche fare ciò non è stato di aiuto dato che WinForms continua a non "vedere" il controllo e ad attivare l'evento CheckedChanged. – Thomas

risposta

11

Penso di avere una soluzione. Il problema non è che non è possibile creare un handle. Puoi farlo semplicemente accedendo alla maniglia ottieni accessor sul Control. Il problema è che WinForms non crea il controllo perché non è visibile. Come risulta, dietro le quinte, un System.Windows.Forms.Control ha due sovraccarichi per CreateControl. Il primo, che è pubblico, non accetta parametri e chiama il secondo che è internal che accetta un singolo parametro boolean: ignoreVisible che, come suggerisce il nome, consente al codice chiamante di creare il controllo anche se non è visibile. Il metodo CreateControl senza argomenti passa falso a questo metodo interno, il che significa che se il controllo non è visibile, non viene creato. Quindi, il trucco è usare Reflection per chiamare il metodo interno.In primo luogo, ho creato due metodi per i controlli che creano:

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

Ora, aggiungiamo una chiamata al CreateControls per la seconda scheda:

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

Inoltre, ho aggiunto alcuni messaggi di debug in modo da poter vedere se l'evento ha sparato:

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

Ora, se si naviga alla seconda scheda o no, facendo clic sul pulsante nella prima scheda scatterà la procedura checkbox1_Changed evento. Dato il design che hai fornito, se fai clic sul pulsante, non visualizzerà il MessageBox perché updating sarà vero. Tuttavia, lo Debug.WriteLine mostrerà che è stato attivato nella finestra Output.

+0

Lo verificherò lunedì per vedere se questo funziona nel mio codice reale se si ottiene il 100 rep. –