Комбинация SelectedItem DataBinding NullReference Exception

Сейчас я немного расстроен с помощью combobox и надеюсь, что у кого-то есть ответ на мой вопрос. Проблема заключается в SelectedItem. Когда я запускаю свое приложение в отладчике, он будет генерировать исключение с ссылкой на null, если я введу текст в ComboBox, который соответствует элементу (то есть.. a, b или c) в элементах, а затем удалит текст. Если я ввожу текст в ComboBox, и это не соответствует, а Item (ie.. z) в Items, а затем удаляет текст, он не падает. Такое поведение происходит только в отладчике. Если я запустил приложение за пределами, я не сбой. Я использую mvvmlight takeit, но я не думаю, что это имеет к этому какое-то отношение. Мой код ниже

Вид:

<ComboBox IsEditable="True"
              VerticalAlignment="Top"
              ItemsSource="{Binding Items}"
              DisplayMemberPath="Name"
              SelectedItem="{Binding Item,Mode=TwoWay}"/>

Модель:

public class Item
{
    public string Name { get; set; }
    public int Id { get; set; }
}

VM:

public MainViewModel()
    {
        Items = new List<Item>
          {
            new Item {Name="a", Id=0},
            new Item {Name="b", Id=1},
            new Item {Name="c", Id=2},
          };
    }

    /// <summary>
    /// The <see cref="Items" /> property name.
    /// </summary>
    public const string ItemsPropertyName = "Items";

    private List<Item> _items;

    /// <summary>
    /// Sets and gets the Items property.
    /// Changes to that property value raise the PropertyChanged event. 
    /// </summary>
    public List<Item> Items
    {
        get
        {
            return _items;
        }
        set
        {
            Set(ItemsPropertyName, ref _items, value);
        }
    }

    /// <summary>
    /// The <see cref="Item" /> property name.
    /// </summary>
    public const string ItemPropertyName = "Item";

    private Item _item;

    /// <summary>
    /// Sets and gets the Item property.
    /// Changes to that property value raise the PropertyChanged event. 
    /// </summary>
    public Item Item
    {
        get
        {
            return _item;
        }
        set
        {
            Set(ItemPropertyName, ref _item, value);
        }
    }

Ответы

Ответ 1

Это ошибка в .NET Framework 4 (и .NET 4.5, не в .NET 3.0 и .NET 3.5).

Метод PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid(object item) вызывает проблему.

Посмотрев на .NET Reflector, его код выглядит так:

private bool DetermineWhetherDBNullIsValid(object item)
{
    PropertyInfo info;
    PropertyDescriptor descriptor;
    DependencyProperty property;
    DynamicPropertyAccessor accessor;
    this.SetPropertyInfo(this._arySVS[this.Length - 1].info, out info, out descriptor, out property, out accessor);
    string columnName = (descriptor != null) ? descriptor.Name : ((info != null) ? info.Name : null);
    object arg = ((columnName == "Item") && (info != null)) ? this._arySVS[this.Length - 1].args[0] : null;
    return SystemDataHelper.DetermineWhetherDBNullIsValid(item, columnName, arg);
}

Проблема находится в следующей строке:

object arg = ((columnName == "Item") && (info != null)) ? this._arySVS[this.Length - 1].args[0] : null;

Код предполагает, что если columnName является "Item", тогда свойство является индексом и пытается получить доступ к его первому аргументу через args[0], и это происходит там, где NullReferenceException происходит, потому что args есть null, поскольку свойство не индексатор. Это просто называется "Item".

Разработчики .NET должны были использовать PropertyInfo.GetIndexParameters() в info, и если возвращенный массив не содержит нулевых элементов, сделайте определенные предположение, что свойство является индексатором. Или используйте Binding.IndexerName для проверки (Binding.IndexerName имеет значение "Item[]").

Почему выдача возникает только в отладчике Visual Studio, гораздо более тонкая и скрыта в следующем методе: PresentationFramework.dll! MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid().

