WPF View-ViewModel松散耦合的通信

时间:2013-11-08 12:43:53

标签: wpf mvvm behavior icommand routedevent

使用WPF,我的团队正在尝试使用MVVM设计模式分离设计和代码。为了实现这一点,我们正逐渐远离UserControl方法,因为它在设计和代码之间有很大的耦合。我们一直在研究使用Control Templates,Styles和DataTemplates以及ViewModel类。到目前为止,我们已经完成了大量工作。

我们遇到的问题与View和ViewModel之间的通信/通知有关。目前,我们已经“解决了”观点 - >使用ICommand的Viewmodel通信问题。即,我们创建一个按钮并将其“Command”参数绑定到ViewModel中定义的RelayCommand的名称。通过这样做,从View中引发的按钮单击或其他Command事件调用ViewModel中定义的函数。这很有效。

我们遇到的主要问题是让通知以相反的方式运行:即ViewModel中的数据更改需要在View中触发更新。我们使用NotifyPropertyChanged和DataTriggers进行此操作,但这并不能满足我们的需求。我们需要的是能够在Viewmodel中引发某种事件并让View订阅所述事件。我们一直在寻找答案,并找到了RoutedEvents和AttachedBehaviors。 RoutedEvents似乎是解决方案的赢家,但是根据我们的研究,RoutedEvents无法注册到不从UIElement扩展的ViewModel,我们特别试图将代码与设计分开。

最终我们要做的是设置一个ViewModel,其中可以设置一个参数,或者可以调用一个函数来引发View上的事件或行为,然后运行动画。我们确实使用了DataTriggers,但是我们试图将我们的Animations分离到ControlTemplate中,这会引发问题,因为包含DataTriggers的DataTemplate无法访问ControlTemplate中定义的Storyboard。

有人可以指出我们正确的方向吗?特别是在ViewModel中引发事件(不需要扩展UIElement)并在View中订阅此事件并激活Storyboard。

由于

6 个答案:

答案 0 :(得分:1)

我自己使用MVVM,如果我想在屏幕上更改内容,我使用INotifyPropertyChanged接口。

每当必须更改某个UI元素时,都会调出OnPropertyChanged事件并更新UI。

答案 1 :(得分:0)

最好的方法是创建一个基本视图,一旦设置了属性,就会自动订阅VM上的INotifyPropertyChanged(不要忘记取消订阅!)。远离视图模型上的RoutedEvents(或任何与UI相关的事件)。他们应该只实现INotifyPropertyChanged。

Catel为您提供了开箱即用的解决方案:

https://catelproject.atlassian.net/wiki/display/CTL/Mapping+properties+from+view+to+view+model

如果您不能使用Catel,只需将您需要的部分复制/粘贴到您自己的框架中即可。但是,不要浪费你的时间一遍又一遍地重新发明轮子; - )

答案 2 :(得分:0)

您还可以使用介体模式从viewmodel启动动画。只需从viewmodel发送一条消息,并在视图上订阅该类消息。

答案 3 :(得分:0)

我使用委托从ViewModel到View进行通信。让我解释。在我的框架中,我有这个抽象类:

public abstract class NotificationObject : INotifyPropertyChanged
{
    // very usefull to order view (if exist) to close 
    public delegate void ActionClose(Boolean closeResult);

    public delegate void ActionUpdate();

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        // take a copy to prevent thread issues
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    // other implementation for RaisePropertyChange (not the point here)
    // protected virtual void RaisePropertyChanged(params string[] propertyNames)
    // protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
}

现在,我的所有ViewModel都可以从NotificationObject继承

public class MyViewModel : NotificationObject
{
    public MyViewModel ()
    {
        // initialisation
    }

    // bindable action UI -> VM
    public ICommand Command1 { get; set; }
    public ICommand Command2 { get; set; }

    // VM -> View
    public ActionUpdate Action1 { get; set; }
    public ActionUpdate Action2 { get; set; }

    // in this method, we need to "notify" view that it must do something (refresh, close, modify UI ...)
    private void MyMethod()
    {
        // never forget that UI can not exist (testing for exemple), or not UI (command-line process), so Action1 can be null
        if (this.Action1 != null)
        {
            this.Action1 ();
        }        
    }
}

现在,在View中,我们可以声明委托(在Window_Loaded中,加载了ViewModel):

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    MyViewModel context = this.DataContext as MyViewModel ;

    if (context != null)
    {
        NotificationObject.ActionUpdate myAction1 = new NotificationObject.ActionUpdate(this.MyAction1);
        context.Action1 = myAction1 ;
        NotificationObject.ActionUpdate myAction2 = new NotificationObject.ActionUpdate(this.MyAction2);
        context.Action2 = myAction2 ;
    }
}

private void MyAction1()
{
    // do something
}

private void MyAction2()
{
    // do something
}

这就是全部。 ^^

答案 4 :(得分:0)

一种更加通用的解决方案,即使是复杂的代码也可以解耦,它是View Services模式。它的长短是类似于视图,视图服务是平台相关的,但与视图不同,是ViewModel所熟知的。一个实际的实现是让ViewModels Constructor期望一个所需ViewService的接口,而&#34; View&#34;代码的一侧处理所述接口的实现,具有View特定代码,如Dialogs,Notifications等。这也适用于Inversion of Control。如果在View上根本没有任何代码可能会有所帮助,那么这不会有所帮助。

答案 5 :(得分:-1)

我发现MVVM不是松耦合的好选择。默认情况下,Microsoft Model-View-无论什么解决方案模板都将所有内容都放入一个项目要真正松散耦合,请尝试将除GUI元素之外的所有内容移动到自己的库中,并避免对特定目标的所有引用。松散耦合的系统只能在目标(客户端/控制台/ Web服务)平台上引用System.Windows或System.Web,并且在模型和业务层库中具有可重用的组件。您可能会得到一些便于通用实现的外观和适配器类。我想这取决于你想要解耦的东西。框架通常会增加更多限制。我知道这不是一个非常受欢迎的观点,但那是我的2美分。