WPF:如何在没有代码隐藏代码的情况下将事件从 ViewModel 发送到 View?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1894329/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 21:19:23  来源:igfitidea点击:

WPF: how to signal an event from ViewModel to View without code in codebehind?

wpfmvvmeventsviewviewmodel

提问by Tomá? Kafka

I have quite simple (I hope :)) problem:

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

In MVVM, View usually listens on changes of ViewModel's properties. However, I would sometimes like to listen on event, so that, for example, View could start animation, or close window, when VM signals.

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

Doing it via bool property with NotifyPropertyChanged (and starting animation only when it changes from false to true) is possible, but it feels like a hack, I'd much prefer to expose event, as it is semantically correct.

通过带有 NotifyPropertyChanged 的​​ bool 属性(并且只有在它从 false 变为 true 时才启动动画)来完成它是可能的,但感觉就像一个黑客,我更喜欢公开事件,因为它在语义上是正确的。

Also, I'd like to do it without code in codebehind, as doing viewModel.myEvent += handlerthere would mean that I'd have manually unregister the event in order to allow View to be GC'd - WPF Views are already able to listen on properties 'weakly', and I'd much prefer to program only declaratively in View.

另外,我想在代码隐藏中没有代码的情况下做到这一点,因为这样做viewModel.myEvent += handler意味着我必须手动取消注册事件以允许 View 被 GC 处理 - WPF 视图已经能够“弱”地侦听属性',而且我更喜欢仅在 View 中进行声明式编程。

The standard strong event subscription is also bad, because I need to switch multiple ViewModels for one View (because creating View every time takes too much CPU time).

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

Thank you for ideas (if there is a standard solution, a link to msdn will suffice)!

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

回答by Kent Boogaart

Some comments:

一些评论:

  • You can use the weak event patternto ensure that the view can be GC'd even if it is still attached to the view model's event
  • If you're already switching multiple VMs in for the one view, wouldn't that be the ideal place to attach/detach the handler?
  • Depending on your exact scenario, you could just have the VM expose a state property which the view uses as a trigger for animations, transitions, and other visual changes. Visual state manageris great for this kind of thing.
  • 您可以使用弱事件模式来确保视图可以被 GC 处理,即使它仍然附加到视图模型的事件
  • 如果您已经为一个视图切换了多个 VM,那这不是附加/分离处理程序的理想位置吗?
  • 根据您的具体场景,您可以让 VM 公开一个状态属性,视图将其用作动画、过渡和其他视觉变化的触发器。视觉状态管理器非常适合这种事情。

回答by Chris Marcus

This is something that I wrestled with as well...

这也是我纠结的事情...

Similar to what others are saying, but here is an example with some code snippets... This example shows how to use pub/sub to have a View subscribe to an event fired by the VM - in this case I do a GridView. Rebind to ensure the gv is in sync with the VM...

与其他人所说的类似,但这里有一个带有一些代码片段的示例...此示例显示了如何使用发布/订阅让 View 订阅 VM 触发的事件 - 在这种情况下,我执行了 GridView。重新绑定以确保 gv 与 VM 同步...

View (Sub):

视图(子):

 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;
        }
    }

Call unsub from the close method to prevent memory leaks.

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

ViewModel (Pub):

视图模型(发布):

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

Payload class

有效载荷类

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;
    }
}

Note that you can also set the constructor up to pass in a Guid, or some identified in, which can be set on Pub and checked on sub to be sure pub/sub is in sync.

请注意,您还可以将构造函数设置为传入 Guid 或某些标识,可以在 Pub 上设置并在 sub 上检查以确保 pub/sub 同步。

回答by voidx

imho yYand separated

imho yYand 分开了

  1. state - to be able to move data back/forth between view <-> vm
  2. actions - to be able to call onto view model functions/commands
  3. notifications - to be able to signal to the view that something has happened and you want it to take a viewy action like make an element glow, switch styles, change layout, focus another element etc.
  1. 状态 - 能够在视图 <-> vm 之间来回移动数据
  2. 动作 - 能够调用视图模型功能/命令
  3. 通知 - 能够向视图发出信号,表明发生了某些事情,并且您希望它执行视图操作,例如使元素发光、切换样式、更改布局、聚焦另一个元素等。

while is true that you can do this with a property binding, its more of a hack as tomas mentioned; always has felt like this to me.

虽然您可以使用属性绑定来做到这一点,但正如托马斯提到的那样,它更像是一种黑客攻击;我一直有这种感觉。

my solution to be able to listen for 'events' from a view model aka notifications is to simple listen for data-context changes and when it does change i verify the type is the vm i'm looking for and connect the events. crude but simple.

我能够从视图模型(又名通知)中侦听“事件”的解决方案是简单地侦听数据上下文更改,当它发生更改时,我验证类型是我正在寻找的虚拟机并连接事件。粗糙但简单。

what i would really like is a simple way to define some 'view model event' triggers and then provide some kind of handler for it that would react on the view side of things all in the xaml and only drop to code behind for stuff thats not do-able in xaml

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

回答by Jim Wallace

Like adrianm said, when you trigger your animation off a bool property you are actually responding to an event. Specifically the event PropertyChangedwhich the WPF subsystem. Which is designed to attach/detach correctly to/from so that you don't leak memory (you may forget to do this when wiring an event yourself and cause a memory leak by having a reference active to an object which otherwise should be GCed).

就像阿德里安说的那样,当您从 bool 属性触发动画时,您实际上是在响应一个事件。特别是PropertyChangedWPF 子系统中的事件。它旨在正确地附加/分离到/从,以便您不会泄漏内存(您可能会在自己连接事件时忘记这样做,并通过对一个对象的引用处于活动状态而导致内存泄漏,否则该对象应该被 GCed) .

This allows you to expose your ViewModel as the DataContextfor the control and respond correctly to the changing of properties on the datacontext through databinding.

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

MVVM is a pattern that works particularly well with WPF because of all these things that WPF gives you, and triggering off a property change is actually an elegant way to use the entire WPF subsystem to accomplish your goals :)

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

回答by JerKimball

A more general question to ask is: "Why am I trying to deal with this event in my ViewModel?"

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

If the answer has anything to do with view-only things like animations, I'd argue the ViewModel needs not know about it: code behind (when appropriate), Data/Event/PropertyTriggers, and the newer VisualStateManager constructs will serve you much better, and maintain the clean separation between View and ViewModel.

如果答案与动画之类的仅查看内容有关,我认为 ViewModel 不需要知道它:代码隐藏(适当时)、数据/事件/属性触发器和较新的 VisualStateManager 构造将为您提供更好的服务,并保持 View 和 ViewModel 之间的清晰分离。

If something needs to "happen" as a result of the event, then what you really want to use is a Command pattern - either by using the CommandManger, handling the event in code behind and invoking the command on the view model, or by using attached behaviors in the System.Interactivity libs.

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

Either way, you want to keep your ViewModel as "pure" as you can - if you see anything View-specific in there, you're probably doing it wrong. :)

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