WPF - isEnabled 出现 NullReferenceException

5

我是WPF的新手,在过去我使用过Windows Forms。我这里遇到了一个问题,希望有人能够为我解释一下。以下是一个非常简单的示例。

我有一个XAML页面,在上面有一个复选框、一个按钮和一个文本字段。默认情况下复选框是选中的。

当复选框未选中时,我想启用按钮和文本字段,例如:

private void UseDefaultFoldersCB_Checked(object sender, RoutedEventArgs e)
{
      //MessageBox.Show("");
      if (StartDirLocationTB.IsEnabled == false)
      {
           StartDirLocationTB.IsEnabled = true;
      }

      if (SelectStartLocationBtn.IsEnabled == false)
      {
            SelectStartLocationBtn.IsEnabled = true;
      }
}

XAML:

<CheckBox Content="Use Default Folders" IsChecked="True" Height="16" HorizontalAlignment="Left" Margin="10,14,0,0" Name="UseDefaultFoldersCB" VerticalAlignment="Top" Checked="UseDefaultFoldersCB_Checked" />
<TextBox Height="23" IsEnabled="False" HorizontalAlignment="Left" Margin="9,38,0,0" Name="StartDirLocationTB" VerticalAlignment="Top" Width="403" Background="WhiteSmoke" />
<Button Content="Select Start Folder" IsEnabled="False" Height="23" HorizontalAlignment="Right" Margin="0,38,6,0" Name="SelectStartLocationBtn" VerticalAlignment="Top" Width="139" />

堆栈跟踪:

系统.NullReferenceException未被 用户代码处理
消息=对象引用未设置到对象的实例。
来源=TestProject 堆栈跟踪: at TestProject.MainWindow.UseDefaultFoldersCB_Checked(Object sender, RoutedEventArgs e) in C:\Users\jc\Desktop\Test\TestProject\MainWindow.xaml.cs:line 611 at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.Controls.Primitives.ToggleButton.OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember property, Object value)

当我从Visual Studio启动应用程序时,会在上述代码中收到NullReferenceException。为什么这段代码会在应用程序启动时执行?我本以为它只会在复选框被选中/取消选中时执行?为什么会出现NullReferenceException?

谢谢。


1
在代码块中,异常确切发生在哪里? - BoltClock
如果(StartDirLocationTB.IsEnabled == false) - 如果我添加注释中的 'MessageBox.Show..' - 那也会显示 - 我的问题是为什么这段代码在应用程序启动时执行。这似乎是WinForms / WPF之间的根本区别。 - Jimmy Collins
可以同时包含相关的XAML部分吗? - Bubblewrap
为什么是-1?我觉得我的解释很清楚了吧? - Jimmy Collins
1
这是。看到那个踩的评价也让我感到困惑。 - BoltClock
4个回答

6
答案就是不要像在WinForms中一样编码,而应使用数据绑定。我为您找到了一个不错的示例这里。如果您仍然想在事件处理程序中完成它,请将其保留在“Checked”事件中,并添加空值检查。如果您使用Click事件,键盘快捷键将不起作用。空值检查示例:
private void UseDefaultFoldersCB_Checked(object sender, RoutedEventArgs e)
{
    if (StartDirLocationTB != null && StartDirLocationTB.IsEnabled == false)
    {
         StartDirLocationTB.IsEnabled = true;
    }

    if (SelectStartLocationBtn != null && SelectStartLocationBtn.IsEnabled == false)
    {
         SelectStartLocationBtn.IsEnabled = true;
    }
}

4
你接收到此事件处理程序的原因是,当初始化页面时,XAML解析器看到一个事件附加到复选框的Checked属性(即每当复选框的IsChecked属性为true时都会调用此事件),因此在加载时调用该事件。
我建议您使用复选框的Click事件,这样每当复选框状态改变时,就会触发一个事件。
因此,你的XAML将类似于这样。
<CheckBox Content="Use Default Folders" IsChecked="True" Height="16" HorizontalAlignment="Left" Margin="10,14,0,0" Name="UseDefaultFoldersCB" VerticalAlignment="Top" Click="UseDefaultFoldersCB_Click" />
<TextBox Height="23" IsEnabled="False" HorizontalAlignment="Left" Margin="9,38,0,0" Name="StartDirLocationTB" VerticalAlignment="Top" Width="403" Background="WhiteSmoke" />
<Button Content="Select Start Folder" IsEnabled="False" Height="23" HorizontalAlignment="Right" Margin="0,38,6,0" Name="SelectStartLocationBtn" VerticalAlignment="Top" Width="139" />

而Eventhandler代码保持不变,如下所示...
private void UseDefaultFoldersCB_Click(object sender, RoutedEventArgs e)
{
      //MessageBox.Show("");
      if (StartDirLocationTB.IsEnabled == false)
      {
           StartDirLocationTB.IsEnabled = true;
      }

      if (SelectStartLocationBtn.IsEnabled == false)
      {
            SelectStartLocationBtn.IsEnabled = true;
      }
}

1
这不是一个好的答案,因为它挂钩了错误的事件,无论是对于键盘用户还是许多UI测试框架-使用数据绑定或仅检查null。 - Ian Ringrose
@Sumit,因为代码逻辑是关于“复选框未选中”,而不是用户使用鼠标进行的操作。 - Ian Ringrose
@Ian:这个点击事件是针对复选框的,如果用户点击除了复选框以外的任何地方,这个事件就不会被触发,对吧?实际上,这个事件充当了复选框状态变化事件的作用,在复选框被选中和取消选中时都会被触发。因此,这两种情况都可以在单个事件中处理,我认为这比分别处理已选中和未选中的两个事件要好。 - Sumit
@Sumit - @Ian 是指当用户使用键盘选中/取消复选框时会发生什么。但是你的代码中没有任何反应。 - Robert Jeppesen

3

XAML中的属性设置器会引起与这些属性变化相关的事件被触发,就像在代码中设置一样(或多或少)。我相信在XAML解析器通过IsChecked="True"设置属性时,事件处理程序会触发 - 在此时,您在XAML中定义的其他对象尚未实例化。

顺便提一下,这是Silverlight和WPF在细节上常常有所不同的地方之一。


1

我认为WPF中的XAML解析器无法保证连接不同属性和事件处理程序的顺序。 我认为在您的情况下,首先它会连接您的UseDefaultFoldersCB_Checked处理程序,然后将IsChecked设置为true,这将触发一个事件。

如果发生异常时,您可以通过提供调用堆栈来扩展您的问题。


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