ViewModel与UserControl视图之间的通信

3

我正在从事WPF MVVM项目。我正在努力解决MainWindow的视图模型与放置在MainWindow内的usercontrol的视图之间的通信问题。

所以我有:

  • 用户控件

  • 主窗口

  • 主窗口视图模型

我的用户控件非常简单:

<Grid MouseDown="UIElement_OnMouseDown">
    <Rectangle Fill="BlueViolet" />
</Grid>

使用代码-behind(当矩形被点击时触发事件并传递坐标):
public partial class FooUserControl : UserControl
{
    public FooUserControl()
    {
        InitializeComponent();
    }

    public event EventHandler<BarEventArgs> BarClick;
    private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        double x = e.GetPosition(this).X;
        double y = e.GetPosition(this).Y;
        string value_to_pass = "[" + x + "," + y + "]";

        BarEventArgs bar = new BarEventArgs() { Bar = 2, Foo = value_to_pass };
        BarClick?.Invoke(sender, bar);
    }
}

我的主窗口没有后台代码,只有xaml。如您所见,我通过命令将单击事件传递给MainWindowViewModel

<Window.DataContext>
    <viewModels:MainWindowViewModel />
</Window.DataContext>
<Grid>
    <local:FooUserControl>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="BarClick">
                <cmd:EventToCommand Command="{Binding ClickedCommand}" PassEventArgsToCommand="True" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </local:FooUserControl>
</Grid>

最后,我的MainWindowViewModel只有这个命令:

public class MainWindowViewModel : ObservableObject
{
    public ICommand ClickedCommand => new RelayCommand<BarEventArgs>(o => Clicked(o.Foo));
    private void Clicked(string a)
    {
        Debug.WriteLine("Clicked " + a);
    }
}

因此,通过命令从UserControl的视图向MainWindow的视图模型进行通信非常有效。但是,如何以相反的方式进行通信?从MainWindowViewModel到UserControl的视图?

你想要进行什么样的通信呢?你可以寻找双向绑定。 - Sudipta Kumar Sahoo
在您的UserControl表面上公开一个ICommand。在ViewModel中绑定它。现在,UserControl可以直接与您的视图模型通信。回传通信?在UserControl上公开所需属性。也许是一个点?或者可能是一只鞋?我不知道。现在,视图模型将其属性设置为所需颜色的鞋子,并通过更改通知,UserControl可以更新自身。您已经获得了MVVM批准的双向通信。 - user1228
你的 FooUserControl 是否应该在应用程序之间可重用(=UserControl),还是只针对你的应用程序特定(=View)?UserControls 没有 ViewModel(只提供依赖属性以绑定到一个),只有 Views 有特定的 viewmodels(UserView => UserViewModel)。 - Tseng
@Tseng 这就是我所拥有的。当我提到“ViewModel”时,我的意思是FooUserControl所在视图的视图模型。 - robertwojnar
这就是 INotifiyPropertyChangedINotifyCollectionChanged 接口的作用,它们告诉视图值已经改变,而你显然应该知道这一点,因为你实现了一个名为 ObservableObject 的类型,它听起来像是实现了 INotifyPropertyChanged(INPC)的类型。 - Tseng
@Tseng 谢谢。我现在更好地理解了。在 View 中,我可以查看 ViewModel 的某些属性,如果它们发生变化,就可以在 View 中执行一些动画。我只需要一点时间来分析 Mediator 设计模式和 MVVM Lights 的 Messenger。 - robertwojnar
2个回答

4

您的ViewModel不应直接访问Views。它们根本不关心Views。它们所做的就是提供属性以使数据可用。Views现在可以绑定到这些属性。

因此,从ViewModel到View的所有通信都通过绑定完成。当ViewModel需要告诉View某些内容时,它提供一个属性。然后,View绑定到该属性并对其进行操作 - 无论这是什么。


谢谢您的建议,但正如我在其他评论中所说,我的用户控件没有视图模型。我应该为我的用户控件创建一个视图模型吗? - robertwojnar
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - M.E.
在第一种情况下,我应该使用mvvm light中的Messenger来将用户控件视图模型与主窗口视图模型进行通信,还是有其他方法可行? - robertwojnar
使用MVVM Light的Messenger是一个不错的方法,因为它可以使你的ViewModels松耦合(它们不需要彼此了解)。 - M.E.

2
MVVM模式认为,视图应该只与其ViewModel交互,而ViewModel只能与其他ViewModel(和Model)交互。您需要的是中介者。 enter image description here
来源:http://dotnetpattern.com/mvvm-light-messenger/
有了这个,您就不必在用户控件中创建事件,可以与任何已实例化的ViewModel通信。 您可以使用mvvm-light,它提供了Mediator模式(Messenger)的实现。它还提供了其他工具,可帮助您构建MVVM应用程序。
这里 是一个关于MVVMLight Messenger的教程。

通过绑定,您可以适当地更新视图。
因此,ViewModel之间进行交流,而相应的View进行更新。这样,您就不会违反任何MVVM原则。

但是我的UserControl没有viewmodel。Messenger仅用于在viewmodel级别上进行通信(从viewmodel到viewmodel)。您是否建议我为我的UserControl创建一个viewmodel? - robertwojnar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接