考虑以下代码:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Slider ValueChanged="slider_ValueChanged/>
<TextBox x:Name="counter"/>
</StackPanel>
</Window>
和
namespace Project1
{
public partial class Window1 : Window
{
public MainWindow() { InitializeComponent(); }
void slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
counter.Text = e.NewValue.ToString();
}
}
}
Slider将在初始化期间引发其ValueChanged
事件,而counter
仍为null
。
这是我在使用WPF时遇到的一个更大问题的例子,即UI事件可以随时触发,并且没有一个地方可以放我的初始化代码,这样它就可以保证在WPF系统拥有的所有指针初始化之后,但在任何UI事件触发之前运行。
处理此问题最优雅的方法是什么这个特定的例子应该使用数据绑定,这一点无关紧要。
根据您的情况,有很多方法可以处理
首先,您可以简单地识别对象可能未初始化的事实,并在处理之前进行检查。例如,
if (counter.Text != null)
counter.Text = e.NewValue.ToString();
其次,您可以将事件附加到对象的Loaded事件中,这样它们在对象初始化之前不会激发。
void Counter_Loaded(object sender, EventArgs e)
{
slider.ValueChanged += Slider_ValueChanged;
}
void Counter_Unloaded(object sender, EventArgs e)
{
slider.ValueChanged -= Slider_ValueChanged;
}
最后,您可以使用WPF的Dispatcher以不同的DispatcherPriority在UI线程上运行事件。默认为Normal
,在Loaded
、Render
和DataBind
操作之后运行
Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
new Action(delegate() { counter.Text = e.NewValue.ToString(); }));
这个问题的true答案是使用MVVM模式,其中文件后面的窗口代码几乎不包含初始化代码。
在这种模式中,UI仅通过数据绑定连接到代码的其余部分。您编写了实现INotifyPropertyChanged
的特殊视图模型类,并将业务逻辑作为UI绑定的一系列属性公开
当然,您可以完全控制视图模型的初始化方式。