Laden...

In XAML auf Instanz von ViewModel zugreifen

Erstellt von robin_ vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.888 Views
R
robin_ Themenstarter:in
38 Beiträge seit 2017
vor 6 Jahren
In XAML auf Instanz von ViewModel zugreifen

Hallo, ich habe folgendes Problem und habe beim googlen nichts finden können, da anscheinend der andere Weg gefragter ist...

Also, ich habe ein ViewModel, welches verschiedene Instanzen von anderen ViewModels enthält.

In einer ListView wird an eine gemeinsame Eigenschaft gebunden und bei der Auswahl eines der Objekte wird der angebundene ContentControl automatisch geladen und angezeigt.

Ich habe nun folgendes Problem: Im Konstruktor des 1. ViewModels wird eine Instanz erzeugt, welche in eine ObservableCollection<> kommt und im ListView angezeigt wird.
Wenn was ausgewählt wird, kommt nun der ContentControl zum Einsatz. Bisher habe ich bei aber immer nur im XAML eine Instanz mit

<ns:MeineKlasse x:key="vm"/>

machen können. Im folgenden Fall würde ich dann ja mit 2 verschiedenen Instanzen arbeiten, einmal die, die im 1. VM erzeugt wird und dann die, die im ResourcenWörterbuch erstellt wird.

Wie gehe ich vor? Gibt es was ähnliches wie TryFindResource? Im Prinzip müsste ich im DataContext anstelle von

{StaticResource meinVM}

auf die Instanz irgendeiner Klasse verweisen...

Danke

MfG Robin

5.657 Beiträge seit 2006
vor 6 Jahren

Siehe dazu Abschnitt 2.2 Instanziierung des ViewModels in [Artikel] MVVM und DataBinding

Weeks of programming can save you hours of planning

M
177 Beiträge seit 2009
vor 6 Jahren

Instanzen von Konstruktoren mit Überladungen im XAML zu initialisieren geht meines Wissens nicht. Du brauchst also immer einen Parameterlosen Konstruktor.

Generell solltest du ViewModelinstanzen im XAML nicht erstellen. Das macht man meistens nur bei Beispielprojekten (weil bequem und schnell).

Besser ist du verwaltest die VMs im Codebehind. In größeren Projekten übernehmen das ViewModel Locator Services in kombination mit Dependency Injection Frameworks wie MEF (wird z.b. von Visual Studio selbst verwendet), Unity um mal zwei zu nennen.

R
robin_ Themenstarter:in
38 Beiträge seit 2017
vor 6 Jahren

OK, das war auch meine Überlegung, also per DataContext zuweisen.

Das geht im MainWindow auch mit this.DataContext recht einfach... Im ViewModel, welches nicht von Window abgeleitet ist, klappt das m.W.n. nicht, richtig?

Ich kann also über Application.current.MainWindow gehen... Aber das kann ich dann ja nur für ein ViewModel machen und nicht mehrere... Muss ich mich dann durch die Einzelnen Elemente hangeln, nur um dem richtigen dann die Instanz per DataContext zuzuweisen?

MfG

MfG Robin

M
177 Beiträge seit 2009
vor 6 Jahren

Besser ist du verwaltest die VMs im Codebehind.

Das kannst du z.B. so machen:

Du hast in deinem WPF Projekt eine App.xaml.cs. In dieser überschreibst du die OnStartup Methode. Dort drinnen erzeugst du die Window als auch deine ViewModel Klassen Instanz(en). Beim Window die Show Methode aufrufen nicht vergessen! [...] In der App.xaml entfernst du den Eintrag StartupUri="Window1.xaml". Damit verhinderst du, dass das Window nicht vom "XAML" erzeugt wird. [Source]

Du hast ja gesagt, dass du so etwas wie ein Haupt-ViewModel hast die die anderen ViewModels verwaltet.

Du könntest also z.B. alle ViewModel-Instanzen in der Startup Methode erzeugen und dann via Konstruktor dem Haupt-ViewModel übergeben.

R
robin_ Themenstarter:in
38 Beiträge seit 2017
vor 6 Jahren

OK, dann werde ich es jetzt so machen, dass alle ViewModels's im Code erzeugt werden - finde ich auch besser.

