ObservableCollectionAdapter-Klasse

Passt eine ObservableCollection eines Typs zur Verwendung als ObservableCollection eines Basistyps an, wo Kovarianz nicht funktioniert.

Gelegentlich kommt es vor, dass man eine generische Auflistung mit Elementen eines bestimmten Typs als Auflistung mit Elementen eines allgemeineren Basistyps verwenden möchte. Einem Feld des Typs List<Control> sollte sich doch prinzipiell eine List<Button>-Instanz zuweisen lassen. Seit .NET 4.0 wird zu diesem Zweck die sogenannte Kovarianz unterstützt, die die Vererbungsbeziehung auf generische Typen anwenden soll. Leider funktioniert das nur in wenigen Spezialfällen, man kann sich im Allgemeinen nicht auf diese Funktion verlassen. Ein ausführlicher Artikel von Marc Gravell erklärt die Hintergründe der Thematik.

So ist es auch mir in einem WPF-MVVM-Projekt ergangen, als ich einer ObservableCollection für allgemeine ViewModel-Instanzen dann eine Collection mit spezialisierteren ViewModel-Instanzen zuweisen wollte und das nicht funktioniert hat. Für diesen Zweck habe ich diesen einfachen Adapter geschrieben. Diese Klasse ist von ObservableCollection<BaseType> abgeleitet, stellt aber zusätzlich eine Verbindung zu einer ObservableCollection<SubType>-Instanz her und synchronisiert die Auflistungen automatisch. Das ist möglich, weil eine ObservableCollection die dafür nötigen Ereignisse auslöst. Wann immer die Collection auf einer Seite des Adapters verändert wird, überträgt der Adapter die Änderung auch zur anderen Seite, als wäre es dieselbe Collection-Instanz.

Kompatibilität: .NET Ab Version 4.0

Beispiel

Der Zweck und die Verwendung dieser Klasse lassen sich vielleicht am besten durch ein Beispiel erläutern. Nehmen wir an, es gibt eine Baumansicht in einer WPF-View, die verschiedene Objekte enthalten kann. Wie man das WPF-TreeView ganz einfach per ViewModel verwendet erklärt ein CodeProject-Artikel von Josh Smith. Alle Objekte sind vom Typ TreeItem­ViewModel, das neben diversen Anzeigezuständen u. a. in einer Children-Eigenschaft die Unterelemente des Knotens enthält (ObservableCollection<TreeItemViewModel>). Ein davon abgeleiteter Typ sei das PersonViewModel, das eine Person darstellt. Er ist damit zur Verwendung mit TreeView-Elementen geeignet. In der Anwendung gibt es jetzt eine Eigenschaft, die alle Personen als Auflistung von PersonViewModels enthält (ObservableCollection<PersonViewModel>).

Wenn ein Knoten im Baum nur Personen enthält, könnte man dessen Children-Eigenschaft auch die Personenauflistung zuweisen, was aber an den inkompatiblen generischen Typen scheitert. Stattdessen kann jetzt dieser Adapter verwendet werden:

// PersonListViewModel ist das ViewModel für den Tree-Knoten, der nur Personen
// enthält. Es ist von TreeItemViewModel abgeleitet, weil es selbst ein Knoten
// in der Baumansicht ist.
class PersonListViewModel : TreeItemViewModel
{
    // Die LoadChildren-Methode wird aufgerufen, wenn die Unterknoten eines
    // Tree-Knotens erstellt werden.
    protected override void LoadChildren()
    {
        // Die Children-Eigenschaft aus TreeItemViewModel ist vom Typ
        // ObservableCollection<TreeItemViewModel>, da sie alle Unterknoten
        // enthält und keinen spezifischeren Typ festlegen kann.
        Children =
            new ObservableCollectionAdapter<TreeItemViewModel, PersonViewModel>(
                project.PersonVMs);
    }
}

Sobald die Anwendung jetzt die Auflistung der Personen im Projekt verändert, also z. B. eine neue Person hinzufügt, wird diese Änderung automatisch in die Children-Auflistung des TreeItemViewModels übertragen und der neue Eintrag erscheint in der Baumansicht. Ganz so, wie man es von der WPF-Datenbindung erwartet.

Hinweise zur Verwendung

Zur Verwendung der ObservableCollectionAdapter-Klasse wird die OpLock-Klasse benötigt.

Download

ObservableCollectionAdapter.cs2,5 KiBQuelltext der ObservableCollectionAdapter-Klasse

Lizenz und Nutzungsbedingungen

Vervielfältigung und Weiterverbreitung dieser Datei, verändert oder unverändert, sind gestattet, vorausgesetzt die Urheberrechtsangabe und dieser Hinweis bleiben erhalten. Diese Datei wird wie vorliegend ohne jegliche Garantie oder Gewährleistung angeboten. (GNU All-Permissive-Lizenz)

Statistische Daten

  • Erstellt am 2012-09-06.