WPF DataContext nicht über Code-Behind?

1 Antwort

Den DataContext kannst du bereits in XAML via Trigger ändern.

Auszug aus dem MainWindow:

<Window.DataContext>
  <local:ViewModel />
</Window.DataContext>
<StackPanel>
  <ToggleButton Content="Toggle" x:Name="Toggle" />
  <ContentControl>
    <ContentControl.Content>
      <Label Content="{Binding Greeting}" />
    </ContentControl.Content>
    <ContentControl.Style>
      <Style TargetType="{x:Type ContentControl}">
        <Style.Triggers>
          <DataTrigger Binding="{Binding ElementName=Toggle, Path=IsChecked}" Value="True">
            <Setter Property="DataContext">
              <Setter.Value>
                <local:ViewModel Greeting="Good morning!" />
              </Setter.Value>
            </Setter>
          </DataTrigger>
          <DataTrigger Binding="{Binding ElementName=Toggle, Path=IsChecked}" Value="False">
            <Setter Property="DataContext">
              <Setter.Value>
                <local:ViewModel Greeting="Good afternoon!" />
              </Setter.Value>
            </Setter>
          </DataTrigger>
        </Style.Triggers>
      </Style>
    </ContentControl.Style>
  </ContentControl>
</StackPanel>

ViewModel:

public class ViewModel : INotifyPropertyChanged
{
  private string _greeting;

  public ViewModel()
  {
    Greeting = "Hello world!";
  }

  public string Greeting
  {
    get => _greeting;
    set
    {
      if (_greeting == value)
      {
        return;
      }

      _greeting = value;
      NotifyPropertyChanged(nameof(Greeting));
    }
  }

  // implement NotifyPropertyChanged interface  ...
}

Ich habe jetzt nur zur Demonstration einfach ein Property (Greeting) hineingeworfen. Für die Implementation des Interfaces schau hier.

Wenn der ToggleButton seinen Zustand ändert, setzt der Trigger einen anderen DataContext. In diesem Fall immer auf eine neue ViewModel-Instanz mit anderem Wert für Greeting.

Im Folgenden zeige ich noch ein Beispiel, wie sich via Commands die View / das DataTemplate austauschen lässt. Ich beginne diesmal mit der Implementation des ViewModel. Um es kurz zu halten, reduziere ich das Beispiel auf zwei Views.

public class ViewModel : ObservableViewModel
{
  private readonly ObservableViewModel _personViewModel = new PersonViewModel();

  private readonly ObservableViewModel _tableViewModel = new FurnitureViewModel();

  private ObservableViewModel _currentView;

  public ViewModel()
  {
    CurrentView = _personViewModel;

    SwitchViewCommand = new RelayCommand<string>(
      ChangeView,
      t => !string.IsNullOrEmpty(t));    
  }

  public ObservableViewModel CurrentView
  {
    get => _currentView;
    set
    {
      if (_currentView == value)
      {
        return;
      }

      _currentView = value;
      NotifyPropertyChanged(nameof(CurrentView));
    }
  }

  public ICommand SwitchViewCommand { get; }

  private void ChangeView(string type)
  {
    switch (type.ToLower())
    {
      case "person":
        CurrentView = _personViewModel;
        break;
      case "table":
        CurrentView = _tableViewModel;
        break;
    }
  }
}

Die RelayCommand-Klasse führe ich nicht extra auf, die kann auch bspw. so aussehen. In der Microsoft-Dokumentation müsste ansonsten ebenso noch eine Beispielimplementation zu finden sein.

Die ObservableViewModel-Klasse ist eine abstrakte Klasse, die das INotifyPropertyChanged-Interface implementiert.

Die beiden ViewModel-Klassen, zwischen denen gewechselt werden soll, erben von der abstrakten Klasse und implementieren zur Veranschaulichung jeweils zwei Properties: Name und Alter für die Person, Höhe und Breite für den Tisch.

