Laden...

MVVM: ViewModel aus anderem ViewModel aktualisieren

Erstellt von JKruse vor 14 Jahren Letzter Beitrag vor 14 Jahren 5.508 Views
JKruse Themenstarter:in
60 Beiträge seit 2007
vor 14 Jahren
MVVM: ViewModel aus anderem ViewModel aktualisieren

Hallo,

ich habe mein Problem zwar hier (dotnet-gui.com) vor einiger Zeit schon mal gepostet, leider aber noch keine Lösung gefunden. Vielleicht könnt ihr mir weiterhelfen.

Um das Layout meiner Anwendung steuern zu können, habe ich in meiner ViewModelBase-Klasse u.a. eine Eigenschaft mit dem Namen "Theme", die mir den aktuellen Wert in eine Konfigurationsdatei schreibt.

Auszug:

public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
  Configuration configuration = new Configuration();

  protected ViewModelBase()
  {
  }

  public virtual string Theme
  {
    get
    {
      return this.configuration.GetValue(Constants.Configuration.Theme);
    }
    set
    {
      if (this.configuration.GetValue(Constants.Configuration.Theme) != value)
      {
        this.configuration.SetValue(Constants.Configuration.Theme, value);
        this.OnPropertyChanged(Constants.ViewModelBase.ThemeProperty);
      }
    }
  }
}

In meiner ConfigurationView habe ich folgenden Code zum Ändern des Themes:

<ComboBox ItemsSource="{Binding Path=Themes, Mode=OneTime}" SelectedValue="{Binding Path=Theme, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ComboBox>

Die ItemsSource ist ein string[] mit den Namen der Themes.

Die Darstellung des Themes in den einzelnen Views erfolgt über folgende Zeile:

<UserControl x:Class="..." 
  themes:ThemeManager.Theme="{Binding Path=Theme}">
</UserControl>

Wenn ich einen anderen Theme aus der Liste auswähle, wird sofort das Layout des Dialogs geändert.

Wie kann ich jetzt aber erreichen, dass auch mein Hauptfenster geändert wird?
Auch mein Hauptfenster erbt von meiner ViewModelBase-Klasse, daher bin ich davon ausgegangen, dass mein Hauptfenster durch das OnPropertyChanged über den Wechsel des Themes informiert wird...

Der Aufruf des Dialogs (ConfigurationViewModel) erfolgt über folgenden Code:

void ShowConfiguration()
{
  try
  {
    this.ShowDialog(new ConfigurationViewModel());
  }
  catch (Exception e)
  {
    this.ErrorDescription = e.Message;
    this.ErrorImageSource = Constants.Exceptions.Type.Error;
  }
}

void ShowDialog(WorkspaceViewModel viewModel)
{
  Dialog dialog = new Dialog(viewModel);
  viewModel.Dialog = dialog;
  dialog.Title = viewModel.DisplayName;
  dialog.WindowStyle = WindowStyle.ToolWindow;
  dialog.ShowInTaskbar = false;
  dialog.Height = viewModel.ViewHeight + 24;
  dialog.Width = viewModel.ViewWidth + 6;
  dialog.ResizeMode = ResizeMode.NoResize;
  dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
  dialog.Topmost = true;
  dialog.ShowDialog();
}

Braucht ihr noch weiteren Quellcode?

Viele Grüße
Jens

104 Beiträge seit 2004
vor 14 Jahren

Hallo JKruse,

vermutlich verwendest du für den Dialog und das Hauptfenster 2 verscheidene ViewModel-Instanzen.

Wenn dem so ist, müsstest du die ausgewählte Theme-Property des HauptFensterViewModels nochmal setzt damit auch für diese Instanz das OnPropertyChangedEvent geschmissen wird.

Gruß

Schaut mal im IRC vorbei:
Server: irc.euirc.net
Channel: #C#

JKruse Themenstarter:in
60 Beiträge seit 2007
vor 14 Jahren

Guten Morgen,

Über

new ConfiguratiopnViewModel()

erzeuge ich ja die neue Instanz.
Gibt es evtl. einen eleganteren Weg, ohne dass ich die Eigenschaft nochmal setzen muss?

Viele Grüße
Jens

