从 ViewModel 触发 UIElement 的操作

Posted

技术标签:

【中文标题】从 ViewModel 触发 UIElement 的操作【英文标题】:Triggering an action of a UIElement from a ViewModel 【发布时间】:2020-12-02 20:42:45 【问题描述】:

我已经实现了Wiesław Šoltés'很棒的ZoomBorder,但我在 WPF 和 MVVM 方面有点挣扎。为了问题的完整性,ZoomBorder 是一个继承自BorderUIElement,并为用户提供了缩放和平移继承边框内容的可能性。它还可以重置缩放和平移。

我想让ZoomBorder 对某个视图模型发布的事件做出反应,以便在发布该事件时,ZoomBorder 重置缩放。在我的实现中,ZoomBorderDataContext 是一个ContentViewModel,它有一个通过Autofac 注入的IEventAggregator (Prism.Events)。理想情况下,我希望将事件聚合器直接注入ZoomBorder,以便它可以订阅事件,但我不能,因为构造函数需要是无参数的。

所以ContentViewModel 必须订阅该事件,但我如何从ContentViewModel 调用ZoomBorderReset 方法?我知道我会违反 MVVM,但我不知道该怎么做。我想过让ZoomBorder 通过依赖属性公开Command,但是Reset 代码必须在视图模型上,但它不能。

【问题讨论】:

【参考方案1】:

您可以在视图或控件中使用ServiceLocator 来解析容器中的类型。

public ZoomBorder()

   _eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();

如果您使用 AutoFac 和不带 Prism 的事件聚合器,则可以使用包 Autofac.Extras.CommonServiceLocator 和 register your container 到 ServiceLocator

var builder = new ContainerBuilder();
var container = builder.Build();

var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);

【讨论】:

【参考方案2】:

我会使用绑定。

为 Zoomborder 添加一个依赖属性。

    public bool? ResetZoom
    
        get
        
            return (bool?)GetValue(ResetZoomProperty);
        
        set
        
            SetValue(ResetZoomProperty, value);
        
    
    public static readonly DependencyProperty ResetZoomProperty =
        DependencyProperty.Register("ResetZoom",
                    typeof(bool?),
                    typeof(CloseMe),
                    new PropertyMetadata(null, new PropertyChangedCallback(ResetZoomChanged)));
    private static void ResetZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        if ((bool?)e.NewValue != true)
        
            return;
        
        ZoomBorder zb = (ZoomBorder)d;
        zb.Reset();
        zb.SetCurrentValue(ResetZoomProperty, false);
    

然后,您可以将其绑定到 ContentViewModel 中的公共 bool 属性。

设置为 true 时,边框将重置。

【讨论】:

谢谢。我考虑过这个选项(很抱歉在问题中没有提到它),但它并没有让我相信将它设置为 false 没有任何效果。这就像你要为实际上不是一个状态的东西维护一个状态,而更像是一个做某事的信号。 将其设置为 false 会被显式捕获,因此绝对没有效果。是的,它是做某事的信号,但使用简单的标准 wpf 机制。【参考方案3】:

本视频展示了如何从视图/UI 组件创建抽象,并使用接口从 VM 调用它们的方法。不要让标题欺骗你。这看起来非常适合这种情况

“如何在 C# 中从 ViewModel 关闭窗口”

https://youtu.be/U7Qclpe2joo

除了在窗口上调用 Close 方法外,您还可以调整它以从 VM 调用控件上的 Reset 方法。

【讨论】:

以上是关于从 ViewModel 触发 UIElement 的操作的主要内容,如果未能解决你的问题,请参考以下文章

WPF MVVM 从 ViewModel 触发事件的正确方法

我的活动中的 ViewModel.Observe() 函数没有触发

Knockout ViewModel 在 ajax 之后被更新,但我的 foreach 没有被触发

从 StackPanel C# UWP 中选择图像(将动作侦听器添加到 UIElement 并将图像添加到 UIElement)

如何停止从另一个 ViewModel 按钮播放音频

从 ViewModel 设置条目的焦点