Im Hauptfenster wird einmal der DataContext gesetzt (vergleiche mit den ersten drei Zeilen meines ersten Snippets) sowie ein ContentControl-Element und zwei Buttons.

<ContentControl Content="{Binding CurrentView}" />
<Button Command="{Binding SwitchViewCommand}" CommandParameter="person" Content="Show person" />
<Button Command="{Binding SwitchViewCommand}" CommandParameter="table" Content="Show table" />

Die CommandParameter werden später in SwitchView verarbeitet.

Wichtig ist zudem, die Views anzulegen (Furniture.xaml, Person.xaml) und im XAML des Hauptfensters in den Ressourcen mit den ViewModel-Klassen zu verknüpfen.

<Window.Resources>
  <DataTemplate DataType="{x:Type local:FurnitureViewModel}">
    <local:Furniture />
  </DataTemplate>
  <DataTemplate DataType="{x:Type local:PersonViewModel}">
    <local:Person />
  </DataTemplate>
</Window.Resources>

Die Views könnten (verkürzt) so aussehen:

<!-- Person.xaml -->
<UserControl>
  <StackPanel>
    <Label Content="{Binding Name}" />
    <Label Content="{Binding Age}" />
  </StackPanel>
</UserControl>

und

<!-- Table.xaml -->
<UserControl>
  <StackPanel>
    <Label Content="{Binding Height}" />
    <Label Content="{Binding Width}" />
  </StackPanel>
</UserControl>

Auf diese Weise bleiben die Klassen des View noch immer vom ViewModel getrennt bzw. man manipuliert im ViewModel zwar den Zustand der grafischen Oberfläche, muss aber dafür nicht auf explizite Komponenten dessen zugreifen, sondern kann über Bindings agieren.

Vielen dank.
Werde es später ausprobieren !

Top !!

0

Hey,

danke nochmal für deine Ausführliche Antwort, besonders das mit Commands sehr sehr gut.

Frage hierzu noch:

Wenn ich auf meiner View beispiel eine Person erstelle (Name, Vorname, Geburtsdatum), werden die Daten dann ja an das jeweilige ViewModel weitergegeben.

Muss ich nun die Daten im ViewModel in einer Methode Verarbeiten, oder erstelle ich dazu eine neue Klasse wo ich meine Methoden Definiere und leite die Daten von ViewModel in die Klasse weiter ?

Beispiel:

Situation:
Ich habe eine Context Klasse mit EF (EntityFramework).

Methode 1:
Daten kommen aus der View ins ViewModel, und die Daten werden dann im ViewModel in die Datenbank gespeichert.

Methode 2:
Daten kommen aus der View ins ViewModel, dort werden die dann weitergegeben in eine andere Klasse, die andere Klasse beinhaltet verschiedene Methoden, unter anderem zum speichern von Daten.

0
@Biggi1234567

Für das Speichern und Laden der Daten kannst du eine eigene Klasse schreiben. Diese könnte bspw. DatabaseHandler o.ä. heißen. So wird eine konkrete Aufgabe (Datenbankkommunikation) erst einmal ausgelagert / von anderen Bereichen abgetrennt.

Das ViewModel könnte nun eine Instanz dieser Handler-Klasse besitzen und mit dieser auf das Model wirken. Somit wäre die bessere Wahl also Methode 2.

Aber da du vom Entity Framework (EF) redest, kannst du vermutlich sogar einigen Aufwand einsparen, in dem du eine Bindung des Models über POCO einrichtest. Grob formuliert synchronisiert das Framework die Daten zwischen deinem Model und der Datenbank dann automatisch. Ein Tutorial dazu findest du hier: https://docs.microsoft.com/en-us/ef/ef6/fundamentals/databinding/wpf

Bezüglich EF + MVVM habe ich jetzt auf die Schnelle nicht so viele Tutorials gefunden, zudem habe ich sie nur überflogen. Aber ich denke, du kannst es mit diesem versuchen: https://erazerbrecht.wordpress.com/2015/10/13/mvvm-entityframework/

0