JKruse Themenstarter:in
60 Beiträge seit 2007
vor 14 Jahren

Guten Morgen,

ich muss den Thread noch mal zum Leben erwecken, weil ich noch eine Frage habe, die in die selbe Richtung geht. Bitte melden, falls es in einen neuen Thread soll.

Gehen wir mal nach wie vor von den beiden oben genannten Views aus (MainWindowViewModel und ConfigurationViewModel).
Ich habe in meiner MainWindowView einen Bereich, wo ich Fehlermeldungen anzeigen möchte, die in meiner Anwendung auftreten. In diesem Bereich möchte ich auch Fehlermeldungen anzeigen, wenn in einem anderen ViewModel Fehler auftreten.

Meine Frage jetzt, wie kann ich am einfachsten die Fehlermeldungen, die in meinen anderen ViewModels auftreten in mein MainWindowViewModel durchreichen?

Viele Grüße
Jens

5.742 Beiträge seit 2007
vor 14 Jahren

Hallo JKruse,

Bitte melden, falls es in einen neuen Thread soll.

Ich melde mich. 😉
Jetzt ist es aber sowieso schon zu spät - abtrennen kann das jetzt nur ein Moderator.

Um ein späteres Abtrennen zu erleichtern, einfach zwei verschiedene Posts zu den verschiedenen "Threads":


Hallo JKruse,

[QUOTE]Um das Layout meiner Anwendung steuern zu können, habe ich in meiner ViewModelBase-Klasse u.a. eine Eigenschaft mit dem Namen "Theme", die mir den aktuellen Wert in eine Konfigurationsdatei schreibt.[/QUOTE]
Wenn ich deine Absicht richtig verstehe: Du möchtest verschiedene Themes für deine Anwendung bereitstellen.
Das würde ich aber anders lösen: Speichere deine Styles, etc. als globale Resourcen und gib ihnen Namen in der Form [Window/ControlName].Control (oder wie auch immer). Wichtig ist nur, dass sie eindeutig sind.
Dann kannst du das Theme sehr angenehm wechseln, wenn du App.Resources ein anderes [i]ResourceDictionary[/i] zuweist.

Hallo JKruse,

[QUOTE]Ich habe in meiner MainWindowView einen Bereich, wo ich Fehlermeldungen anzeigen möchte, die in meiner Anwendung auftreten. In diesem Bereich möchte ich auch Fehlermeldungen anzeigen, wenn in einem anderen ViewModel Fehler auftreten.[/QUOTE]
Spontan sehe ich hier zwei Möglichkeiten:
[list]
[*]Der "direkte Weg": [i]ConfigurationViewModel[/i] stellt ein Event bereit, das [i]MainViewModel[/i] abboniert
[*]Der "indirekte Weg" über eine weitere Vermittlerklasse, der alle ViewModels ihre Fehler melden und die das [i]MainViewModel[/i] irgendwie informiert.
Hier lassen sich auch einige "Spielereien" mit DepenedencyInjection usw. treiben.
[/list] 
JKruse Themenstarter:in
60 Beiträge seit 2007
vor 14 Jahren

Hallo winsharp93,

vielen Dank für deine beiden Antworten.

Hallo JKruse,

Zitat von JKruse:
Um das Layout meiner Anwendung steuern zu können, habe ich in meiner ViewModelBase-Klasse u.a. eine Eigenschaft mit dem Namen "Theme", die mir den aktuellen Wert in eine Konfigurationsdatei schreibt.

Wenn ich deine Absicht richtig verstehe: Du möchtest verschiedene Themes für deine Anwendung bereitstellen.
Das würde ich aber anders lösen: Speichere deine Styles, etc. als globale Resourcen und gib ihnen Namen in der Form [Window/ControlName].Control (oder wie auch immer). Wichtig ist nur, dass sie eindeutig sind.
Dann kannst du das Theme sehr angenehm wechseln, wenn du App.Resources ein anderes ResourceDictionary zuweist.

Ja, hast du richtig verstanden. Was die Theme-Steuerung angeht, habe ich mich an dem Codeplex-Beispiel WPF Themes orientiert. Ich schaue mir deinen Ansatz aber auch noch mal an.

Hallo JKruse,

