当绑定属性的值不变时,是否可以说服PropertyChangedCallback运行?

时间:2014-09-01 08:59:04

标签: c# wpf mvvm

我的应用程序使用MVVM架构,ViewModel不了解View。当ViewModel对象需要显示新的View时,它会公开一个公共ShowNewView属性,该属性是一个对象,其类基于我的ViewModel基类。 WPF视图将自定义DependencyProperty绑定到此,并使用PropertyChangedCallback构造并显示一个允许的窗口。

第一次设置ShowNewView属性时,一切正常。但是,如果用户关闭窗口然后尝试重新打开它,则在引发PropertyChanged事件并且未调用PropertyChangedCallback时,ShowNewView属性的值未发生更改。

为了欺骗' DependencyProperty检测到值已经发生了变化(即使存储在ViewModel属性中的值可能没有实际更改),我使用了Window类公开的SetCurrentValue方法来强制DependencyProperty&# 39; s值为null

#region ShowNewViewProperty

private static readonly DependencyProperty _ShowNewViewProperty =
    DependencyProperty.RegisterAttached
    (
        "ShowNewView",
        typeof(IRootViewModel),
        typeof(WpfViewWindow),
        new PropertyMetadata(ShowNewViewPropertyChanged)
    );

    public static DependencyProperty ShowNewViewProperty { get { return _ShowNewViewProperty; } }

    public static IRootViewModel GetShowNewView(Window source)
    {
        return (IRootViewModel)source.GetValue(ShowNewViewProperty);
    }

    public static void SetShowNewView(Window target, IRootViewModel value)
    {
        target.SetValue(ShowNewViewProperty, value);
    }

    private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        WpfViewWindow window = d as WpfViewWindow;
        IRootViewModel newValue = e.NewValue as IRootViewModel;
        if ((null != window) && (null != newValue))
        {
            // Create a child WpfViewWindow.  This method is part of my
            // framework that uses ResourceDictionary entries, imported by MEF
            // to locate the View class corresponding to the ViewModel parameter's
            // class.
            WpfViewWindow modelessWindow = window.CreateWpfViewWindow(newValue);
            if (null != modelessWindow)
            {
                // Show the new WpfViewWindow.
                modelessWindow.Show();
            }

            // Clear the current value so that the next PropertyChanged event
            // is processed even if the underlying value has not actually changed.
            window.SetCurrentValue(ShowNewViewProperty, null);
        }
    }

    #endregion

从技术上讲,这是有效的,因为它会导致在PropertyChanged事件触发时运行回调,无论该值是否实际发生了变化。但是,每次更新ViewModel属性时,都会导致回调被调用(递归)两次:一次响应ViewModel事件,一次响应SetCurrentValue方法被调用

在这里,有很多问题涉及PropertyChangedCallback未被调用,或者在其他情况下不被多次调用。

是否有更优雅的方法来实现这一点,不会导致回调从ViewModel的每个PropertyChanged事件运行两次?即有什么方法可以绕过框架检查来验证旧值和新值是否不同?

澄清

正在创建的视图并不总是一个WPF窗口,例如,在我的单元测试中它是一个模拟,后来在项目中它可能是一个sperate日志组件。并非所有ViewModel对象都来自同一个程序集,众所周知,将来还需要其他功能,但具体细节目前尚未定义。该应用程序允许用户通过简单的网络连接设备。最初网络是通过RS-485的ModbusRTU,但最终客户可能想要使用CANOpen或Profinet或其他一些传输层,我必须提供插件机制,允许添加新功能而不改变现有代码。

公平地说,我可以使用几种替代机制来实现相同的结果(即让ViewModel请求创建一个新的View),但是我有兴趣知道是否有办法制作一个DependencyPropety'忘了'它之前的价值是什么。

1 个答案:

答案 0 :(得分:1)

此类问题的常用解决方案是从您的ShowNewViewPropertyChanged方法中提取代码并将其置于不同的方法中:

private void SomeNewMethod(IRootViewModel newValue)
{
    // Create a child WpfViewWindow.  This method is part of my
    // framework that uses ResourceDictionary entries, imported by MEF
    // to locate the View class corresponding to the ViewModel parameter's
    // class.
    WpfViewWindow modelessWindow = CreateWpfViewWindow(newValue);
    if (null != modelessWindow)
    {
        // Show the new WpfViewWindow.
        modelessWindow.Show();
    }

    // Clear the current value so that the next PropertyChanged event
    // is processed even if the underlying value has not actually changed.
    SetCurrentValue(ShowNewViewProperty, null);
}

现在您只需从ShowNewViewPropertyChanged处理程序和您想要的任何地方调用该方法:

private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    WpfViewWindow window = d as WpfViewWindow;
    IRootViewModel newValue = e.NewValue as IRootViewModel;
    if ((null != window) && (null != newValue))
    {
        window.SomeNewMethod(newValue);
    }
}