在代码后台对视图模型属性的更改做出反应

9
当视图模型(未指定类型)的属性foo在代码后台更改时,假设视图模型正确实现了INotifyPropertyChanged,最佳应对方法是什么?通常情况下,控件元素可以绑定到视图模型属性上,一切正常。但在这种情况下,这还不够,我需要在属性更改时执行一些C#代码。可能的解决方案之一是监听DataContext的相应事件,但这样一来,我还需要处理DataContext本身的变化。另一个可能的解决方案是在代码后台文件中引入一个DependencyProperty。但难道没有更简单的方法吗?

我不确定我正确理解了问题,但为什么不直接在ViewModel中对绑定属性的更改做出反应呢? - Fang
好问题,应该在问题中提到这一点。原因是我需要直接修改控件(通过向特定控件添加列)。我不想在视图模型中这样做。 - MarkusParker
那么属性Foo绑定了什么? - adminSoftDK
那么从该控件派生并为ItemsSource属性注册另一个PropertyChanged回调怎么样? - Clemens
我发布了一个可能的解决方案,现在我需要走了,但你可以看一下。 - adminSoftDK
显示剩余6条评论
3个回答

14

如果可能的话,我会建议避免这样做。如果有必要,订阅 PropertyChanged 事件。

我会将此代码放在代码后台的构造函数中。

INotifyPropertyChanged viewModel = (INotifyPropertyChanged)this.DataContext; 
viewModel.PropertyChanged += (sender, args) => {
    if (!args.PropertyName.Equals("foo"))
        return;
    // execute code here.
};

可维护性提示 如果您确定数据上下文是具有该属性的类型,请将其转换为该类型并使用nameof运算符而不是魔术字符串。

编辑(响应数据上下文更改)

您应该能够订阅DataContextChanged事件来处理数据上下文的更改。

INotifyPropertyChanged previous;
// constructor
public SomeCodeBehindClass()
{
    previous = (INotifyPropertyChanged)this.DataContext;
    DataContextChanged += (sender, args) => SubscribeToFooChanges((INotifyPropertyChanged)args.NewValue);
    SubscribeToFooChanges(previous);
}

// subscriber
private void SubscribeToFooChanges(INotifyPropertyChanged viewModel)
{
    if (previous != null)
        previous.PropertyChanged -= FooChanged;
    previous = viewModel;
    if (viewModel != null)
        viewModel.PropertyChanged += FooChanged;
}

// event handler
private void FooChanged (object sender, PropertyChangedEventArgs args)
{
    if (!args.PropertyName.Equals("foo"))
        return;
    // execute code here.
}

1
这是一个有效的解决方案,但正如我上面所述,当 this.DataContext 改变时需要额外的工作。在 xaml 中使用绑定不需要这样做。我想知道是否有可能在代码后台执行类似的操作。 - MarkusParker
我已经添加了一个使用DataContextChanged DependencyPropertyChangedEventHandler的示例。我还没有运行代码。 - Kelson Ball
1
在你的SubscribeToFooChanges方法中,需要对previousviewModel进行空值检查,因为DataContext最初为空,也可能被赋值为空。 - myermian
我正努力获取更多关于为什么有些人认为这是一个不好的模式的信息。在需要在更改 ViewModel 属性时执行复杂视图逻辑的情况下,这似乎是必要的。 - Chucky
1
@Chucky,我认为将视图逻辑放在代码后面是可以的,只要将其放在类似于自定义控件中,并使用实际的XAML绑定与视图模型绑定即可。这使得很容易将真正的“视图逻辑”与“业务逻辑”分开。这也意味着您始终可以查看视图的XAML以查看所有绑定,而无需在背景中使用额外的“隐藏”绑定。 - Kelson Ball

1
可能的解决方案,在您的代码后台,您可以这样做。
 (((dynamic)DataContext).foo as ObservableCollection<object>).CollectionChanged += (s, e) =>
        {
            if (e.Action == NotifyCollectionChangedAction.Replace)
            {

            }
            else
            {
               //and so on
            }
        };

这是一种有趣的方法,但它并没有考虑到DataContext是否会改变或者变量foo是否被赋予另一个值。 - MarkusParker
@MarkusParker DataContextChanged是一个可以在代码后台轻松访问的事件。将整个集合分配给新值不是一个好主意,因为所有绑定都需要重新分配(这里有一些相关阅读http://updatecontrols.net/doc/tips/common_mistakes_observablecollection)。最好清除集合。 - adminSoftDK

-1

如果这些代码行涉及业务逻辑,您不应在代码后面使用它们。 在其他情况下,视图模型的属性将始终绑定到任何标记元素,如果必须对此属性值进行预处理,则应使用转换器


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接