Nun habe ich ebenfalls mittels

protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MainWindow w = new MainWindow();
            _MainViewModel = new MainViewModel();
            w.DataContext = _MainViewModel;
            w.Show();
        }

Dem Window.DataContext eine Instanz des MainViewModel's übergeben. Nun die wichtige Frage ( Ich bin jetzt an einer x-beliebigen Stelle, wo ich vorher über XAML die Instanz erstellt hätte):

Über DataContext hätte ich jetzt einem Sub-Control sein ViewModel zugewiesen.. Mittels StaticResources. Ich muss nun also hergehen und sagen, dass der DataContext des ListViews nicht

Window.DataContext

ist sondern

Window.DataContext.MeineEigenschaft

, wobei MeineEigenschaft z.B. Ein SubViewModel, welches im MainViewModel erzeugt wurde. Mittels ItemsSource könnte ich dann zb. an Eigenschaften von SubViewModel binden... Verstehst du was ich meine?

Ich muss also sagen, dass der DataContext meines Sub-Controls eine Eigenschaft, also ein Path, des Objektes ist, das an den DataContext des Windows übergeben wurde.

MfG

MfG Robin

M
177 Beiträge seit 2009
vor 6 Jahren

Über DataContext hätte ich jetzt einem Sub-Control sein ViewModel zugewiesen.. Mittels StaticResources.

Eine StaticResource ist nicht notwendig. Die Controls erben alle den DataContext vom Window. Du verwendest für dein Vorhaben also ein normales Binding.


[...]
<UserControl DataContext={Binding SubVM} />

R
robin_ Themenstarter:in
38 Beiträge seit 2017
vor 6 Jahren

Eine StaticResource ist nicht notwendig. Die Controls erben alle den DataContext vom Window. Du verwendest für dein Vorhaben also ein normales Binding.

Grrrr... Danke für die Antwort, ich hätte schwören können, dass ich das Probiert habe und einen Compiler-Fehler hatte... Hab also anscheinend doch irgendwas trotz x-maliger Überprüfung übersehen. Gut. Hatte mich eh gewundert, dass das nicht klappte.

Bisher läuft das mit den Bindings soweit super, ich bin immer mehr davon begeistert, wie einfach es doch im Endeffekt ist!

Ich habe vorhin auch den Beispiel-Converter benutzt, um die Bindings zu debuggen.
Und genau da bin ich jetzt:

  1. ListView klappt, dem DataContext wird ein Objekt vom Typ
System.Windows.Data.ListCollectionView

übergeben und im GridView dann an eine Eigenschaft mit dem Bezeichner "Name" gebunden. Klappt.

Nun ein 2. ListView, auch dort erhalte ich mittels Converter als value ein Object von oben genannten Typ - jedoch ist die ListView leider leer. Value enthält auch die richtige Anzahl an Objekten (In meinem Fall heißen die "SubjectModel" mit der einzigen Eigenschaft Name, die auch richtig initialisiert wird) und der Zeiger "CurrentItem" zeigt auf das erste Objekt. Scheint also von der Datenlage her richtig zu sein. Nun der Code:

<ListView Grid.Row="2" Name="ScheduleListView" DataContext="{Binding ScheduleCollectionView, Converter={StaticResource DebugConverter}}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn DisplayMemberBinding="{Binding Name,Converter={StaticResource DebugConverter}}" Header="Mein Name" />
                    </GridView>
                </ListView.View>

Ich erhalte keinen Fehler in der Ausgabe und der Converter wird auch nur beim ListView - Binding aufgerufen, komischer Weise jedoch nicht beim DisplayMemberBinding der GridView...

Ferner habe ich auch schon probiert, die ObserveableCollection einfach nur mit Strings zu befüllen - klappt auch nicht.

Jemand eine Idee?
Danke & LG

MfG Robin

M
177 Beiträge seit 2009
vor 6 Jahren

Den DataContext bei jedem Control einzeln zu setzen ist die falsche Herangehensweise.

Die ListBox, ListView haben dafür z.B. die DependencyPropertie (DP) ItemsSource vorgesehen.

Darauf bindest du dann deine Liste welche du im ViewModel hälst.