2009-04-20 6 views
5

Das folgende Bild zeigt, wie mein Code funktioniert. Wenn ich Knopf2 drücke, wird das Listenfeld aktualisiert, aber nicht, wenn ich Knopf1 drücke. Warum?Winforms-Listenfeld wird nicht aktualisiert, wenn sich die gebundenen Daten ändern

pseudo code http://i44.tinypic.com/mj69oj.gif

Ist das Problem damit verbundene Threading? Wenn ja, wo sollte ich den Aufruf zu (Begin) Invoke hinzufügen?

Eine interessante Sache zu beachten ist, dass, wenn ich zuerst Button1 und dann Button2 die Daten, die durch den Klick Button1 erzeugt werden, wenn ich auf Button2 klick. Es sieht also so aus, als würden die von doFoo erzeugten Daten irgendwo gepuffert und dann in die Listbox geschoben, sobald ich Button2 drücke.

EDIT:

versuchte ich AddNumber das Formular Code hinzufügen, und fügte hinzu, um einen Anruf zu berufen, wenn listBox1.InvokeRequired true zurück. Dies löst das Problem, ist aber nicht das schönste Design. Ich möchte nicht, dass die GUI sich darüber Gedanken machen muss, wie man Elemente zu einer Liste hinzufügt, die Teil des Modells ist.

Wie kann ich die Logik hinter dem Hinzufügen zu der Liste innerhalb der Listenklasse beibehalten, während die GUI immer noch aktualisiert wird, wenn sich die Liste ändert?

EDIT 2:

Jetzt, wo wir bestätigt haben, dass dies ein Threading-Problem ist ich das Bild aktualisiert haben enger an das Design des eigentlichen Code reflektieren ich arbeite.

Während Luceros Vorschlag immer noch das Problem löst, habe ich auf etwas gehofft, das die Form nicht benötigt, um etwas über die DLL oder CDllWrapper zu wissen.

Das Modell (ListBoxDataBindingSource usw.) sollte gar nichts über die Ansicht (Listenfelder, Knöpfe, Etiketten usw.)

Antwort

2

Meine Vermutung weiß, ist, dass dies auf die Update-Meldung beruht auf dem falschen Thread gehandhabt wird. Hintergrund: Jeder Thread hat seine eigene Nachrichtenwarteschlange. Nachrichten, die in die Nachrichtenwarteschlange gestellt werden, landen standardmäßig im selben Thread wie der Anrufer. Daher wird der Rückruf möglicherweise eine Nachricht auf dem falschen Thread senden.

Versuchen Sie Folgendes: Verschieben Sie die AddNumber() - Methode in das Formular und verwenden Sie Invoke() (geerbt von Control), um das Element im richtigen Thread hinzuzufügen. Dies kann das Problem beseitigen.

Bearbeiten Sie, um Ihre Folge zu reflektieren: Die UI muss nicht über Ihre Komponente wissen. Was Sie brauchen, ist nur eine ordnungsgemäße Synchronisierung zwischen dem Hinzufügen des Elements zu Ihrer Liste und der Benutzeroberfläche, da UI-Updates nur funktionieren werden, wenn der Thread übereinstimmt. Daher möchten Sie möglicherweise das Steuerelement an die Klasse übergeben, die BindingList umschließt, und dann Invoke für die Liste selbst ausführen. Dadurch macht sich die Liste Sorgen über das Auslösen des upate im UI-Thread und nimmt die Sorge von der Benutzeroberfläche und der externen Komponente des Aufrufs des Handlers für den richtigen Thread in Kauf.

So:

internal class ListBoxDataBindingSource { 
    private readonly Control uiInvokeControl; 
    private readonly BindingList<Item> list = new BindingList<Item>(); 

    public ListBoxDataBindingSource(Control uiInvokeControl) { 
     if (uiInvokeControl == null) { 
      throw new ArgumentNullException("uiInvokeControl"); 
     } 
     this.uiInvokeControl = uiInvokeControl; 
     CDIIWrapper.setFP(AddNumber); 
    } 

    public void AddNumber(int num) { 
     Item item = new Item(num.ToString()); 
     if (uiInvokeControl.InvokeRequired) { 
      uiInvokeControl.Invoke(list.Add, item); 
     } else { 
      list.Add(item); 
     } 
    } 

    private BindingList<Item> List { 
     get { 
      return list; 
     } 
    } 
} 
+0

Ich verstehe, was das Problem ist, jetzt müssen wir nur mit einer guten Lösung kommen :) Ich versuche, dies nach dem MVC-Muster zu entwerfen, so dass ListBoxDa taBindingSource (Teil des Modells) wissen über ein Steuerelement (Teil der Ansicht) wird im Allgemeinen als schlechte Praxis angesehen. – Tobbe

+0

Nun, es ist nicht wirklich über das Steuerelement zu wissen, sondern muss nur heißen, um den Add-Aufruf zum richtigen Thread zu marshalieren. Sie könnten Ihre eigene Marshalling-Klasse erstellen, die das Steuerelement kennt und an die Liste übergeben wird, sodass dieser Aspekt ordnungsgemäß ausgeblendet wird. – Lucero

+0

(* @ Tobbe: * Vielleicht [diese Antwort] (http://stackoverflow.com/questions/3381536/winforms-data-binding-to-business-objects-in-a-multi-threaded-scenario-without-in-)/3381685 # 3381685) auf eine meiner vorherigen Fragen - [Winforms-Datenbindung an Geschäftsobjekte in einem Multithread-Szenario ohne InvokeRequired?] (Http://stackoverflow.com/q/3381536/240733) - wird angewendet hier.) – stakx

0

Statt die setFP zu haben setzen Sie den Rückruf an lbDataBindingSource.AddNumber, eine private Methode in Ihrem Code erstellen den Rückruf zu handhaben und dann lbDataBindingSource.AddNumber rufen aus diesem Rückruf.

1

Ich weiß, das ist alt, obwohl ich ein sehr ähnliches Problem hatte.

Hier war die Lösung: BindingList not updating bound ListBox.

+0

Danke. Ich habe dieses Projekt vor langer Zeit auf Eis gelegt, aber wenn ich es wieder aufnehme, werde ich sehen, ob das hilft :) – Tobbe

0

Ich brauche meine Ansicht Modell nennen die Dinge auf die Binding hinzuzufügen, so brauche ich eine anonyme Funktion

Verweis auf Lucero Antwort- und folgende Beitrag zu schreiben: Anonymous method in Invoke call

Mein Code:

listBox.Invoke((Action)delegate 
{ 
    MyViewModel.AddItem(param1, param2); 
}); 
Verwandte Themen