MVVM ładuje dane podczas budowy ViewModel lub po jej zakończeniu?

Moje ogólne Pytanie brzmi, jak sam tytuł stwierdza, czy najlepiej jest ładować dane podczas budowy ViewModel, czy później poprzez załadowaną obsługę zdarzeń?

Zgaduję, że odpowiedź jest po budowie poprzez załadowane obsługi zdarzeń, ale zastanawiam się, jak to jest najbardziej czyste skoordynowane między ViewModel i View?

Oto więcej szczegółów na temat mojej sytuacji i konkretnego problemu, który próbuję rozwiązać:

Ja również używam frameworka MVVM Light jako jedność dla DI. Mam kilka zagnieżdżonych widoków, z których każdy jest powiązany z odpowiednim modelem widoku. ViewModels są powiązane z DataContext Root control każdego widoku poprzez pomysł ViewModelLocator, który Laurent Bugnion wprowadził do MVVM Light. Pozwala to na znajdowanie Viewmodeli za pomocą zasobu statycznego oraz na kontrolowanie czasu życia Viewmodeli za pomocą frameworka Injection zależności, w tym przypadku Unity. Pozwala również na Expression Blend, aby zobaczyć wszystko w odniesieniu do ViewModels i jak związać oni.

Tak czy inaczej, mam widok nadrzędny, który ma ComboBox databound do ObservableCollection w swoim ViewModel. SelectedItem pliku ComboBox jest również powiązany (dwukierunkowy) z właściwością w modelu widoku. Gdy zmieni się wybór Comboboxu, spowoduje to uruchomienie aktualizacji w innych widokach i podwidywaniach. Obecnie jestem osiągnięcie tego za pośrednictwem systemu wiadomości, który znajduje się w MVVM Light. To wszystko działa świetnie i zgodnie z oczekiwaniami, gdy wybierzesz różne przedmioty w ComboBox.

Jednakże, ViewModel pobiera swoje dane w czasie budowy poprzez serię inicjalizujących wywołań metod. Wydaje się, że jest to problem tylko, jeśli chcę kontrolować, co początkowy SelectedItem ComboBox jest. Korzystanie z systemu wiadomości MVVM Light, mam go obecnie skonfigurowany, gdzie setter właściwości SelectedItem ViewModel jest Tym, który transmituje aktualizację, a inne zainteresowane ViewModels rejestrują wiadomość w swoich konstruktorach. Wygląda na to, że jestem obecnie próbuję ustawić SelectedItem za pomocą ViewModel w czasie budowy, co nie pozwoliło na konstruowanie i rejestrację pod-ViewModels.

Jaki byłby najczystszy sposób koordynowania ładowania danych i początkowego ustawienia SelectedItem w modelu widoku? Naprawdę chcę trzymać się stawiania jak najmniej w kodzie widoku-tyle, ile jest rozsądne. Myślę, że potrzebuję tylko sposobu, aby ViewModel wiedział, kiedy coś się załadowało i że może dalej ładować dane i Zakończ fazę konfiguracji.

Z góry dzięki za odpowiedzi.

Author: mkmurray, 2010-03-28

5 answers

Dla zdarzeń należy użyć EventToCommand w MVVM Light Toolkit. Za pomocą tego można powiązać dowolne zdarzenie dowolnego elementu ui z relaycommand. Zobacz jego artykuł o EventToCommand na

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

Pobierz próbkę i zobacz. Świetnie. Nie będziesz potrzebował żadnego kodu. Przykład jest następujący:

<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>

A tryb widoku może wyglądać tak

 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";
            });
        }
    }

Jeśli chcesz, aby model widoku miał EventArgs, możesz w prosty sposób ustawić PassEventArgsToCommand na true:

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

I model widoku będzie podobny

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....;
        });
    }

}
 27
Author: nabeelfarid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 13:39:38

Poniższe rozwiązanie jest podobne do tego, które zostało już dostarczone i zaakceptowane, ale nie używa polecenia w modelu widoku do załadowania danych, ale "normalnej metody". Myślę, że polecenia są bardziej odpowiednie dla działań użytkownika (polecenia mogą być dostępne i nie są dostępne w czasie wykonywania), dlatego użyj zwykłego wywołania metody, ale także przez ustawienie wyzwalacza interakcji w widoku.

Proponuję to: Utwórz klasę modelu widoku. Utworzenie instancji klasy modelu widoku w xaml widoku przez tworzenie go wewnątrz właściwości DataContext.

Zaimplementuj metodę ładowania danych w modelu widoku, np. LoadData. Ustaw widok tak, aby ta metoda była wywoływana podczas ładowania widoku. Odbywa się to za pomocą wyzwalacza interakcji w widoku, który jest powiązany z metodą w modelu widoku (odniesienia do " Microsoft.Ekspresja.Interakcje " i " System.Okna.Interaktywność " są potrzebne):

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>   

Spowoduje wywołanie metody LoadData w ViewModel podczas wykonywania, gdy widok jest załadowany. Tutaj ładujesz swoje dane.

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
    }

Jeśli metoda w repozytorium jest metodą asynchroniczną, możesz również uczynić metodę LoadData asynchroniczną, ale nie jest to konieczne w każdym przypadku.

Przy okazji, generalnie nie ładowałbym danych w konstruktorze modelu widoku. W powyższym przykładzie konstruktor (parametr less) modelu widoku jest wywoływany, gdy projektant wyświetli Twój widok. Robienie skomplikowanych rzeczy tutaj może spowodować błędy w Projektancie podczas wyświetlania widoku (dla z tego samego powodu nie robiłbym skomplikowanych rzeczy w konstruktorze widoków).

W niektórych scenariuszach kod w konstruktorze modeli widoku może nawet powodować problemy podczas wykonywania, gdy konstruktory modeli widoku wykonują, ustawiają właściwości modelu widoku, które są powiązane z elementami w widoku, podczas gdy obiekt widoku nie jest w pełni gotowy do tworzenia.

 4
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-03 15:33:06

W porządku. :-)

Można powiązać z metodą w modelu widoku za pomocą zachowania.

Oto link, który Ci w tym pomoże. http://expressionblend.codeplex.com/

 2
Author: Klinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-29 16:08:50

Zdecydowałem się po prostu mieć deklaratywnie przypisany XAML do załadowanej obsługi zdarzeń w kodzie widoku, która z kolei wywołała metodę w obiekcie ViewModel, poprzez główny element widoku UserControl DataContext.

To było dość proste, proste i czyste rozwiązanie. Chyba miałem nadzieję, że uda mi się powiązać załadowane Zdarzenie z obiektem ViewModel w taki sam deklaratywny sposób, jaki można zastosować w ICommands w XAML.

Mogłem dać klingerowi oficjalną odpowiedź kredyt, ale zamieścił komentarz do mojego pytania, a nie ODPOWIEDŹ. Więc przynajmniej dałem mu jeden komentarz.

 1
Author: mkmurray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-28 20:54:41

Miałem ten sam problem, gdy miałem do czynienia z wiadomościami między oknem rodzica a oknem potomka. Wystarczy zmienić kolejność tworzenia modeli widoku w klasie ViewModelLocator. Upewnij się, że wszystkie modele widoku zależne od wiadomości są tworzone przed modelem widoku, który wysyła wiadomość.

Na przykład, w Twoim widoku konstruktor klasy Modellocator:

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

    if (s_messageSenderVm == null)
    {
        s_messageSenderVm = new MessageSenderVM();
    }
}
 0
Author: bugged87,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-19 22:36:06