Zitat von JKruse:
Ich habe in meiner MainWindowView einen Bereich, wo ich Fehlermeldungen anzeigen möchte, die in meiner Anwendung auftreten. In diesem Bereich möchte ich auch Fehlermeldungen anzeigen, wenn in einem anderen ViewModel Fehler auftreten.

Spontan sehe ich hier zwei Möglichkeiten:
•Der "direkte Weg": ConfigurationViewModel stellt ein Event bereit, das MainViewModel abboniert

•Der "indirekte Weg" über eine weitere Vermittlerklasse, der alle ViewModels ihre Fehler melden und die das MainViewModel irgendwie informiert.
Hier lassen sich auch einige "Spielereien" mit DepenedencyInjection usw. treiben.

Was mir hier noch nicht so klar ist, wie komme ich bei dem Event ("direkter Weg") an die Fehlermeldung? Bisher habe ich in meinem MainWindowViewModel eine Eigenschaft "ErrorDescription", die kann ich ja über das Event dann ansprechen.

Hast du evtl. für eine von beiden Wegen ("direkt" oder "indirekt") einen Lösungsansatz in Quellcodeform?

Viele Grüße
Jens

5.742 Beiträge seit 2007
vor 14 Jahren

Hast du evtl. für eine von beiden Wegen ("direkt" oder "indirekt") einen Lösungsansatz in Quellcodeform?

Na ja, der direkte Weg verwendet ein benutzerdefiniertes Event, wie es in [FAQ] Eigenen Event definieren / Information zu Events beschrieben wird.
In deiner Situation wäre eine eigene EventArgs Klasse mit dem Fehler passend.

Der indirekte Weg würde ein Interface benötigen, das eine Methode wie "ReportError" bereitstellen würde.
Das MainViewModel könnte dann eine Implementierung davon bei z.B. einem Dep-Inj. Container registrieren, welche die Meldungen dann letztendlich im Hauptfenster ausgibt.
Das ConfigurationViewModel könnte sich dann von dem Dep.-Inj. Container die Implementierung holen und den Fehler melden. Vorteil wäre, dass du das ganze wunderbar (auch ohne Neukompilierung) zu einer Protokollausgabe ändern könntest.
Codetechnisch sähe das dann in etwa so aus (abhängig vom tatsächlich verwendeten Framework):


container.Get<IErrorReporter>.ReportError("Fehler!!!");

JKruse Themenstarter:in
60 Beiträge seit 2007
vor 14 Jahren

Hallo winSharp93,

vielen Dank für den Link zu den FAQs.

Ich habe mir da jetzt mal etwas rausgesucht, was zumindest zum Test in einer Consolenanwendung super funktioniert. Ich baue das jetzt in Ruhe noch mal um.

Der zweite Weg hört sich gut an, momentan stehe ich aber etwas auf dem Schlauch und verstehe es noch nicht so ganz. Ich habe mich bisher auch noch nicht mit dem Thema "Dependency Injection" beschäftigt.
Kannst du mir hier evtl. auch noch den einen oder anderen Link nennen (außer google)?

Vielen Dank und viele Grüße!

Jens

5.742 Beiträge seit 2007
vor 14 Jahren

Kannst du mir hier evtl. auch noch den einen oder anderen Link nennen (außer google)?

Ja (der neuen Suchmaschine von MS sei dank) 😁 : Bing Suche nach Dependecy Injection.
Aber mal ganz im Ernst: Es sollten sich genügend Informationen dazu im Internet finden - ein wirklich "ideales" Tutorial, das ich uneingeschränkt empfehlen könnte, ist mir noch nicht begegnet.

DI gehört wirklich nicht zu den leichtesten Dingen.
Für den Anfang reicht vielleicht eine einfache Property des ConfigurationViewModel vom Typ IErrorReporter. Später - wenn tatsächlich Bedarf dazu bestehen sollte - kann man das dann vergleichsweise einfach umbauen.

JKruse Themenstarter:in
60 Beiträge seit 2007
vor 14 Jahren

Ok, dann nutze ich die Suche mal... 😉

Ich werde mich da mal in den nächsten Tagen ein wenig mehr mitbeschäftigen, und sonst weiß ich ja, wen ich fragen kann... 😉

Also, noch mal vielen Dank!!

Jens