Zweifache Bindung einer "virtuellen" Liste von Strings an eine Spalte

9

Ich habe eine Liste von Strings.

Nun, konzeptionell. Sie werden woanders gespeichert, aber ich möchte ein Objekt bereitstellen, das wie eine Liste funktioniert (und darüber hinaus alle notwendigen Ereignisse bereitstellt), mit Eigenschaften, an die ich mich binden könnte.

Ich möchte eine bidirektionale Bindung über diese Daten herstellen, um sie als eine änderbare Spalte in DataGrid anzuzeigen. Ich habe folgende Probleme damit:

  • Ich kann keine bidirektionale Bindung machen, da die Bindung einen Pfad benötigt (dh ich kann nicht wie {Binding} oder {Binding Path=.} in der Spalte aussehen, muss {Binding Path=someField"} als änderbar gemacht werden, wenn Ich habe das richtig verstanden, was vernünftig klingt).
  • Ich weiß nicht genau, wie das Proxy-Sammlungsobjekt im Hinblick auf Schnittstellen aussehen sollte (wäre IEnumerable + INotifyCollectionChanged ausreichend?)

Gibt es eine Lösung, bei der kein Proxy-Objekt für jeden String in der Sammlung erstellt wird? Können Sie ein effizientes Design vorschlagen?

Um die Diskussion auf den Schienen zu halten, nehmen wir an, dass ich an etwas wie dieses binden möchte:

%Vor%

Das ist nicht genau mein Fall, aber wenn ich weiß, wie ich mich daran binden kann, denke ich, dass ich in der Lage sein werde, mit jeder Situation wie dieser fertig zu werden.

Ich bin in Ordnung, wenn ich ein Adapterobjekt über der Source bereitstellen muss, mit allen notwendigen Schnittstellen und Ereignissen, aber ich möchte kein Adapterobjekt pro Datenzeile haben.

    
Kos 14.03.2012, 21:38
quelle

7 Antworten

1

Ich finde Ihre Herangehensweise etwas komisch.

DataGrids werden normalerweise zum Anzeigen von Zeilen verwendet. Zeilen bestehen aus Daten, die zusammen gehören. Sie könnten zum Beispiel einfach eine Reihe zu einer bestimmten Klasse zuordnen. Dies bedeutet, dass die Spalten in Ihrem Datagrid Eigenschaften in Ihrer Klasse darstellen.

Was Sie versuchen, ist das Gegenteil, Sie versuchen, eine Beziehung zwischen den Spaltenwerten anstelle der Zeilenwerte zu erhalten.

Wäre es nicht einfacher, eine Sammlung Ihrer Klasse zu haben, an die Sie die Spalte dann binden können?

Zum Beispiel

%Vor%

Wenn Sie eine ObservableCollection von MyClass hätten, könnten Sie das DataGrid an diese Sammlung binden. Wenn sich die Eigenschaft, die ich "Column" nannte, ändert, könnten Sie Ihre spezielle Liste aktualisieren.

Sie können dies tun, indem Sie einige Ereignisse anschließen. Mit der Implementierung von INotifyPropertyChanged werden Ihre Spalten aktualisiert, wenn Sie den "Column" -Wert direkt aktualisieren.

    
Davio 08.05.2012 14:21
quelle
1

Während ein Adapter für die Source relativ klar ist, ist der Kern des zweiten Problems ('nicht jeden String in ein Miniobjekt zu wickeln') unglücklicherweise ein in den .Net und WPF eingebauter Konflikt.

>

Das erste ist, dass das WPF Ihnen viele Möglichkeiten bietet, "auf Daten modifizierte" Callbacks zu registrieren, aber es gibt keine Möglichkeit, Callbacks zu registrieren, die einen Wert liefern würden. Ich meine, die "set" -Phase ist nur erweiterbar, nicht abschaltbar und das "bekommen" - gar nichts. WPF wird einfach die Daten behalten und zurückgeben, die es einmal zwischengespeichert hat.

Die zweite Sache ist, dass in .Net das string ... unveränderlich ist.

Wenn Sie jemals eine Zeichenfolge direkt als Pfadlose Bindung oder als Datenkontext für ein Steuerelement angeben, sind Sie in einer Sackgasse . Das Problem ist, dass WPF tatsächlich nur den tatsächlichen Wert der Bindung übergibt, ohne die Information "woher es kam". Dem zugrunde liegenden Steuerelement wird einfach die Zeichenfolgeninstanz zugewiesen, und es gibt keine vernünftige Möglichkeit, es zu ändern, da sich die Zeichenfolge selbst nicht ändern kann. Sie werden nicht einmal über einen solchen Versuch informiert, genau wie bei schreibgeschützten Eigenschaften. Wenn Sie es jemals schaffen, einen solchen Modifizierungsversuch zu unterbrechen, und wenn Sie eine korrekte neue Zeichenfolge erzeugen, wird der WPF Sie nie wieder nach dem neuen Wert fragen. Um die Benutzeroberfläche zu aktualisieren, müssten Sie manuell erzwingen, dass WPF Sie erneut anfragt, indem Sie beispielsweise die ursprüngliche Bindung ändern, sodass sie an anderer Stelle (auf den neuen Wert) oder den Datenkontext (auf die neue Instanz) verweist. Es ist mit einigen VisualTree-Scans machbar, da jeder 'geänderte' Callback Ihnen die DependencyObjects (Controls!) Gibt, so dass Sie nach oben / unten scannen und ihre Eigenschaften manipulieren können. Merken Sie sich diese Option - I ' Ich werde in einer Minute darauf hinweisen.

Also, alles läuft darauf hinaus, dass Sie, um eine normale 2-Wege-Bindung zu erhalten, keinen Pfad haben müssen, Sie müssen nur ein veränderliches darunterliegendes Datenobjekt haben. Wenn Sie einen unveränderlichen haben - dann müssen Sie eine Bindung an eine veränderbare Eigenschaft verwenden, die den unveränderlichen Wert enthält.

Nachdem Sie das gesagt haben, müssen Sie die Strings einfach umschlingen, wenn Sie modifizieren möchten.

Die andere Frage ist, wie man das macht. Es gibt viele Möglichkeiten, dies zu tun. Natürlich können Sie sie einfach so umhüllen, wie es Joe und Davio vorgeschlagen haben (Notiz an Joe: Inotify wäre dort auch nötig), oder Sie können versuchen, einige XAML-Tricks mit angehängten Eigenschaften und / oder Verhaltensweisen und / oder Konvertern zu machen Sie. Dies ist komplett machbar, siehe zum Beispiel mein anderer Beitrag - Ich habe dort gezeigt, wie man "eine virtuelle Eigenschaft" einfügt zog die Daten vollständig von woanders (ein Binde + Konverter führte das Wrapping im laufenden Betrieb aus, zweite Bindung extrahierte die Werte aus dem angehängten Wrapper). Auf diese Weise könnten Sie eine "Contents" -Eigenschaft für die Zeichenfolge erstellen, und diese Eigenschaft könnte einfach die Zeichenfolge selbst zurückgeben und wäre ohne irgendwelche Ausnahmen vollständig bidirektional bindbar.

Aber .. es würde NICHT 2-Wege-ish funktionieren.

Irgendwo an der Wurzel Ihrer Bindung / Verhalten / Conventer-Kette gibt es eine unveränderliche Zeichenfolge . Sobald Ihre Smart-Autowrapping-Bindungskette mit einem "On-Modified" -Rückruf ausgelöst wird, werden Sie mit einem Paar alter / neuer Werte benachrichtigt. Sie können die Werte neuen und alten Strings zuweisen. Wenn Sie alles perfekt implementiert haben, wird der WPF einfach den neuen Wert verwenden. Wenn Sie irgendwo gestolpert sind, müssen Sie den neuen Wert künstlich auf die Benutzeroberfläche zurücksetzen (siehe die Optionen, an die ich Sie erinnern wollte). Also, es ist in Ordnung. Kein Wrapper, alter Wert war sichtbar, er war änderbar, du hast einen neuen Wert, die UI zeigt einen neuen Wert an. Wie wäre es mit Speicher?

Irgendwo in der Zwischenzeit hast du ein altes / neues Wertpaar bekommen. Wenn Sie sie analysieren, erhalten Sie alte / neue Zeichenfolgen. Aber wie aktualisieren Sie die alte unveränderliche Zeichenfolge ? Kann nicht tun. Selbst wenn das Autowrapping funktioniert, selbst wenn die UI funktioniert, selbst wenn die Bearbeitung zu funktionieren scheint, stehen Sie nun mit der eigentlichen Aufgabe: Sie haben einen modifizierten Callback aufgerufen und müssen diesen unveränderlichen String tatsächlich aktualisieren.

Zuerst brauchst du deine Quelle. Ist es statisch? Puh. Was für ein Glück! So ist es sicherlich instanziert. Im On-Modified-Callback haben wir nur eine alte + neue Zeichenkette erhalten. Wie bekommt man die Source-Instanz? Optionen:

  • scannen Sie den VisualTree und suchen Sie in den Datenkontexten danach und verwenden Sie, was gefunden wurde ..
  • fügen Sie weitere angefügte Eigenschaften und Bindungen hinzu, um eine virtuelle "Source" -Eigenschaft an jede Zeichenfolge zu binden und diese Eigenschaft von dem neuen Wert
  • zu lesen

Gut machbar, aber riecht, aber keine anderen Optionen.

Warten Sie, es gibt noch mehr: Es werden nicht nur der alte / neue Wert und eine Instanz von Source benötigt! Sie benötigen auch den REIHENINDEX. Oh! Wie bekomme ich das aus den gebundenen Daten? Noch einmal, Optionen:

  • scanne den VisualTree und suche danach (blaargh) ...
  • fügen Sie weitere angehängte Eigenschaften und Bindungen hinzu, um eine virtuelle "RowIndex" -Eigenschaft an jedes (blaaergh) ...
  • zu binden

Zu diesem Zeitpunkt, während ich sehe, dass all dies implementierbar scheint und tatsächlich richtig funktioniert , denke ich wirklich, dass jede Zeichenfolge in ein kleines

eingeschlossen wird %Vor%

wird einfach lesbarer, eleganter und .. KURZER zu implementieren. Und weniger fehleranfällig, da mehr Details explizit sein werden, anstelle von WPFs Bindung + angehängter Magie.

    
quetzalcoatl 28.08.2012 13:31
quelle
0

Ich habe dieses bisschen Code, um eine Liste von benutzerdefinierten Objekten an ein DataContextMenu zu binden. Sie können es ändern, um eine Liste von Strings zu verwenden und sie an das zu binden, was Sie brauchen

%Vor%     
Jmyster 14.03.2012 21:50
quelle
0

Lazy Lösung

Leite von ObservableCollection<string> ab und lasse diese Sammlung aus der Quelle bestehen. Registrieren Sie in der abgeleiteten Klasse Ereignisse für Sammlungsänderungen und aktualisieren Sie die Quelle entsprechend. Binden Sie die DataGrid-Spalte an die beobachtbare Auflistung.

Das sollte ziemlich einfach zu schreiben sein, hat aber den großen Nachteil, alle Daten in der Sammlung zu duplizieren.

Effizientere Lösung

Erstellen Sie einen Adapter (wie Sie es vorgeschlagen haben) und implementieren Sie IList<string> und INotifyCollectionChanged . Lassen Sie die Listenoperationen direkt zur Quelle durch. Binden Sie die DataGrid-Spalte an den Adapter.

Dieser Ansatz würde einige langweilige Textbausteine ​​erfordern, aber es ist eine dünne Schicht zwischen dem WPF-Steuerelement und Ihrem Source .

    
Dominik 21.04.2012 22:33
quelle
0

Dies hängt wirklich davon ab, wie Sie die Benutzeroberfläche implementieren. Bea Stollnitz hat die ItemsSource für das WPF DataGrid in Ссылка ausgezeichnet virtualisiert. Mit der Arbeit habe ich sowohl Daten bearbeitet als auch angezeigt.

    
Echilon 22.06.2012 11:03
quelle
0

Der einfachste Weg ist, die Zeichenfolge in eine Wrapper-Klasse zu platzieren.

%Vor%

Dann verwenden Sie den String über die Wrapper-Klasse. Dies war die Liste Elemente bleiben gleich, aber der Inhalt ändert sich. Das Problem ist, wenn Sie dies ohne das tun, dann wird eine alte Zeichenfolge gelöscht und eine neue Zeichenfolge erstellt und die Sammlung ist verwirrt.

    
Joe Sonderegger 28.08.2012 12:39
quelle
-2

Beginnen Sie mit einem ObservableCollection<string> . Setzen Sie dann die ItemsSource des bindbaren Steuerelements auf die ObservableCollection.

    
code4life 14.03.2012 21:42
quelle

Tags und Links