WPF:如何从ViewModel发送一个事件信号到代码隐藏没有代码视图?

我有相当简单的(我希望:))问题:

在MVVM中,View通常监听ViewModel属性的变化。 不过,我有时候喜欢听事件,例如,View可以在VM信号时开始animation,或者closures窗口。

通过使用NotifyPropertyChanged的bool属性来做到这一点(只有当它从false更改为true时才开始animation)是可能的,但感觉像是一个黑客攻击,我更喜欢暴露事件,因为它在语义上是正确的。

此外,我想这样做没有代码在代码隐藏,因为做viewModel.myEvent += handler将意味着我会手动取消注册该事件,以便允许查看GC'D – WPF视图已经能听'属性'弱',我更喜欢编程只声明在视图。

标准的强大的事件订阅也是不好的,因为我需要切换多个视图模型为一个视图(因为每次创build视图需要太多的CPU时间)。

谢谢你的想法(如果有一个标准的解决scheme,一个链接到MSDN就足够了)!

感谢您指出您的问题。

使用混合行为和现有触发器和操作来满足Tomas的要求:

我怎样才能有一个WPF EventTrigger视图触发当底层的ViewModel指示它应该?

一些评论:

  • 您可以使用弱事件模式来确保视图可以GC'd,即使它仍然附加到视图模型的事件
  • 如果您已经为一个视图切换了多个虚拟机,那么这不是理想的连接/分离处理器的地方吗?
  • 根据具体情况,您可以让VM暴露一个视图用作animation,转换和其他视觉更改的触发器的状态属性。 视觉状态pipe理器对于这种事情是很好的。

这个线程是相当老,但我会提供我的$ 0.02,因为这是我最近与摔跤的东西…

类似于其他人所说的,但是这里有一些代码片段的例子…这个例子展示了如何使用pub / sub让一个View订阅一个由VM发起的事件 – 在这种情况下,我做了一个GridView。 重新绑定以确保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上设置的Guid,并在sub上进行检查以确保pub / sub同步。

我和你分开了

  1. 状态 – 能够在视图之间来回移动数据
  2. 操作 – 能够调用视图模型函数/命令
  3. 通知 – 能够向视图发出信号,表明发生了某些事情,并且希望它能够采取一种有意义的行动,例如使元素发光,切换样式,更改布局,聚焦另一个元素等。

虽然你可以用一个属性绑定来做到这一点,但它更像是托马斯提到的黑客攻击。 总是觉得这样对我。

我的解决scheme能够侦听来自视图模型(即通知)的“事件”是简单地监听数据上下文的变化,当它发生变化时,我确认types是我正在查找并连接事件的虚拟机。 粗糙而简单。

我真正想要的是一个简单的方法来定义一些“视图模型事件”触发器,然后提供某种types的处理程序,它将在xaml中的所有事物的视图方面作出反应,并只放弃代码后面的东西可以在xaml中使用

就像adrianm说的,当你从一个布尔属性触发你的animation时,你实际上正在响应一个事件。 具体是WPF子系统的事件PropertyChanged 。 它被devise为正确地连接/分离,以便不泄漏内存(您可能会忘记在自己连线事件时执行此操作,并通过将对象的引用激活,从而导致内存泄漏) 。

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

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

一个更普遍的问题是:“为什么我要在ViewModel中处理这个事件?

如果答案与像animation这样的仅包含视图的东西有关,那么我认为ViewModel不需要知道它:后面的代码(适当的时候),Data / Event / PropertyTriggers以及更新的VisualStateManager构造将为您提供更好的效果并保持View和ViewModel之间的清晰分离。

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

无论哪种方式,你都希望保持你的ViewModel为“纯”,如果你看到任何特定于View的内容,你可能会做错了。 🙂