Frage

Meine generische Frage ist, wie der Titel sagt, ist es am besten, Daten während der ViewModel -Konstruktion oder anschließend durch eine geladene Ereignishandhabung zu laden?

Ich vermute, dass die Antwort nach dem Bau durch eine geladene Ereignisbehandlung ist, aber ich frage mich, wie das zwischen ViewModel und View am saubersten koordiniert wird.

Hier finden Sie weitere Details zu meiner Situation und dem besonderen Problem, das ich zu lösen versuche:

Ich verwende das MVVM -Lichtrahmen sowie die Einheit für DI. Ich habe einige verschachtelte Ansichten, die jeweils an ein entsprechendes ViewModel gebunden sind. Die ViewModels sind über die ViewModelloCator -Idee an die Root Control Datacontext der einzelnen Ansicht gebunden, dass Laurent Bugnion in MVVM -Licht eingebracht hat. Dies ermöglicht das Finden von ViewModels über eine statische Ressource und die Steuerung der Lebensdauer von ViewModels über einen Abhängigkeitsinjektionsrahmen, in diesem Fall Einheit. Es ermöglicht auch die Expressionsmischung, um alles in Bezug auf ViewModels zu sehen und sie zu binden.

Also habe ich eine übergeordnete Ansicht, die eine Combobox -Datenbank für eine Beobachtung in seinem ViewModel hat. Das SelectedItem des Combobox ist ebenfalls an eine Eigenschaft auf dem ViewModel gebunden. Wenn sich die Auswahl der Combobox ändert, dient dies dazu, Aktualisierungen in anderen Ansichten und Unteransichten auszulösen. Derzeit erreichen ich dies über das Messaging -System, das in MVVM -Licht zu finden ist. Dies funktioniert alles großartig und erwartet, wenn Sie verschiedene Elemente im Combobox auswählen.

Das ViewModel erhält jedoch seine Daten während der Bauzeit über eine Reihe von Initialisierungsmethodenaufrufen. Dies scheint nur ein Problem zu sein, wenn ich steuern möchte, was der anfängliche SelectedItem des Combobox ist. Mit dem Messaging -System von MVVM Light habe ich es derzeit eingerichtet, wo der Setter der SelectedItem -Eigenschaft des ViewModel diejenige ist, die das Update ausstrahlt, und die anderen interessierten ViewModels registrieren sich für die Nachricht in ihren Konstruktoren. Es scheint, dass ich derzeit versuche, das SelectedItem über das ViewModel zur Bauzeit festzulegen, wodurch SubviewModels noch nicht konstruiert und registriert werden konnten.

Was wäre der sauberste Weg, um die Datenlast und die erste Einstellung von SelectedItem im ViewModel zu koordinieren? Ich möchte mich wirklich daran halten, so wenig in die Code-Behind der Ansicht zu stecken, wie es vernünftig ist. Ich denke, ich brauche nur eine Möglichkeit, dass das ViewModel wissen kann, wann Stoffe geladen wurden, und dass es dann weiterhin die Daten laden und die Setup -Phase abschließen kann.

Vielen Dank im Voraus für Ihre Antworten.

War es hilfreich?

Lösung

Für Veranstaltungen sollten Sie das EventTocommand im MVVM -Licht -Toolkit verwenden. Mit dieser Weise können Sie jedes Ereignis eines UI -Elements an RelayCommand binden. Schauen Sie sich seinen Artikel über EventTocommand unter an

http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx

Laden Sie das Beispiel herunter und schauen Sie sich an. Es ist großartig. Sie brauchen dann kein Codebehind. Ein Beispiel ist wie folgt:

<Page x:Class="cubic.cats.Wpf.Views.SplashScreenView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
    Title="SplashScreenPage">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <cmd:EventToCommand Command="{Binding LoadedCommand}" />
        </i:EventTrigger>        
    </i:Interaction.Triggers>

    <Grid>
        <Label Content="This is test page" />
    </Grid>
</Page>

Und der Ansichtsmodus könnte so sein

 public class SplashScreenViewModel : ViewModelBase
    {
        public RelayCommand LoadedCommand
        {
            get;
            private set;
        }

        /// <summary>
        /// Initializes a new instance of the SplashScreenViewModel class.
        /// </summary>
        public SplashScreenViewModel()
        {
            LoadedCommand = new RelayCommand(() =>
            {
                string a = "put a break point here to see that it gets called after the view as been loaded";
            });
        }
    }

Wenn Sie möchten, dass das Ansichtsmodell die EventArgs hat, können Sie PasseventArgstocommand einfach auf wahr einstellen:

<i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding LoadedCommand}" />
  </i:EventTrigger>        
</i:Interaction.Triggers>

und das Ansichtsmodell wird wie sein

public class SplashScreenViewModel : ViewModelBase
{
    public RelayCommand<MouseEventArgs> LoadedCommand
    {
        get;
        private set;
    }

