属性更改事件始终为空

44

我有以下(缩写)的XAML代码:

<TextBlock Text="{Binding Path=statusMsg, UpdateSourceTrigger=PropertyChanged}"/>

我有一个单例类:

public class StatusMessage : INotifyPropertyChanged
{   
    private static StatusMessage instance = new StatusMessage();

    private StatusMessage() { }

    public static StatusMessage GetInstance()
    {
        return instance;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string status)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(status));
        }
    }

    private string statusMessage;
    public string statusMsg
    {
        get
        {
            return statusMessage;
        }
        set
        {
            statusMessage = value;
            OnPropertyChanged("statusMsg");
        }
    }
}

在我的主窗口构造函数中:

StatusMessage testMessage = StatusMessage.GetInstance();
testMessage.statusMsg = "This is a test msg";    

我无法使文本块显示测试信息。当我通过调试监视代码时,PropertyChanged 总是为 null。有什么想法吗?


22
你应该在哪里设置DataContext与一个StatusMessage实例相关联? - Jérôme Laban
12个回答

21

谢谢Jerome!一旦我设置了DataContext,它就开始按照预期工作了!为了测试目的,我在主窗口构造函数中添加了以下内容:

 this.DataContext = testMessage;

16

今天我遇到了这个问题,浪费了一些时间,最终解决了它。希望这可以帮助节省您和其他人的时间。

如果您的事件没有订阅者,并且您只是简单地声明了事件:

public event EventHandler SomeEventHappened;

然后预期出现 null 引用。 解决方法是声明如下:

public event EventHandler SomeEventHappened = delegate { };
这将确保在调用时它不是一个null引用。
SomeEventHappened()

我看到的另一种模式是 不要 初始化为 delegate {},而是检查是否为 null:

var eventToRaise = SomeEventHappened;
if( eventToRaise != null )
{
    SomeEventHappened()
}

1
设置 this.DataContext = testMessage; 能够使其工作的原因是,根据我的回答,它有效地建立了一个订阅你希望触发的事件,因此它不再为空。 - danielpops
3
我认为“检查空值”的方法现在是首选方法。我尝试过像你在第一个情况中那样初始化事件处理程序,确实,事件不再为空,但INotifyPropertyChanged事件没有触发我正在使用的List<T>的更新。解决方法是使用INotifyCollectionChanged(参见:https://stackoverflow.com/questions/37329991/propertychanged-event-handler-always-null-in-win-phone-8-application,了解我的这个问题的经验)。 - rfreytag

10

您的OnPropertyChanged字符串必须与属性名称完全匹配,因为它区分大小写。

尝试更改

OnPropertyChanged("StatusMsg");

to

OnPropertyChanged("statusMsg");

更新:另外,我注意到您绑定到了StatusMsg(大写的'S'); 因此,控件没有绑定到属性,这也是它没有更新的另一个原因!


我按照你建议的两个更改进行了修改,Ian。但是 PropertyChanged 仍然被评估为 NULL。有点奇怪,我知道这应该可以工作!我编辑了代码以反映这些更改。 - Dave
3
@IanR 我认为它不会为空,因为那样的话, 它只是不会更新该属性 他指出它甚至没有机会更新,因为它为空。 - eran otzap

5

以防万一: 我遇到了类似的问题,但我的错误在于实现INotifyPropertyChanged的类是私有的。将其改为公共的解决了我的问题。


4

在我的情况下,这样做可以使它工作:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;    // this row fixed everything
    }
    ****
    Some code here with properties etc
    ***
}

1
另外一点 - 如果要使 PropertyChanged 为空,请确保将对象绑定到 DataContext,然后设置 Path,而不是直接将属性分配给 UI 字段。

1

我也曾经看到过当我给控件的数据绑定属性分配现有数据时,PropertyChanged事件为空的情况:

<TextBlock Name="CarTireStatus" Text="{Binding TireStatus}" >Bad Text!</TextBlock>

而这个有效:
<TextBlock Name="CarTireStatus" Text="{Binding TireStatus}" ></TextBlock>

0

为了完整地讲述故事,我还发现如果绑定属性是静态的,而PropertyChanged事件是非静态的,它将会是null。一个简单的解决方法是声明另一个静态事件,这样你就有了两个如下所示:

public event PropertyChangedEventHandler PropertyChanged;
public static event PropertyChangedEventHandler StaticPropertyChanged;

根据目标属性是否为静态,调用PropertyChanged?.Invoke()StaticPropertyChanged?.Invoke()

0

观察 PropertyChanged 事件对象为 null 时,有几个要检查的项目。

  1. 确保在引发事件时传递的属性名称与您要定位的属性名称实际匹配。

  2. 确保您仅使用包含要绑定到的属性的对象的一个实例。

对于第二项,可以通过在包含要绑定的属性的对象的类构造函数上放置断点来完成。 如果断点触发超过一次,则存在问题,并且需要将对象实例数解决为运行时通过 XAML 调用的一个实例。

因此,最好将该类实现为单例模式,以便您可以确保运行时只有一个对象实例。


3
什么,单例?什么?!我这辈子从没读过这么多垃圾。绑定系统会订阅正确实例的事件。如果不是这样,WPF 就无法正常工作。请参考任何入门级的 MVVM 文章,以获得一个可行的最佳实践演示,以证明你是多么错误。 - Gusdor
这是我从故障排除中的个人经验。在运行时,我的视图模型有多个实例。因此,这个更新对我很有效。不过还是感谢你的建议。 - Scott Nimrod
另外,请确保所有数据都已加载(即已加载事件)。 - Scott Nimrod
我认为Scott和Gusdor在谈论不同的东西。Scott在谈论ViewModel,它确实应该始终保持相同,而Gusdor在谈论Model,它可以是任意数量的对象。Scott的确启发了我解决类似情况的调试问题。谢谢。 - lznt

0
不是针对提问者,而是针对在互联网上搜索的人: 我遇到了同样的问题,我的
public event PropertyChangedEventHandler? PropertyChanged;

一直都是null!

自从我建立了一个类层次结构,我在顶级类中实现了接口“INotifyPropertyChanged”,但我忘记在类定义中指定接口:

public class PropertyChangedBase : INotifyPropertyChanged // <-- these here!

现在我在类定义中添加了INotifyPropertyChanged,并创建了PropertyChanged事件。

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