1

使用 WPF,我的团队正在尝试使用 MVVM 设计模式分离设计和代码。为了实现这一点,我们正在慢慢远离 UserControl 方法,因为它在设计和代码之间存在大量耦合。我们一直在研究结合 ViewModel 类使用控制模板、样式和数据模板。迄今为止,我们已经完成了大量此类工作。

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

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

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

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

谢谢

4

6 回答 6

1

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

每当某些 UI 元素必须更改时,就会调用 OnPropertyChanged 事件并更新 UI。

于 2013-11-08T12:51:55.323 回答
0

您还可以使用中介模式从视图模型开始动画。只需从视图模型发送一条消息并订阅视图上的那种消息。

于 2013-11-08T12:59:47.933 回答
0

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

Catel 为您提供了一个开箱即用的解决方案来解决这些问题:

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

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

于 2013-11-08T12:51:14.177 回答
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
}

就这样。^^

于 2013-11-08T13:31:55.217 回答
0

View Services 模式是一个更通用的解决方案,甚至可以将复杂的代码解耦。总而言之,与视图类似,视图服务是平台相关的,但与视图不同的是,视图模型是已知的。一个实际的实现是让 ViewModels 构造函数期望一个所需 ViewService 的接口,而代码的“视图”端使用视图特定代码(如对话框、通知等)处理所述接口的实现。这非常适用于控制反转也。但是,如果 View 上根本不应该有任何代码,那也没什么帮助。

于 2015-06-24T16:19:52.753 回答
-1

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

于 2015-06-09T04:15:55.163 回答