WPF在InitializeComponent期间获取控件空引用

17

在 Window 的构造函数中调用的 InitializeComponent 方法会遍历 XML 并添加控件,将它们连接到它们的事件。

因此,当其中一个控件的属性更改时,它会调用订阅该事件的方法。该方法引用了尚未构建的控件。

为什么会出现这种顺序?在 WinForms 中可以正常工作,因为事件直到所有控件创建后才触发。 是否有办法在 WPF 中强制执行这个顺序?

我看到的其他解决方案是

  • 需要在初始化后订阅事件。

  • 需要在处理控件时检查空值。


知道具体是哪些事件导致了问题会很有用 - 你能再添加一些细节吗?不过我怀疑答案可能是你提到的第一个要点... - Chris Ballard
你能解释一下更多的架构吗?我很好奇为什么一个属性会包含事件订阅。 - Scott J
6个回答

29

我也遇到了这个问题,通过在访问空控件的代码行中加入空检查解决了它。但这似乎是一种折中的解决方案。

我认为WPF试图通过在InitializeComponent()期间调用我们的Checked事件来帮助我们,以确保基于复选框的初始状态执行任何UI逻辑(例如显示/隐藏相关组件)。我测试了默认情况下未选中复选框,并且即使我将其连接到Checked和Unchecked事件,事件处理程序也不会被调用。我甚至在屏幕上有一个单个复选框的空白WPF项目中重新生成了它,并且它的行为是相同的。

这种默认行为的问题显然是其他一些组件尚未初始化。我认为WPF应该等待所有组件初始化后才默认触发Checked事件。这可能不被视为错误,但我仍然打算向相关的MSDN页面添加说明...


我确认 - 当我将单击事件处理程序更改为复选框上的已选中和未选中事件处理程序时,问题出现了。 - vinsa

5

您应该能够检查窗口的IsInitialized或IsLoaded属性,以验证它是否已完成初始化/加载。否则,您需要在代码后端中检查null或添加事件订阅(在InitializeComponent之后)。

此外,您可能可以调整如何访问元素。例如,如果您有类似以下内容:

<ListBox x:Name="listBox" SelectionChanged="OnListBoxSelectionChanged" />

然后在你的代码中,你可以通过以下几种方式获取列表框:

private void OnListBoxSelectionChanged(object sender, SelectionChangedEventArgs e) {
    ListBox lb = this.listBox; // May be null
    ListBox lb = sender as ListBox; // Should never be null
    ListBox lb = e.Source as ListBox; // Same as sender in this case
    ListBox lb = e.OriginalSource as ListBox; // Always the element that started the event (if handler is not attached directly to ListBox).
    // ... Do Something ...
}

我有一个繁忙的表单,需要不断地启用/禁用。我想我必须在初始化运行后将事件订阅移动到构造函数中。 - rediVider

5

这是与单选按钮相关的Checked事件。在xaml中删除Checked="true"后,问题消失了(虽然窗口启动时仍会被选中)。不确定出了什么问题,但至少我没有必须进行重大更改来解决它...目前为止。


谢谢rediVider,你的解决方案对我很有帮助(我使用的是.NET 4.5.1)。 - Ashish Singh

1
我遇到了同样的问题,我认为这是一个 bug。不过我找到了一个解决办法: 我在 Xaml 中移除了 "Ischecked",然后在代码中初始化后设置它。

0

任何控件使用双向数据绑定吗?我遇到了这个问题,我将文本框绑定到ViewModel上的属性。ViewModel初始化触发了INotifyPropertyChanged,导致绑定的控件触发了文本框的TextChanged事件。我的短期解决方法是将事件订阅移动到窗口Loaded事件中,但像你所说,这有点麻烦。我需要重构代码以更改对象初始化的顺序,使得WPF视图(即窗口和用户控件)在ViewModel之前创建。然后我就可以将事件处理程序注册移到XAML中。


我没有使用任何编译时数据绑定。 - rediVider

0

窗口控件在初始化可选中的子控件并将其设置为选中状态时触发Checked事件。

我强烈认为这是一个错误。仅仅因为它从MFC程序集深处触发了NullReferenceException异常,就足以说明这是意外行为。

考虑到Xaml编辑器会在您的Control部分类中创建处理程序函数,如果该类尚未完成构造,则无法处理事件。我认为对于您设置为已初始化为选中状态的控件来说,触发Checked事件似乎并不正确。

我的意思是,如果您将其初始状态设置为未选中,它是否应该触发Unchecked事件?


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