我需要在代码后台调用NotifyPropertyChange()吗?

3
在我的程序中,当我的另一个contentPresenter获得焦点时,我想禁用一个contentPresenter。每个presenter都由位于我的MainWindowViewModel中的属性表示。这也是两个presenter的IsEnabled属性所在的位置。
两个contentPresenters都是使用以下结构创建的:UserControl -> ViewModel -> Data Model。
现在,我正在尝试通过从获取焦点的contentPresenter的代码后台更改主窗口的ViewModel中的IsEnabled属性来禁用必要的contentPresenter。
contentPresenter用户控件代码后台:
public partial class EditBlockUC : UserControl
{
    public EditBlockViewModel ViewModel { get { return DataContext as EditBlockViewModel; } }

    public EditBlockUC()
    {
        InitializeComponent();
    }

    //Runs when the user control gets focus
    private void UserControl_GotFocus(object sender, RoutedEventArgs e)
    {
        //This UserControl has access to MainWindowViewModel through
        //it's own ViewModel, EditBlockViewModel         
        ViewModel.MainViewModel.LeftWidgetEnabled = false;
    }
}

这行代码:ViewModel.MainViewModel.LeftWidgetEnabled = false; 可以成功地改变主窗口视图模型中的属性,但是视图没有被影响。如果我找到一种方法调用NotifyPropertyChange(),能否解决这个问题?如果可以,怎么做?
如果这不是正确的解决方案,请告诉我并帮我修复。
谢谢
更新1:
我的完整基类:
public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    public virtual void NotifyPropertyChange<TProperty>(Expression<Func<TProperty>> property)
    {
        var lambda = (LambdaExpression)property;

        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression)lambda.Body;
            memberExpression = (MemberExpression)unaryExpression.Operand;
        }
        else
            memberExpression = (MemberExpression)lambda.Body;

        OnPropertyChanged(memberExpression.Member.Name);
    }

    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

更新2:

我的LeftWidgetEnabled属性:

public bool LeftWidgetEnabled
{
    get { return _leftWidgetEnabled; }
    set { SetField(ref _leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}

你在调用 SetField 后是否成功获取了 LeftWidgetEnabled 的 getter 方法? 请发布你的 LeftWidgetEnabled 属性。 - eran otzap
我刚在我的 LeftWidgetEnabled 属性的 get 方法上设置了一个断点,但程序从未停在那里。 - Eric after dark
返回实现了INotifyPropertyChanged和SetField方法的基类,这样你就能确保PropertyChanged事件被成功触发了。 - eran otzap
你调试过这个程序并检查了Property Change是否为空,以及是否给出了正确的属性名称吗? - eran otzap
2个回答

4
您的 ViewModel.MainViewModel 类的 LeftWidgetEnabled 必须像这样:
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
    get { return leftWidgetEnabled; }
    set { SetField(ref leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}

此外,你的MainViewModel必须实现INotifyPropertyChanged。最好让MainViewModel继承自ViewModelBase,并让ViewModelBase实现INotifyPropertyChanged
public class MainViewModel : ViewModelBase
{        
    private bool leftWidgetEnabled;
    public bool LeftWidgetEnabled
    {
        get { return leftWidgetEnabled; }
        set { SetField(ref leftWidgetEnabled, value, "LeftWidgetEnabled"); }
    }
}

public class ViewModelBase : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

更新 1

您的 ContentPresenter 应该像这样绑定:

<ContentPresenter IsEnabled="{Binding Path=LeftWidgetEnabled}" />

同时,您的UserControlDataContext(其中包含ContentPresenter)应该是MainViewModel的一个实例。

例如:

<UserControl 
    x:Class="MyApplication.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:viewModels="**PATH TO YOUR VIEWMODELS-ASSEMBLY**"
    mc:Ignorable="d">

<UserControl.DataContext>
    <viewModels:MainViewModel />
</UserControl.DataContext>

    <ContentPresenter IsEnabled="{Binding Path=LeftWidgetEnabled}" />
</UserControl>

我已经对属性进行了更改,并在我的 PropertyChangedBase 类中添加了 SetFieldMethod。然而,属性被设置为 false,但视图仍未更改。我将会更新我的问题,附上我正在使用的 SetField 方法。 - Eric after dark
我默认将LeftWidgetEnabled设置为“true”是否有问题?我也可以说我的“ViewModelBase”类中正好有您的内容。 - Eric after dark
不,一个默认设置不应该影响这个。你的 LeftWidgetEnabled 属性是否已经绑定到你的控件上了?我已经编辑了我的代码。 - SeToY
这就是为什么您需要设置UserControl的“DataContext”。请看上面。 - SeToY
如果该控件的DataContext已经是其自身ViewModel的数据上下文怎么办? - Eric after dark
显示剩余3条评论

0

你可以按照以下方式实现INotifyPropertyChanged

class ViewModel : INotifyPropertyChanged
{

  private bool leftWidgetEnabled;
  public bool LeftWidgetEnabled
  {
     get 
      { 
        return leftWidgetEnabled;
      }
      set 
      { 
          leftWidgetEnabled=value
         OnPropertyChanged("LeftWidgetEnabled"); 
      }
   }

    public void OnPropertyChanged(string PropertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

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