我的一般问题是,正如标题所述,最好是在 ViewModel 构建期间加载数据,还是之后通过某些 Loaded 事件处理加载数据?

我猜答案是在通过一些 Loaded 事件处理构建之后,但我想知道 ViewModel 和 View 之间如何最清晰地协调?

以下是有关我的情况以及我试图解决的特定问题的更多详细信息:

我正在使用 MVVM Light 框架以及 Unity for DI。我有一些嵌套视图,每个视图都绑定到相应的 ViewModel。ViewModel 通过 Laurent Bugnion 已放入 MVVM Light 中的 ViewModelLocator 思想绑定到每个视图的根控件 DataContext。这允许通过静态资源查找 ViewModel 并通过依赖注入框架(在本例中为 Unity)控制 ViewModel 的生命周期。它还允许 Expression Blend 查看有关 ViewModel 的所有内容以及如何绑定它们。

所以无论如何,我有一个父视图,它有一个 ComboBox 数据绑定到其 ViewModel 中的 ObservableCollection。ComboBox 的 SelectedItem 也绑定(双向)到 ViewModel 上的属性。当ComboBox的选择发生变化时,这是为了触发其他视图和子视图中的更新。目前,我正在通过 MVVM Light 中的消息系统来完成此任务。当您在组合框中选择不同的项目时,这一切都工作得很好并且符合预期。

然而,ViewModel 在构造期间通过一系列初始化方法调用获取其数据。如果我想控制 ComboBox 的初始 SelectedItem 是什么,这似乎只是一个问题。使用 MVVM Light 的消息传递系统,我目前已将其设置为 ViewModel 的 SelectedItem 属性的设置器是广播更新的设置器,而其他感兴趣的 ViewModel 在其构造函数中注册消息。看来我目前正在尝试在构造时通过 ViewModel 设置 SelectedItem,这还不允许构造和注册子 ViewModel。

在 ViewModel 中协调 SelectedItem 的数据加载和初始设置的最简洁方法是什么?我真的想坚持在视图的代码隐藏中尽可能少地添加合理的内容。我想我只需要一种方法让 ViewModel 知道东西何时加载,然后它可以继续加载数据并完成设置阶段。

预先感谢您的回复。

有帮助吗?

解决方案

对于事件,您应该在MVVM Light Toolkit中使用EventTocommand。使用此功能,您可以将任何UI元素的任何事件绑定到中继命令。查看他有关事件的文章

http://blog.galasoft.ch/archive/2009/11/11/05/mvvm-light-toolkit-v3-v3-v3-alpha-2--2--2--------------------------------------------------------------------------------------------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....;
        });
    }

}

其他提示

以下解决方案类似于已经提供和接受的解决方案,但是它不使用视图模型中的命令来加载数据,而是使用“正常方法”。我认为命令更适合用户操作(命令可以在运行时可用并且无法使用),这就是为什么使用常规方法调用,也可以通过在视图中设置交互触发器的原因。

我建议这样做:创建一个视图模型类。通过在视图的XAML中实例化视图模型类,通过在视图的XAML中创建它 DataContext 财产。

实施一种将数据加载到视图模型中的方法,例如 LoadData。设置视图,以便在视图加载时调用此方法。这是通过视图中的交互触发器来完成的,该触发器在视图模型中链接到方法(引用“ Microsoft.expression.interactions”和“ System.Windows.Intractivity”):

查看(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 方法异步也是如此,但这在每种情况下都不需要。

顺便说一句,通常我不会将数据加载到视图模型的构造函数中。在上面的示例中,当设计师显示您的视图时,请调用视图模型的(参数少)构造函数。在显示您的视图时,在这里做复杂的事情可能会导致设计人员的错误(出于相同的原因,我不会在视图构造函数中制作复杂的东西)。

在视图模型中的某些方案中,构造函数甚至可能在运行时引起问题,当视图模型构造仪执行时,设置视图模型的属性,该属性绑定到视图中的元素,而视图对象并未完全完成创建。

那好吧。 :-)

您可以使用行为可以绑定到ViewModel中的方法。

这是一个可以帮助您的链接。http://expressionblend.codeplex.com/

我决定将 XAML 以声明方式绑定到 View 代码隐藏上的 Loaded 事件处理程序,而该处理程序又通过 View 的根元素 UserControl DataContext 调用 ViewModel 对象上的方法。

这是一个相当简单、直接、干净的解决方案。我想我希望有一种方法将 Loaded 事件绑定到 ViewModel 对象,就像使用 XAML 中的 ICommand 一样的声明方式。

我可能已经给了克林格官方答案信用,但他对我的问题发表了评论,而不是答案。所以我至少对他的评论进行了一番评论。

在处理父窗口和子窗口之间的消息时,我也遇到了同样的问题。只需更改在ViewModeLocator类中创建视图模型的顺序即可。确保在发送消息的视图模型之前创建所有取决于消息的视图模型。

例如,在您的ViewModeLocator类的构造函数中:

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

    if (s_messageSenderVm == null)
    {
        s_messageSenderVm = new MessageSenderVM();
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top