Данные MVVM нагрузки во время или после конструкции ViewModel?
-
22-09-2019 - |
Вопрос
Мой общий вопрос, как утверждает заголовок, лучше ли загружать данные во время конструкции 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 в
Загрузите образец и посмотрите. Это великолепно. Тогда вам не понадобится код. Пример заключается в следующем:
<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();
}
}