Вот разобранный код:

private void DetermineWhetherDBNullIsValid()
{
    bool flag = false;
    object item = this.GetItem(this.Length - 1);
    if ((item != null) && AssemblyHelper.IsLoaded(UncommonAssembly.System_Data))
    {
        flag = this.DetermineWhetherDBNullIsValid(item);
    }
    this._isDBNullValidForUpdate = new bool?(flag);
}

Так как переменная item не будет нулевой (это фактически экземпляр WeakReference, который содержит экземпляр MainViewModel), вызывается только условие, для которого вызывается метод отказа DetermineWhetherDBNullIsValid(item), если загружена сборка System.Data.dll, которая проверяется с помощью AssemblyHelper.IsLoaded(UncommonAssembly.System_Data).

Отладчик Visual Studio всегда будет загружать System.Data.dll, потому что проект ссылается на него, хотя он не использует его. Вне отладчика Visual Studio System.Data.dll загружается только в том случае, если он используется, а это никогда не происходит, и поэтому приложение не выходит за пределы Visual Studio.

У вас есть следующие варианты, чтобы избавиться от этой проблемы:

  • Переименуйте свойство, которое привязано к ComboBox.SelectedItem к некоторому имени, отличному от "Item", так что реализация buggy.NET не предполагает, что это свойство является индексом.
  • Удалите System.Data.dll из ссылок на проекты, чтобы он не загружался даже в отладчике Visual Studio.

Я нахожу вариант 2 более хрупким, так как может возникнуть ситуация, когда файл System.Data.dll должен быть загружен либо напрямую вашим приложением, либо косвенно с помощью другой загруженной сборки.

Итак, я бы пошел с вариантом 1.

Ответ 2

Я могу воспроизвести это на моей стороне. Добавьте это в свой код со списком:

IsTextSearchEnabled="False"

В любом случае, кто еще интересуется этой проблемой, stacktrace для этого исключения выглядит следующим образом

