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

StackOverflow https://stackoverflow.com/questions/2531839

Вопрос

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

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

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

Я использую фреймворк MVVM Light, а также Unity для DI. У меня есть несколько вложенных представлений, каждый из которых связан с соответствующей ViewModel. ViewModels связаны с DataContext DataContext Control каждого представления с помощью идеи ViewModellocator, которую Laurent Bugnion включил в свет MVVM. Это позволяет найти ViewModels через статический ресурс и контролировать срок службы ViewModels через структуру впрыска в зависимости, в данном случае Unity. Это также позволяет смеси выражения видеть все в отношении ViewModels и как их связывать.

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

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

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

Заранее спасибо за ваши ответы.

Это было полезно?

Решение

Для событий вы должны использовать EventToCommand в MVVM Light Toolkit. Используя это, вы можете связать любое событие любого элемента пользовательского интерфейса с 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....;
        });
    }

}

Другие советы

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

Я предлагаю это: создать класс модели представления. Создать создание класса View Model в XAML представления, создав его внутри DataContext имущество.

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

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>   

Это позвонит 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 Метод также асинхронно, но в каждом случае это не требуется.

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

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

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

Вы можете связаться с методом в ViewModel, используя поведение.

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

Я решил просто сделать XAML, связанный с загруженным обработчиком событий на коде-коде, который, в свою очередь, только что назвал метод на объекте ViewModel, через DataContext userControl.

Это было довольно простое, простое и чистое решение. Я предполагаю, что я надеялся на способ связать загруженное событие с объектом ViewModel таким же декларативным, что и с Icommands в XAML.

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

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

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

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

    if (s_messageSenderVm == null)
    {
        s_messageSenderVm = new MessageSenderVM();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top