2016-08-04 36 views
0

Ich habe einen Kampf mit einer ComboBox in einer WPF-App. Es ähnelt einigen anderen Fragen, aber die klassische Lösung für dieses Problem scheint nicht zu funktionieren.WPF ComboBox-Einstellung auf Null nach ItemsSource aktualisiert

Im Wesentlichen ist es das gleiche Problem wie folgt aus:

WPF ComboBox SelectedItem Set to Null on TabControl Switch

jedoch meine Itemssource ist bereits in der XAML nach dem SelectedItem, das ist das, was normalerweise diese aussortiert.

Was passiert, ist, dass ich eine Ansicht mit der Combobox darauf mit Daten bereits geladen habe, dann wird ein Ereignis ausgelöst, das die Dateneinspeisung in die ComboBox aktualisiert. Das ViewModel konsumiert das Ereignis (ausgelöst durch einen BackgroundWorker, der die Daten abruft) und aktualisiert seine ObservableCollection, die ItemsSource, mit den neuen Daten. Wie folgt aus:

int id = (int)Invoice.Customer.DatabaseID; 
Customers = new ObservableCollection<Customer>(customers); 
Invoice.Customer = Customers.FirstOrDefault(x => x.DatabaseID == id); 

Wie Sie sehen können ist es, den Kunden auf der Rechnung zu setzen versucht, zurück zu dem, was es ursprünglich war. Dies geschieht jedoch, beobachtet mit einem Unterbrechungspunkt, sobald dies abgeschlossen ist, wird der Kunde von einer nicht identifizierten Quelle auf Null zurückgesetzt (keiner meiner Codes erscheint in der Aufrufliste, es ist alles Framework-Zeug).

Die XAML für die ComboBox ist dies:

<ComboBox DisplayMemberPath="AccountCode" 
    SelectedItem="{Binding Invoice.Customer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" 
    ItemsSource="{Binding Customers}"/> 

So wird meine ComboBox zusammenzufassen SelectedItem auf null gesetzt, nachdem der Itemssource aktualisiert wird und gewährleistet ist nach Itemssource SelectedItem nichts tut. Ich kann wirklich nicht herausfinden, warum es auf Null gesetzt wird und ich bin mir nicht sicher, wo ich hinschauen soll. Irgendwelche Hinweise oder Dinge, die ich betrachten kann, um eine Lösung zu finden, würde sehr geschätzt werden.

EDIT: Ok, ich habe damit ein bisschen mehr gespielt und ich vermute, dass es etwas mit dem Update von einem BackgroundWorker zu tun hat. Ich verwende einen Timer und einen BackgroundWorker in meinem Datendienst, um die Kundenliste regelmäßig von der Datenbank zu aktualisieren, um sicherzustellen, dass die Daten relativ aktuell sind. Der BackgroundWorker löst nach dem Beenden ein Ereignis aus, um interessierte Objekte darüber zu informieren, dass die Liste aktualisiert wurde. Dies scheint zu bedeuten, dass wenn die Ereignisse verbraucht werden, sie sich in einem anderen Thread befinden. Wenn es auf diese Weise aktualisiert wird, wird das SelectedItem auf null gesetzt, nachdem ich es auf das richtige Element festgelegt habe, und setzt daher Invoice.Customer auf null. Ich habe meiner Ansicht schnell einen Button hinzugefügt, um die Kunden zu aktualisieren, ohne den BackgroundWorker zu verwenden. Dies scheint jedes Mal zu funktionieren. Ich möchte die Daten regelmäßig aktualisieren, aber ich muss das herausfinden, bevor ich das kann.

+0

Ich habe meine ItemsSource aktualisiert und mein SelectedItem bleibt dort, wo es sein sollte. Kann das Problem nicht reproduzieren. Zeigt die Definition der Invoice.Customer -Eigenschaft an. – AnjumSKhan

Antwort

-1