    /// <summary>
    /// Initializes a new instance of the SplashScreenViewModel class.
    /// </summary>
    public SplashScreenViewModel()
    {
        LoadedCommand = new RelayCommand<MouseEventArgs>(e =>
        {
            var a = e.WhateverParameters....;
        });
    }

}

Andere Tipps

Die folgende Lösung ähnelt der bereits bereitgestellten und akzeptierten Lösung, verwendet jedoch keinen Befehl im Ansichtsmodell, um die Daten zu laden, sondern eine "normale Methode". Ich denke, die Befehle eignen sich besser für Benutzeraktionen (Befehle können zur Laufzeit verfügbar sein und nicht verfügbar). Deshalb verwenden Sie einen regulären Methodenaufruf, aber auch durch Einstellen eines Interaktionsauslösers in der Ansicht.

Ich schlage Folgendes vor: Erstellen Sie eine Ansichtsmodellklasse. Instantieren Sie die Ansichtsmodellklasse innerhalb des XAML der Ansicht, indem Sie sie in der erstellen DataContext Eigentum.

Implementieren Sie eine Methode, um die Daten in Ihrem Ansichtsmodell zu laden, z. B. LoadData. Richten Sie die Ansicht ein, damit diese Methode aufgerufen wird, wenn die Ansicht geladen wird. Dies geschieht durch einen Interaktionsauslöser in Ihrer Ansicht, der mit der Methode im Ansichtsmodell verknüpft ist (Verweise auf "microsoft.expression.Interactions" und "System.Windows.Interactivity" sind erforderlich):

View (xaml):

<Window x:Class="MyWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test" 
    xmlns:viewModel="clr-namespace:ViewModels"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"            
    >
<Window.DataContext>
    <viewModel:ExampleViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/>
    </i:EventTrigger>
</i:Interaction.Triggers>   

Dies wird das anrufen LoadData Methode im ViewModel zur Laufzeit, wenn die Ansicht geladen wird. Hier laden Sie Ihre Daten.

public class ExampleViewModel
{
    /// <summary>
    /// Constructor.
    /// </summary>
    public ExampleViewModel()
    {
        // Do NOT do complex stuff here
    }


    public void LoadData()
    {
        // Make a call to the repository class here
        // to set properties of your view model
    }

Wenn die Methode im Repository eine asynchrische Methode ist, können Sie das erstellen LoadData Methode Async auch, aber dies wird jeweils nicht benötigt.

Übrigens würde ich im Allgemeinen keine Daten im Konstruktor des View -Modells laden. Im obigen Beispiel wird der Konstruktor (Parameter weniger) des Ansichtsmodells aufgerufen, wenn der Designer Ihre Ansicht zeigt. Wenn Sie hier komplexe Dinge tun, können Sie beim Designer Fehler verursachen, wenn Sie Ihre Ansicht zeigen (aus dem gleichen Grund würde ich nicht komplexe Dinge im Ansichtenkonstruktor machen).

In einigen Szenarien -Code im Ansichtsmodelle kann der Konstruktor sogar Probleme zur Laufzeit verursachen. Wenn die Konstruktoren der Ansichtsmodelle ausgeführt werden, setzen Sie Eigenschaften des Ansichtsmodells, die an Elemente in der Ansicht gebunden sind, während das Ansichtsobjekt nicht vollständig erstellt wird.

OK dann. :-)

Sie können mit einem Verhalten an eine Methode im ViewModel binden.

Hier ist ein Link, der Ihnen dabei hilft.http://expressionblend.codeplex.com/

Ich habe mich entschlossen, nur die XAML an einen geladenen Ereignishandler in der Code-Behind der Ansicht gebunden zu lassen, was wiederum über das Root Element Usercontrol-Daten für das ViewModel-Objekt nur eine Methode als Methode bezeichnet.

Es war eine ziemlich einfache, direkte und saubere Lösung. Ich glaube, ich hatte gehofft, dass eine Möglichkeit, das geladene Ereignis an das ViewModel -Objekt zu binden, auf die gleiche deklarative Weise mit icommands in der xaml zu binden.

Ich habe Klinger vielleicht den offiziellen Antwortkredit gegeben, aber er hat einen Kommentar zu meiner Frage gepostet und keine Antwort. Also gab ich ihm zumindest einen One-Up über seinen Kommentar.

Ich hatte das gleiche Problem, wenn ich mit Nachrichten zwischen einem übergeordneten Fenster und einem untergeordneten Fenster zu tun hatte. Ändern Sie einfach die Reihenfolge, in der Ihre Ansichtsmodelle in Ihrer ViewModelloCator -Klasse erstellt werden. Stellen Sie sicher, dass alle Ansichtsmodelle, die von einer Nachricht abhängig sind, vor dem Ansichtsmodell erstellt werden, das die Nachricht sendet.

Zum Beispiel in Ihrem Konstruktor der ViewModellocator -Klasse:

public ViewModelLocator()
{
    if (s_messageReceiverVm == null)
    {
        s_messageReceiverVm = new MessageReceiverVM();
    }

    if (s_messageSenderVm == null)
    {
        s_messageSenderVm = new MessageSenderVM();
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top