mkm*_*ray 26 data-binding constructor onload viewmodel mvvm-light
我的通用问题是标题所述,是否最好在ViewModel构造期间或之后通过某些Loaded事件处理加载数据?
我猜测答案是在构建之后通过一些Loaded事件处理,但我想知道ViewModel和View之间如何最清晰地协调?
这里有关于我的情况以及我想要解决的特定问题的更多细节:
我正在使用MVVM Light框架以及Unity for DI.我有一些嵌套的视图,每个视图都绑定到相应的ViewModel.ViewModel通过Laurent Bugnion放入MVVM Light的ViewModelLocator理念绑定到每个View的根控件DataContext.这允许通过静态资源查找ViewModels并通过依赖注入框架控制ViewModels的生命周期,在本例中为Unity.它还允许Expression Blend查看与ViewModel相关的所有内容以及如何绑定它们.
所以无论如何,我有一个父View,它有一个ComboBox数据绑定到其ViewModel中的ObservableCollection.ComboBox的SelectedItem也绑定(双向)到ViewModel上的属性.当ComboBox的选择发生变化时,这将触发其他视图和子视图中的更新.目前我正在通过MVVM Light中的Messaging系统实现这一目标.当您在ComboBox中选择不同的项目时,这一切都非常有效.
但是,ViewModel通过一系列初始化方法调用在构造期间获取其数据.如果我想控制ComboBox的初始SelectedItem是什么,这似乎只是一个问题.使用MVVM Light的消息传递系统,我目前已经设置了ViewModel的SelectedItem属性的setter是广播更新的设置者,另一个感兴趣的ViewModels注册用于它们的构造函数中的消息.看来我正在尝试在构造时通过ViewModel设置SelectedItem,这样就不允许构建子ViewModel并进行注册.
在ViewModel中协调数据加载和SelectedItem初始设置的最简洁方法是什么?我真的很想坚持在View的代码隐藏中尽可能少地使用合理的代码.我想我只需要一种方法让ViewModel知道什么时候有东西已经加载,然后它可以继续加载数据并完成设置阶段.
在此先感谢您的回复.
nab*_*rid 24
对于事件,您应该使用MVVM Light Toolkit中的EventToCommand.使用此方法,您可以将任何ui元素的任何事件绑定到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>
Run Code Online (Sandbox Code Playgroud)
并且视图模式可以是这样的
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";
});
}
}
Run Code Online (Sandbox Code Playgroud)
如果您希望视图模型具有EventArgs,您可以简单地将PassEventArgsToCommand设置为true:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Run Code Online (Sandbox Code Playgroud)
而视图模型就像
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....;
});
}
}
Run Code Online (Sandbox Code Playgroud)
下面的解决方案与已经提供并接受的解决方案类似,但它没有使用视图模型中的命令来加载数据,而是一种“正常方法”。我认为命令更适合用户操作(命令在运行时可用但不可用),这就是为什么使用常规方法调用,但也通过在视图中设置交互触发器。
我建议这样做:创建一个视图模型类。通过在DataContext属性中创建视图的 xaml 中的视图模型类来实例化它。
实现一个方法来在你的视图模型中加载数据,例如LoadData。设置视图,以便在视图加载时调用此方法。这是由视图中的交互触发器完成的,该触发器链接到视图模型中的方法(需要引用“Microsoft.Expression.Interactions”和“System.Windows.Interactivity”):
查看(xml):
<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>
Run Code Online (Sandbox Code Playgroud)
这将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
}
Run Code Online (Sandbox Code Playgroud)
如果存储库中的方法是异步方法,您也可以将LoadData方法设为异步,但并非在每种情况下都需要这样做。
顺便说一句,通常我不会在视图模型的构造函数中加载数据。在上面的示例中,当设计器显示您的视图时,会调用视图模型的(无参数)构造函数。在这里做复杂的事情会导致设计器在显示视图时出错(出于同样的原因,我不会在视图构造函数中做复杂的事情)。
在某些场景中,视图模型构造函数中的代码甚至会在运行时导致问题,当视图模型构造函数执行时,设置绑定到视图元素的视图模型的属性,而视图对象还没有完全创建完成。