【问题标题】:Do I need to call NotifyPropertyChange() in code-behind?我需要在代码隐藏中调用 INotifyPropertyChanged() 吗?
【发布时间】:2013-11-05 14:47:36
【问题描述】:

在我的程序中,当我的另一个 contentPresenter 获得焦点时,我想禁用 contentPresenter。每个演示者都由位于我的MainWindowViewModel 中的属性表示。这也是两位演示者的 IsEnabled 属性所在的位置。

contentPresenters 均使用以下结构创建:UserControl -> ViewModel -> Data Model。

现在我正在尝试通过更改主窗口 ViewModel 中的 IsEnabled 属性来禁用必要的 contentPresenter,方法是从获得焦点的 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 属性。
  • 我只是在我的LeftWidgetEnabled 属性中的get 处设置了一个断点,并且程序从未停止过。
  • 这意味着您的 PropertyChanged 事件从未在实现 INotifyPropertyChanged 和 SetField 方法的基类之后引发。
  • 你有没有调试过这个并检查属性更改是否不为空并且给出了正确的属性名称?

标签: c# wpf mvvm inotifypropertychanged


【解决方案1】:

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}" />

而UserControl 的DataContext(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>

【讨论】:

  • 我已经对属性进行了更改,并将 SetFieldMethod 添加到我的PropertyChangedBase 类中。但是,属性设置为false,但视图仍然没有改变。我将使用我正在使用的SetField 方法更新我的问题。
  • 如果我将 LeftWidgetEnabled 默认设置为 true 是否有问题?我也可以说我在ViewModelBase 课堂上拥有你所拥有的一切
  • 不,默认设置不应该影响这一点。您的 LeftWidgetEnabled 属性是否实际绑定到您的控件?我已经编辑了我的代码。
  • 我已将我的LeftWidgetEnabled 属性绑定到我的contentPresenter,就像您在上面一样。当我运行程序时,在输出窗口中它现在说它找不到该属性。它似乎在要禁用的 contentPresenter 的 ViewModel 中寻找它。
  • 这就是为什么你需要设置你的 UserControl 的DataContext。往上看。
【解决方案2】:

你实现 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 2012-05-26
    相关资源
    最近更新 更多