Подтвердить что ты не робот

MVVM загружает данные во время или после конструкции ViewModel?

Мой общий вопрос, как указано в заголовке, лучше всего загружать данные во время конструкции ViewModel или позже через некоторую обработку событий Loaded?

Я предполагаю, что ответ после построения через некоторую обработку событий Loaded, но мне интересно, как это наиболее четко согласовано между ViewModel и View?

Здесь более подробно о моей ситуации и конкретной проблеме, которую я пытаюсь решить:

Я использую структуру MVVM Light, а также Unity для DI. У меня есть несколько вложенных представлений, каждая связана с соответствующей ViewModel. ViewModels привязаны к каждому представлению корневого управления DataContext с помощью идеи ViewModelLocator, которую Лоран Бугнион внес в MVVM Light. Это позволяет находить ViewModels через статический ресурс и управлять временем жизни ViewModels через инфраструктуру Injection Dependency, в этом случае Unity. Он также позволяет Expression Blend видеть все в отношении ViewModels и как их связывать.

Так или иначе, у меня есть родительский вид, у которого есть привязка к ComboBox к ObservableCollection в его ViewModel. ComboBox SelectedItem также привязан (в двух направлениях) к свойству ViewModel. Когда выбор ComboBox изменяется, это значит инициировать обновления в других представлениях и представлениях. В настоящее время я выполняю это через систему обмена сообщениями, которая находится в MVVM Light. Все это отлично работает и, как ожидается, когда вы выбираете разные элементы в ComboBox.

Тем не менее, ViewModel получает свои данные во время построения через серию вызовов вызова инициализации. Кажется, это проблема только в том случае, если я хочу контролировать, что такое исходный SelectedItem из ComboBox. Используя MVVM Light messaging system, я в настоящее время настроил его, когда установщик свойства ViewModel SelectedItem является тем, кто передает обновление, а другие заинтересованные регистры ViewModels для сообщения в своих конструкторах. Похоже, что в настоящее время я пытаюсь установить SelectedItem через ViewModel во время построения, что не позволяет создавать и регистрировать суб-ViewModels.

Каким будет самый чистый способ координации загрузки данных и начальной настройки SelectedItem в ViewModel? Я действительно хочу придерживаться так мало, как разумно. Я думаю, мне просто нужно, чтобы ViewModel узнал, когда материал загружен, и что он может продолжить загрузку данных и завершить настройку.

Заранее благодарим за ваши ответы.

4b9b3361

Ответ 1

Для событий вы должны использовать EventToCommand в MVVM Light Toolkit. Используя это, вы можете связать любое событие любого элемента ui с командой relaycommand. Просмотрите его статью о EventToCommand в

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

Загрузите образец и посмотрите. Здорово. Тогда вам не понадобится код. Пример следующий:

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

и режим просмотра может быть таким:

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

если вы хотите, чтобы модель представления имела EventArgs, вы можете просто установить PassEventArgsToCommand в true:

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

и модель представления будет выглядеть как

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

}

Ответ 2

Хорошо, тогда.: -)

Вы можете привязываться к методу в ViewModel с помощью поведения.

Вот ссылка, которая поможет вам в этом. http://expressionblend.codeplex.com/

Ответ 3

Я решил, что XAML декларативно привязан к обработчику событий Loaded в коде кода View, который, в свою очередь, просто называется методом объекта ViewModel, через элемент root root UserControl DataContext.

Это было довольно простое, прямолинейное и чистое решение. Наверное, я надеялся на способ привязки события Loaded к объекту ViewModel тем же декларативным способом, который вы можете использовать с ICommands в XAML.

Возможно, я дал Клингеру официальный ответ, но он отправил комментарий к моему вопросу, а не ответ. Поэтому я, по крайней мере, дал ему один комментарий к его комментарию.

Ответ 4

Следующее решение похоже на уже предоставленное и принятое, но оно не использует команду в модели представления для загрузки данных, а "обычный метод". Я думаю, что команды более подходят для пользовательских действий (команды могут быть доступны и недоступны во время выполнения), поэтому использовать обычный вызов метода, но также путем установки триггера взаимодействия в представлении.

Я предлагаю следующее: Создайте класс модели представления. Создайте экземпляр класса модели представления в пределах xaml представления, создав его внутри свойства DataContext.

Реализовать метод загрузки данных в вашей модели представления, например. LoadData. Настройте представление, чтобы этот метод вызывался при загрузке представления. Это делается с помощью триггера взаимодействия в вашем представлении, связанного с методом в модели представления (необходимы ссылки на "Microsoft.Expression.Interactions" и "System.Windows.Interactivity" ):

Вид (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>   

Это вызовет метод LoadData в ViewModel во время выполнения при загрузке представления. Здесь вы загружаете свои данные.

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
    }

Если метод в репозитории является асинхронным методом, вы можете также сделать асинхронный метод LoadData, но это не нужно в каждом случае.

Кстати, вообще-то я не загружал данные в конструктор модели представления. В приведенном выше примере конструктор (параметр less) модели представления вызывается, когда дизайнер показывает ваше представление. Выполнение сложных вещей здесь может привести к ошибкам в дизайнере при показе вашего представления (по той же причине я не буду создавать сложные вещи в конструкторе представлений).

В некоторых сценариях код конструктора представлений может даже вызвать проблемы во время выполнения, когда выполняются конструкторы моделей представлений, задает свойства модели представления, привязанные к элементам в представлении, в то время как объект вида не полностью завершен.

Ответ 5

У меня была такая же проблема при работе с сообщениями между родительским окном и дочерним окном. Просто измените порядок, в котором ваши модели просмотра создаются в вашем классе ViewModelLocator. Убедитесь, что все модели представлений, зависящие от сообщения, создаются перед моделью представления, которая отправляет сообщение.

Например, в конструкторе класса ViewModelLocator:

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

    if (s_messageSenderVm == null)
    {
        s_messageSenderVm = new MessageSenderVM();
    }
}