2013-07-09 10 views
5

Hintergrund gefeuert:Bestimmen Sie, wer ein Ereignis

In meiner WinForms Form, ich habe ein Karo Listview und eine "Master" Checkbox namens checkBoxAll. Das Verhalten des Masters ist wie folgt:

  • Wenn der Master aktiviert oder deaktiviert wird, müssen alle Listviewitem entsprechend ändern.

  • Wenn der Benutzer ein ListViewItem deaktiviert, muss der Master entsprechend geändert werden.

  • Wenn der Benutzer ein ListViewItem überprüft und alle anderen ListViewItems ebenfalls überprüft werden, muss der Master entsprechend geändert werden.

Ich habe den folgenden Code geschrieben, dieses Verhalten zu imitieren:

private bool byProgram = false; //Flag to determine the caller of the code. True for program, false for user. 

private void checkBoxAll_CheckedChanged(object sender, EventArgs e) 
{ 
    //Check if the user raised this event. 
    if (!byProgram) 
    { 
     //Event was raised by user! 

     //If checkBoxAll is checked, all listviewitems must be checked too and vice versa. 

     //Check if there are any items to (un)check. 
     if (myListView.Items.Count > 0) 
     { 
      byProgram = true; //Raise flag. 

      //(Un)check every item. 
      foreach (ListViewItem lvi in myListView.Items) 
      { 
       lvi.Checked = checkBoxAll.Checked; 
      } 

      byProgram = false; //Lower flag. 
     } 
    } 
} 

private void myListView_ItemChecked(object sender, ItemCheckedEventArgs e) 
{ 
    //Get the appropiate ListView that raised this event 
    var listView = sender as ListView; 

    //Check if the user raised this event. 
    if (!byProgram) 
    { 
     //Event was raised by user! 

     //If all items are checked, set checkBoxAll checked, else: uncheck him! 

     bool allChecked = true; //This boolean will be used to set the value of checkBoxAll 

     //This event was raised by an ListViewItem so we don't have to check if any exist. 
     //Check all items untill one is not checked. 
     foreach (ListViewItem lvi in listView.Items) 
     { 
      allChecked = lvi.Checked; 
      if (!allChecked) break; 
     } 

     byProgram = true; //Raise flag. 

     //Set the checkBoxAll according to the value determined for allChecked. 
     checkBoxAll.Checked = allChecked; 

     byProgram = false; //Lower flag. 
    } 
} 

In diesem Beispiel verwende ich eine Flagge (byProgram) ein Ereignis, um sicherzustellen, dass durch die Nutzer verursacht wurde oder nicht , wodurch eine Endlosschleife verhindert wird (ein Ereignis kann ein anderes auslösen, das wiederum das erste auslösen kann usw. usw.). IMHO, das ist eine Hacky-Lösung. Ich suchte, aber ich konnte keine dokumentierte MSDN-Methode finden, um festzustellen, ob ein User Control Event dank des Benutzers direkt ausgelöst wurde. Was mir seltsam vorkommt (wieder IMHO).

Ich weiß, dass das FormClosingEventArgs über ein Feld verfügt, mit dem wir ermitteln können, ob der Benutzer das Formular schließt oder nicht. Aber soweit ich weiß, ist, dass die einzige EventArg, die diese Art von Funktionalität bietet ...

Also zusammenfassend:

Gibt es eine Möglichkeit (außer meinem Beispiel), um zu bestimmen, ob ein Ereignis war direkt vom Benutzer gefeuert?

Bitte beachten Sie: Ich meine nicht den Absender eines Ereignisses! Es spielt keine Rolle, ob ich codeCheckBox.Checked = true; oder manuelles Setzen von someCheckBox, der Absender des Ereignisses wird immer someCheckBox sein. Ich möchte herausfinden, ob es möglich ist, festzustellen, ob es durch den Benutzer (klick) oder durch das Programm (.Checked = true) war.

Aaand auch: 30% der Zeit, die es dauerte, diese Frage zu schreiben, war, die Frage und den Titel richtig zu formulieren. Immer noch nicht sicher, ob es ein 100% clear ist, also bitte bearbeiten, wenn Sie denken, dass Sie es besser machen können :)

+0

Nur eine wilde Vermutung, aber es sollte wirklich etwas in den EventArgs sein. Haben Sie sie zur Laufzeit überprüft (Debugging)? –

+0

Ein Klick-Ereignis setzt eine Markierung, um zu wissen, dass dieser Anruf von einem Klick und nicht von einem Code kommt? – MEYWD

+0

Verwenden Sie den 'Absender'? – DGibbs

