Исключение WPF — NullReferenceException при IsEnabled

#c# #wpf #isenabled

#c# #wpf #isenabled

Вопрос:

Я новичок в 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" />
  

Трассировка стека:

Исключение System.NullReferenceException не было обработано пользовательским кодом
Сообщение = Ссылка на объект не установлена для экземпляра объекта.
Источник= Трассировка стека TestProject: в TestProject.Главное окно.UseDefaultFoldersCB_Checked (отправитель объекта, RoutedEventArgs e) в C:UsersjcDesktopTestTestProjectMainWindow.xaml.cs:line 611 в системе.Windows.Маршрут события.InvokeHandlersImpl (источник объекта, аргументы RoutedEventArgs, логическое значение повторно вызывается) в системе.Windows.Пользовательский интерфейс.Поднимите Eventimpl (отправитель DependencyObject, аргументы RoutedEventArgs) в системе.Windows.Элементы управления.Примитивы.Кнопка переключения.OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) в системе.Windows.Объект зависимости.OnPropertyChanged(DependencyPropertyChangedEventArgs e) в системе.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) в системе.Windows.DependencyObject.NotifyPropertyChange(аргументы DependencyPropertyChangedEventArgs) в системе.Windows.Объект зависимости.Обновите эффективное значение (EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntryamp; newEntry, логическое принудительное значение с deferredreference, логическое принудительное значение с currentValue, operationType operationType) в системе.Windows.DependencyObject.SetValueCommon (DependencyProperty dp, значение объекта, метаданные PropertyMetadata, логическая принудительная ссылка с заданной ссылкой, логическая принудительная ссылка с текущим значением, operationType operationType, Логическая внутренняя) в системе.Windows.DependencyObject.setValue(DependencyProperty dp, значение объекта) в MS Internal.Xaml.Runtime.ClrObjectRuntime.setValue(объект inst, свойство XamlMember, значение объекта)

Когда я запускаю приложение из Visual Studio, я получаю исключение NullReferenceException в приведенном выше коде. Почему этот код выполняется при запуске приложения? Я бы подумал, что это будет выполняться только тогда, когда флажок установлен / снят? Почему исключение NullReferenceException?

Спасибо.

Комментарии:

1. Где именно в блоке кода возникает исключение?

2. если (StartDirLocationTB. IsEnabled == false) — Если я добавлю в закомментированное ‘MessageBox.Show ..’ — которое также отображается — мой вопрос в том, почему этот код выполняется при запуске приложения. Похоже, это фундаментальное различие между WinForms / WPF.

3. Может также включать соответствующие биты xaml?

4. Почему -1, я думал, мое объяснение было довольно ясным??

5. Это так. Увидев отрицательный результат, я тоже почесал голову.

Ответ №1:

Ответ заключается в том, чтобы прекратить кодирование, как если бы вы работали в WinForms. Вместо этого используйте привязку данных. Я нашел для вас хороший образец здесь. Если вы все еще хотите сделать это в обработчиках событий, сохраните это в событии ‘Checked’ и просто добавьте нулевые проверки. Если вы используете событие Click, сочетания клавиш не будут работать. Пример проверки на нуль:

 private void UseDefaultFoldersCB_Checked(object sender, RoutedEventArgs e)
{
    if (StartDirLocationTB != null amp;amp; StartDirLocationTB.IsEnabled == false)
    {
         StartDirLocationTB.IsEnabled = true;
    }

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

Ответ №2:

Причина, по которой вы получаете вызов этого обработчика событий, заключается в том, что при инициализации страницы XAML pareser видит, что событие прикреплено к атрибуту Checked флажка (т. Е. Это событие будет вызвано, когда любое свойство checkbox IsChecked имеет значение true), таким образом, оно вызывает событие во время самой загрузки.

Я предлагаю вам использовать событие щелчка флажка, чтобы вы могли получать событие всякий раз, когда изменяется состояние флажка.

Таким образом, ваш 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" />
  

и код обработчика событий остается таким же, как..

 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. Это не очень хороший ответ, поскольку он перехватывает неправильное событие, как для пользователей клавиатуры, так и для многих фреймворков тестирования пользовательского интерфейса — используйте привязку данных или просто проверьте значение null.

2. @Sumit, потому что логика кода заключается в том, что «флажок снят», а не в том, что пользователь делает с помощью мыши.

3. @Ian: это событие щелчка имеет флажок, если пользователь нажимает в любом другом месте, кроме флажка, это событие не будет запущено правильно.. Фактически это событие служит событием изменения статуса флажка, которое будет запускаться оба раза, когда флажок установлен и когда флажок снят. Таким образом, оба случая могут быть обработаны в одном событии, что, я думаю, лучше, чем обрабатывать эти два случая в двух разных событиях Checked и UnChecked.

4. @Sumit — @Ian ссылается на то, что происходит, когда пользователь устанавливает / снимает флажок с помощью клавиатуры. С вашим кодом ничего не происходит.

Ответ №3:

Установщики свойств в XAML вызовут события, связанные с изменением этих свойств, как если бы они были установлены в коде (более или менее). Я полагаю, что в тот момент, когда анализатор XAML устанавливает свойство через ваш IsChecked = «True», срабатывает обработчик событий — и в этот момент другие ваши объекты, определенные в XAML, не были созданы.

(Кстати, это одно из тех мест, где Silverlight и WPF, как правило, отличаются в деталях).

Ответ №4:

Я не думаю, что анализатор XAML в WPF гарантирует порядок, в котором будут подключены различные свойства и обработчики событий. Я думаю, что в вашем случае сначала он присоединяет ваш UseDefaultFoldersCB_Checked обработчик, а затем устанавливает IsChecked значение true, которое запускает событие.

Вы можете немного расширить свой вопрос, предоставив стек вызовов при возникновении исключения.