WPF:如何将ViewModel中的事件发送到View,而无需代码中的代码?

时间:2009-12-12 18:42:59

标签: wpf mvvm events view viewmodel

我很简单(我希望:))问题:

在MVVM中,View通常会侦听ViewModel属性的更改。但是,我有时会喜欢听事件,例如,当VM发出信号时,View可以启动动画或关闭窗口。

通过带有NotifyPropertyChanged的bool属性(并且只有当它从false变为true时才开始动画)是可能的,但感觉就像是一个hack,我更喜欢暴露事件,因为它在语义上是正确的。

此外,我想在代码隐藏中没有代码的情况下执行此操作,因为执行viewModel.myEvent += handler意味着我已手动取消注册事件以允许View为GC'd - WPF视图是已经能够“弱势地”听取属性了,我更喜欢在View中以声明方式编程。

标准的强事件订阅也很糟糕,因为我需要为一个View切换多个ViewModel(因为每次创建View会花费太多的CPU时间)。

感谢您的想法(如果有标准解决方案,msdn的链接就足够了)!

5 个答案:

答案 0 :(得分:2)

一些意见:

  • 您可以使用weak event pattern确保视图可以GC,即使它仍然附加到视图模型的事件
  • 如果您已经为一个视图切换了多个VM,那么这不是理想的附加/分离处理程序的地方吗?
  • 根据您的具体情况,您可以让VM公开状态属性,视图将其用作动画,过渡和其他视觉变化的触发器。 Visual state manager非常适合这种事情。

答案 1 :(得分:2)

这个帖子已经很老了,但是我会提供0.02美元,因为这是我最近摔跤的事情......

与其他人的说法相似,但这里有一些代码片段的例子......这个例子展示了如何使用pub / sub让一个View订阅一个由VM触发的事件 - 在这种情况下我做了一个网格视图。重新绑定以确保gv与VM同步...

查看(子):

 using Microsoft.Practices.Composite.Events;
 using Microsoft.Practices.Composite.Presentation.Events;

 private SubscriptionToken getRequiresRebindToken = null;

    private void SubscribeToRequiresRebindEvents()
    {
        this.getRequiresRebindToken =
            EventBus.Current.GetEvent<RequiresRebindEvent>()
            .Subscribe(this.OnRequiresRebindEventReceived, 
                ThreadOption.PublisherThread, false,
                MemoryLeakHelper.DummyPredicate);
    }

    public void OnRequiresRebindEventReceived(RequiresRebindEventPayload payload)
    {
        if (payload != null)
        {
            if (payload.RequiresRebind)
            {
                using (this.gridView.DeferRefresh())
                {
                    this.gridView.Rebind();
                }
            }
        }
    }

    private void UnsubscribeFromRequiresRebindEvents()
    {
        if (this.getRequiresRebindToken != null)
        {
            EventBus.Current.GetEvent<RequiresRebindEvent>()
                .Unsubscribe(this.getRequiresRebindToken);
            this.getRequiresRebindToken = null;
        }
    }

从close方法调用unsub以防止内存泄漏。

ViewModel(Pub):

 private void PublishRequiresRebindEvent()
 {
      var payload = new RequiresRebindEventPayload();
      payload.SetRequiresRebind();
      EventBus.Current.GetEvent<RequiresRebindEvent>().Publish(payload);
 }

有效负载类

using System;
using Microsoft.Practices.Composite.Presentation.Events;

public class RequiresRebindEvent 
    : CompositePresentationEvent<RequiresRebindEventPayload>
{

}

public class RequiresRebindEventPayload
{
    public RequiresRebindEventPayload()
    {
        this.RequiresRebind = false;
    }

    public bool RequiresRebind { get; private set; }

    public void SetRequiresRebind()
    {
        this.RequiresRebind = true;
    }
}

请注意,您也可以将构造函数设置为传入Guid或其中一些,这可以在Pub上设置并在子上检查以确保pub / sub同步。

答案 2 :(得分:1)

imho yYand分开

  1. state - 能够在视图&lt; - &gt;之间来回移动数据VM
  2. 操作 - 能够调用视图模型函数/命令
  3. 通知 - 能够向视图发出信号,表明事情已经发生,并且您希望它采取一种看似动作,例如使元素发光,切换样式,更改布局,聚焦另一个元素等。
  4. 虽然你可以用属性绑定来做到这一点,但它更像是提到的tomas;总觉得这样对我来说。

    我的解决方案是能够从视图模型中监听'事件'即通知是简单地监听数据上下文更改,当它发生更改时我验证类型是我正在寻找的vm并连接事件。原油但很简单。

    我真正想要的是一种简单的方法来定义一些“视图模型事件”触发器,然后为它提供某种处理程序,它将在xaml中的所有事物的视图方面做出反应,并且只返回到后面的代码那些在xaml中无法实现的东西

答案 3 :(得分:0)

就像adrianm所说,当你从bool属性中触发你的动画时,你实际上正在回应一个事件。特别是WPF子系统的事件PropertyChanged。它被设计为正确地连接/分离,以便您不会泄漏内存(您可能会忘记在自己连接事件时执行此操作并通过激活对象来引起内存泄漏,否则应该对其进行GC操作) 。

这允许您将ViewModel公开为控件的DataContext,并通过数据绑定正确响应datacontext上属性的更改。

MVVM是一种特别适合WPF的模式,因为WPF为您提供了所有这些功能,触发属性更改实际上是使用整个WPF子系统实现目标的优雅方式:)

答案 4 :(得分:0)

更常见的问题是:“为什么我要在ViewModel中处理此事件?”

如果答案与动画等只有视图的东西有关,我认为ViewModel不需要知道它:后面的代码(适当的时候),Data / Event / PropertyTriggers和更新的VisualStateManager构造将会服务你做得更好,并保持View和ViewModel之间的清晰分离。

如果事件需要“发生”事件,那么你真正想要使用的是命令模式 - 通过使用CommandManger,在后面的代码中处理事件并在视图模型上调用命令,或者在System.Interactivity libs中使用附加行为。

无论哪种方式,您都希望将ViewModel保持为“纯粹” - 如果您在那里看到任何特定于View的内容,那么您可能做错了。 :)