Antwort

2

Das ist etwas, stoße ich auf eine sehr viel und was neige ich tun, um zu versuchen, ist spaltete es nicht zwischen Benutzer-Interaktion vs Programm: Sie können den Event-Handler vor oder nach dem jeweils ändern Steuerelemente zu entfernen und hinzufügen Interaktion - Ich verwende mehr generischen Code, dh die Benutzeroberfläche wird aktualisiert und erfordert keine Ereignisse zu behandeln. Ich verpacke dies üblicherweise durch BeginUpdate/EndUpdate Verfahren, z.B.

private int updates = 0; 

public bool Updating { get { return updates > 0; } } 

public void BeginUpdate() 
{ 
    updates++; 
} 

public void EndUpdate() 
{ 
    updates--; 
} 

public void IndividualCheckBoxChanged(...) 
{ 
    if (!Updating) 
    { 
     // run code 
    } 
} 

public void CheckAllChanged(...) 
{ 
    BeginUpdate(); 
    try 
    { 
     // run code 
    } 
    finally 
    { 
     EndUpdate(); 
    } 
} 
+1

+1, immer noch eine Flagge, aber ich werde diese Konstruktion das nächste Mal verwenden, wenn ich eine Flagge heben muss. – Jordy

+0

@Jordy es ist nicht * eine andere * Flagge, es ist eine Verbesserung Ihrer bestehenden Flagge durch Hinzufügen von mehr Struktur und Bedeutung. Das "BeginUpdate"/"EndUpdate" -Muster ist ein gut benutzter & angenommener Ansatz für diese Art von Problem, über verschiedene Plattformen hinweg (einschließlich .NET). Die Idee ist, dass Sie auf "Zustand" reagieren, unabhängig davon, ob ein Ereignis angehängt ist oder nicht, dies macht viel mehr lesbaren Code. – James

+0

Ich sehe ... Ich hätte es wahrscheinlich nicht als "immer noch eine Flagge" beschrieben. Aber aus irgendeinem Grund kann ich meinen Kommentar nicht bearbeiten, wenn es zu alt oder etwas ist: \ – Jordy

4

Anstatt das geänderte Ereignis zu verwenden, könnten Sie das angeklickte Ereignis verwenden, um die Änderung zu den relevanten Steuerelementen zu kaskadieren. Dies würde als Reaktion auf einen Benutzerklick erfolgen, und nicht der Wert, der programmatisch geändert wird.

+0

+1 für eine Lösung, die keine zusätzlichen Flags enthält. Aber wissen Sie, dass sich die Immobilie tatsächlich geändert hat? Das würde bedeuten, alte Werte zu speichern, oder? – Jordy

+1

Wenn ich ein angeklicktes Ereignis im Feld Alles auswählen bekomme, kann ich die Werte aller Kontrollkästchen festlegen. Wenn ich ein angeklicktes Ereignis für eines der Optionselemente erhalte, würde ich nur das Kontrollkästchen Alles auswählen basierend auf dem Status der Optionen aktualisieren. –

+0

Sie haben Recht! Es ist mir egal, ob der Wert sich geändert hat oder nicht. Ich kann mir nur den Wert ansehen, den es derzeit hat. – Jordy

5

Nein, es gibt keine praktische Möglichkeit zu bestimmen, ob die Änderung von der GUI kam oder von einem Programm ausgeführt wurde (in der Tat, Sie könnten den Callstack analysieren - aber das ist nicht empfehlenswert, weil er sehr langsam und fehleranfällig ist).

BTW, es gibt noch eine andere Sache, die Sie tun könnten, anstatt byProgram zu setzen.

checkBoxAll.CheckedChanged -= checkBoxAll_CheckedChanged; 
// do something 
checkBoxAll.CheckedChanged += checkBoxAll_CheckedChanged; 
+0

Das mache ich regelmäßig. – dotNET

+0

+1 für eine Lösung, die keine zusätzlichen Ereignisse oder Flags beinhaltet! – Jordy

+1

Ich würde argumentieren, dass es keine gute Idee ist, Ereignisbehandlungsroutinen auf diese Weise zu lösen/wieder anzubringen. Abgesehen von dem * leichten * Overhead, was ist, wenn Sie 20 Event-Handler haben? Ich finde, dass der Blocking-Code besser ist, weil Sie eher auf "Zustand" reagieren, als darauf, ob ein Event angeschlossen ist oder nicht, weil Sie immer die Chance haben, dass Sie vergessen, sich neu zu verbinden oder zu lösen. – James

Verwandte Themen