Ok, wie ich in meinem Schnitt vermutete, war dies in gewisser Weise mit Threading zu tun. Das Aktualisieren der ComboBox ItemsSource nach dem Absetzen des Updates durch einen Timer führte dazu, dass es auf Null gesetzt wurde, nachdem es auf den korrekten Kunden aktualisiert wurde. Ich bestätigte dieses Verhalten in einer neuen App, die nicht alle anderen Bits enthielt und daher konnte ich sie möglicherweise auf null setzen, wozu ich eigentlich nicht in der Lage war (obwohl der Call-Stack stark darauf hinwies, dass es nicht ich war) es tun). Als das Ereignis ausgelöst wurde, um die Ergebnisse zu aktualisieren, bekam ich etwas, das in der neuen App im Vergleich zur echten App genau der gleiche Aufruf-Stack aussah.

Nach ein bisschen herumspielen mit verschiedenen Dingen war die Methode, die ich gefunden habe (in meiner neuen App - nicht auf die echte App verteilt, aber die Daumen drücken, auch dort!) War es, die Veranstaltung von einem gefeuert zu haben konkurrierendes Update durchläuft eine TaskFactory (Idee von hier Is it wrong to use the Dispatcher within my ViewModel?).

Im Ansichtsmodell eine Taskfactory erklären:

TaskFactory uiFactory; 

In Ihrem Konstruktor festgelegt es wie folgt auf:

uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); 

Dann in dem Fall, dass die Daten läuft, nachdem etwas tun, wie diese aktualisiert wurde :

private void AsyncMethods_TaskCompleted(object sender, EventArgs e) 
{ 
    uiFactory.StartNew(() => UpdateResults()); 
} 

Und UpdateResults in diesem Kontext ist das gleiche wie die Aktualisierung des Kunden auf t er Rechnung. Es ruft die alte ID ab, legt die ItemsSource auf die neue Auflistung fest und legt dann die an SelectedItem gebundene Eigenschaft auf das entsprechende Element in der neuen Auflistung fest. Dies scheint zu funktionieren und mir nicht das seltsame Verhalten zu geben, das ich vorher hatte. Ich werde es in der eigentlichen App bereitstellen und hoffe, dass es auch dort funktioniert. Wenn ja, komme ich zurück und akzeptiere diese Antwort.

-1

Wenn Sie eine 'neue' Instanz eines Objekts erstellen, kann es manchmal Ihre Bindungen brechen. Sie können die vorhandene Sammlung aktualisieren, ohne "neu" aufzurufen ODER Sie können die ObservableCollection zu einer Abhängigkeitseigenschaft machen.

-1

Das Problem wird durch diese 2 Zeilen verursacht.

Customers = new ObservableCollection<Customer>(customers); 
Invoice.Customer = Customers.FirstOrDefault(x => x.DatabaseID == id); 

Ihre Combobox Quelle ist Customers und du es bist Initialisierung wieder. Dann versuchen Sie, Daten von einem neu initialisierten Element abzurufen. Ein neu initialisiertes Mitglied enthält keine Daten.

dh Customers enthält keine Daten. Daher wird Invoice.Customer wahrscheinlich null sein.

Ich verstehe nicht, warum Sie es initialisieren und einfach versuchen, Daten daraus zu bekommen. Haben Sie übersprungen, um die Quelle zu füllen?

Wenn Sie die Quelle nicht ausfüllen konnten, füllen Sie die Quelle zuerst mit Daten. Dann können Sie diesen Code ausführen, ohne ihn erneut zu initialisieren, sodass Invoice.Customer nicht null ist.

+0

Die Quelle ist gefüllt. Ich habe das vielleicht nicht gut genug erklärt, aber ich habe einen Punkt auf die Rechnung gesetzt. Es setzt Kunden wie erwartet (von Invoice.Customer = ...), aber dann wird es durch etwas anderes auf null gesetzt, und ich nehme an, es ist etwas im Framework, da der Stack-Trace nur externen Code enthält. – Tominator

+0

Wie kann das Framework es so einstellen? Es ist unmöglich. Eine Eigenschaft kann niemals null sein. Vielleicht setzen Sie irgendwo im Code selbst null. – ViVi

+0

Wenn Sie ItemsSource vor SelectedItem in das XAML-Objekt einfügen, kann es Dinge wie in der verknüpften Frage auf Null setzen. Ich habe das hier noch nicht gemacht, aber ich bekomme immer noch mein SelectedItem auf null und es ist nicht von irgendwo in meinem Code, den ich finden kann. – Tominator