PresentationFramework.dll! MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid(объект объекта) + 0xc7 байт
    PresentationFramework.dll! MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid() + 0x64 байт
    PresentationFramework.dll! MS.Internal.Data.PropertyPathWorker.IsDBNullValidForUpdate.get() + 0x2e байт     PresentationFramework.dll! MS.Internal.Data.ClrBindingWorker.IsDBNullValidForUpdate.get() + 0xa байт
    PresentationFramework.dll! System.Windows.Data.BindingExpression.ConvertProposedValue(значение объекта) + 0x177 байт
    PresentationFramework.dll! System.Windows.Data.BindingExpressionBase.UpdateValue() + 0x92 байт
    PresentationFramework.dll! System.Windows.Data.BindingExpression.UpdateOverride() + 0x3d байт
    PresentationFramework.dll! System.Windows.Data.BindingExpressionBase.Update() + 0x20 байт
    PresentationFramework.dll! System.Windows.Data.BindingExpressionBase.ProcessDirty() + 0x2f байт     PresentationFramework.dll! System.Windows.Data.BindingExpressionBase.Dirty() + 0x40 байт
    PresentationFramework.dll! System.Windows.Data.BindingExpressionBase.SetValue(System.Windows.DependencyObject d, System.Windows.DependencyProperty dp, значение объекта) + 0x24 байт
    WindowsBase.dll! System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, значение объекта, метаданные System.Windows.PropertyMetadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal) + 0x3c4 bytes
    WindowsBase.dll! System.Windows.DependencyObject.SetCurrentValueInternal(System.Windows.DependencyProperty dp, значение объекта) + 0x35 байт
    PresentationFramework.dll! System.Windows.Controls.Primitives.Selector.UpdatePublicSelectionProperties() + 0x13f байт
    PresentationFramework.dll! System.Windows.Controls.Primitives.Selector.SelectionChanger.End() + 0x80 байт
    PresentationFramework.dll! System.Windows.Controls.Primitives.Selector.SelectionChanger.SelectJustThisItem(System.Windows.Controls.ItemsControl.ItemInfo info, bool acceptInItemsCollection) + 0x145 байт
    PresentationFramework.dll! System.Windows.Controls.Primitives.Selector.OnSelectedIndexChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) + 0xd9 байт
    WindowsBase.dll! System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) + 0x4d байт     PresentationFramework.dll! System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) + 0x50 байт
    WindowsBase.dll! System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args) + 0x3b байт
    WindowsBase.dll! System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, метаданные System.Windows.PropertyMetadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) + 0x757 байтов
    WindowsBase.dll! System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, значение объекта, метаданные System.Windows.PropertyMetadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal) + 0x2ea bytes
    WindowsBase.dll! System.Windows.DependencyObject.SetCurrentValueInternal(System.Windows.DependencyProperty dp, значение объекта) + 0x35 байт
    PresentationFramework.dll! System.Windows.Controls.ComboBox.TextUpdated(строка newText, bool textBoxUpdated) + 0x26e байт
    PresentationFramework.dll! System.Windows.Controls.ComboBox.OnEditableTextBoxTextChanged(отправитель объекта, System.Windows.Controls.TextChangedEventArgs e) + 0x2e байт     PresentationFramework.dll! System.Windows.Controls.TextChangedEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) + 0x2c байты
    PresentationCore.dll! System.Windows.RoutedEventArgs.InvokeHandler(обработчик System.Delegate, объект-объект) + 0x33 байт
    PresentationCore.dll! System.Windows.RoutedEventHandlerInfo.InvokeHandler(объект target, System.Windows.RoutedEventArgs routedEventArgs) + 0x44 байт
    PresentationCore.dll! System.Windows.EventRoute.InvokeHandlersImpl(источник объекта, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1a8 байт
    PresentationCore.dll! System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject отправитель, System.Windows.RoutedEventArgs args) + 0x73 байт
    PresentationCore.dll! System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) + 0x29 байт     PresentationFramework.dll! System.Windows.Controls.Primitives.TextBoxBase.OnTextChanged(System.Windows.Controls.TextChangedEventArgs e) + 0x5 байт
    PresentationFramework.dll! System.Windows.Controls.Primitives.TextBoxBase.OnTextContainerChanged(отправитель объекта, System.Windows.Documents.TextContainerChangedEventArgs e) + 0xe0 байт
    PresentationFramework.dll! System.Windows.Controls.TextBox.OnTextContainerChanged(отправитель объекта, System.Windows.Documents.TextContainerChangedEventArgs e) + 0x17d байт     PresentationFramework.dll! System.Windows.Documents.TextContainer.EndChange(bool skipEvents) + 0xb6 байт
    PresentationFramework.dll! System.Windows.Documents.TextContainer.System.Windows.Documents.ITextContainer.EndChange(bool skipEvents) + 0xb байт     PresentationFramework.dll! System.Windows.Documents.TextRangeBase.EndChange(System.Windows.Documents.ITextRange thisRange, bool disableScroll, bool skipEvents) + 0x59 байт     PresentationFramework.dll! System.Windows.Documents.TextRange.System.Windows.Documents.ITextRange.EndChange(bool disableScroll, bool skipEvents) + 0x11 байт
    PresentationFramework.dll! System.Windows.Documents.TextRange.ChangeBlock.System.IDisposable.Dispose() + 0x15 байт
    PresentationFramework.dll! System.Windows.Documents.TextEditorTyping.OnDelete(объектный отправитель, System.Windows.Input.ExecutedRoutedEventArgs args) + 0x1a7 байт
    PresentationCore.dll! System.Windows.Input.CommandBinding.OnExecuted(отправитель объекта, System.Windows.Input.ExecutedRoutedEventArgs e) + 0x65 байт     PresentationCore.dll! System.Windows.Input.CommandManager.ExecuteCommandBinding(отправитель объекта, System.Windows.Input.ExecutedRoutedEventArgs e, System.Windows.Input.CommandBinding commandBinding) + 0x92 байт
    PresentationCore.dll! System.Windows.Input.CommandManager.FindCommandBinding(System.Windows.Input.CommandBindingCollection commandBindings, отправитель объекта, System.Windows.RoutedEventArgs e, System.Windows.Input.ICommand command, bool execute) + 0x105 bytes
    PresentationCore.dll! System.Windows.Input.CommandManager.FindCommandBinding(отправитель объекта, System.Windows.RoutedEventArgs e, System.Windows.Input.ICommand command, bool execute) + 0x15e байт     PresentationCore.dll! System.Windows.Input.CommandManager.OnExecuted(отправитель объекта, System.Windows.Input.ExecutedRoutedEventArgs e) + 0x25 байт     PresentationCore.dll! System.Windows.UIElement.OnExecutedThunk(отправитель объекта, System.Windows.Input.ExecutedRoutedEventArgs e) + 0x46 байт
    PresentationCore.dll! System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(System.Delegate genericHandler, цель объекта) + 0x3c байтов
    PresentationCore.dll! System.Windows.RoutedEventArgs.InvokeHandler(обработчик System.Delegate, объект-объект) + 0x33 байт
    PresentationCore.dll! System.Windows.RoutedEventHandlerInfo.InvokeHandler(объект target, System.Windows.RoutedEventArgs routedEventArgs) + 0x44 байт
    PresentationCore.dll! System.Windows.EventRoute.InvokeHandlersImpl(источник объекта, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1a8 байт
    PresentationCore.dll! System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject отправитель, System.Windows.RoutedEventArgs args) + 0x73 байт
    PresentationCore.dll! System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) + 0x3d байт
    PresentationCore.dll! System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) + 0x40 байт
    PresentationCore.dll! System.Windows.Input.RoutedCommand.ExecuteImpl(параметр объекта, объект System.Windows.IInputElement, bool userInitiated) + 0x105 байт
    PresentationCore.dll! System.Windows.Input.RoutedCommand.ExecuteCore(объект-параметр, System.Windows.IInputElement target, bool userInitiated) + 0x59 байт     PresentationCore.dll! System.Windows.Input.CommandManager.TranslateInput(System.Windows.IInputElement targetElement, System.Windows.Input.InputEventArgs inputEventArgs) + 0x59b байт
    PresentationCore.dll! System.Windows.UIElement.OnKeyDownThunk(отправитель объекта, System.Windows.Input.KeyEventArgs e) + 0x52 байт
    PresentationCore.dll! System.Windows.Input.KeyEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) + 0x2c байты
    PresentationCore.dll! System.Windows.RoutedEventArgs.InvokeHandler(обработчик System.Delegate, объект-объект) + 0x33 байт
    PresentationCore.dll! System.Windows.RoutedEventHandlerInfo.InvokeHandler(объект target, System.Windows.RoutedEventArgs routedEventArgs) + 0x44 байт
    PresentationCore.dll! System.Windows.EventRoute.InvokeHandlersImpl(источник объекта, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1a8 байт
    PresentationCore.dll! System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject отправитель, System.Windows.RoutedEventArgs args) + 0x73 байт
    PresentationCore.dll! System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) + 0x3d байт
    PresentationCore.dll! System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) + 0x40 байт
    PresentationCore.dll! System.Windows.Input.InputManager.ProcessStagingArea() + 0x1f8 байт
    PresentationCore.dll! System.Windows.Input.InputManager.ProcessInput(вход System.Windows.Input.InputEventArgs) + 0x45 байт     PresentationCore.dll! System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport) + 0x62 байт
    PresentationCore.dll! System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawKeyboardActions action, int scanCode, bool isExtendedKey, bool isSystemKey, int virtualKey) + 0xee байт     PresentationCore.dll! System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(ref System.Windows.Interop.MSG msg, ref bool handled) + 0xac bytes
    PresentationCore.dll! System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(ref System.Windows.Interop.MSG msg, Модификаторы System.Windows.Input.ModifierKeys) + 0x94 байт
    PresentationCore.dll! System.Windows.Interop.HwndSource.OnPreprocessMessage(параметр объекта) + 0x12c байт
    WindowsBase.dll! System.Windows.Threading.ExceptionWrapper.InternalRealCall(обратный вызов System.Delegate, object args, int numArgs) + 0x56 байт     WindowsBase.dll! MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(источник объекта, метод System.Delegate, object args, int numArgs, System.Delegate catchHandler) + 0x3a байт
    WindowsBase.dll! System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate, object args, int numArgs) + 0x10e байт     WindowsBase.dll! System.Windows.Threading.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority priority, System.Delegate method, object arg) + 0x3e байты
    PresentationCore.dll! System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(ref System.Windows.Interop.MSG msg, ref bool обработано) + 0x93 байт
    PresentationCore.dll! System.Windows.Interop.HwndSource.WeakEventPreprocessMessage.OnPreprocessMessage(ref System.Windows.Interop.MSG msg, ref bool обработано) + 0x33 байт
    WindowsBase.dll! System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(ref System.Windows.Interop.MSG msg) + 0x3c байты
    WindowsBase.dll! System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame) + 0x9a байт
    WindowsBase.dll! System.Windows.Threading.Dispatcher.PushFrame(файл System.Windows.Threading.DispatcherFrame) + 0x49 байт
    WindowsBase.dll! System.Windows.Threading.Dispatcher.Run() + 0x4b байт
    PresentationFramework.dll! System.Windows.Application.RunDispatcher(игнорирование объекта) + 0x17 байт
    PresentationFramework.dll! System.Windows.Application.RunInternal(окно System.Windows.Window) + 0x6f байт     PresentationFramework.dll! System.Windows.Application.Run(окно System.Windows.Window) + 0x26 байт     PresentationFramework.dll! System.Windows.Application.Run() + 0x1b байт     WpfApplication1.exe! WpfApplication1.App.Main() + 0x59 байтов С#     Переход к управляемому переходному процессу

    [Управляемый для коренного перехода]
    mscorlib.dll! System.AppDomain.ExecuteAssembly(строка assemblyFile, System.Security.Policy.Evidence assemblySecurity, string [] args) + 0x6b байт
    Microsoft.VisualStudio.HostingProcess.Utilities.dll! Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x27 байт
    mscorlib.dll! System.Threading.ThreadHelper.ThreadStart_Context (состояние объекта) + 0x6f байт
    mscorlib.dll! System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executeContext, System.Threading.ContextCallback callback, состояние объекта, bool preserveSyncCtx) + 0xa7 байт
    mscorlib.dll! System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executeContext, System.Threading.ContextCallback callback, состояние объекта, bool preserveSyncCtx) + 0x16 байт
    mscorlib.dll! System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executeContext, System.Threading.ContextCallback callback, состояние объекта) + 0x41 байт
    mscorlib.dll! System.Threading.ThreadHelper.ThreadStart() + 0x44 байт
    [Отправлено к управляемому переходному процессу]

Ответ 3

Попробуйте следующее:

  • Записать конвертер

    public class NullToItemConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null)
                return new Item();
            else
                return value;
        }
    }
    
  • в XAML

    <Window.Resources>
        <local:NullToItemConverter x:Key="nullToItemConverter"/>
    </Window.Resources
    

    ...

    <ComboBox IsEditable="True"
          VerticalAlignment="Top"
          ItemsSource="{Binding Items}"
          DisplayMemberPath="Name"
          SelectedItem="{Binding Item, Mode=TwoWay , Converter={StaticResource nullToItemConverter}}"/>