简体   繁体   中英

Combobox SelectedItem DataBinding NullReference Exception

I am a bit frustrated with combobox right now and am hoping someone has an answer for my question. The problem is with SelectedItem. When i run my app in debugger it will throw a null reference exception if I enter text into the ComboBox that matchs an Item(ie.. a, b, or c) in Items, and then delete the text. If i enter text into the ComboBox and that does not match and Item(ie.. z) in Items and then delete the text, it does not crash. This behavior only happens within the debugger. If I run the application outside I do not crash. I'm using the mvvmlight tookit however i'm not thinking it has anything to do with that. My code is below

View:

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

Model:

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's name.
    /// </summary>
    public const string ItemsPropertyName = "Items";

    private List<Item> _items;

    /// <summary>
    /// Sets and gets the Items property.
    /// Changes to that property's 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's name.
    /// </summary>
    public const string ItemPropertyName = "Item";

    private Item _item;

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

This is a bug in .NET Framework 4 (and .NET 4.5, not in .NET 3.0 and .NET 3.5 ).

Method PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid(object item) is causing the issue.

Looked with .NET Reflector, its code looks like this:

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);
}

Issue is in the following line:

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

Code assumes that if columnName is "Item" , then property is indexer and tries to access its first argument via args[0] and this is where NullReferenceException occurs because args is null since property is not indexer. It just happens to be named "Item" .

.NET implementers should have used PropertyInfo.GetIndexParameters() on info and if returned array doesn't contain zero elements, make certain assumption that property is indexer. Or use Binding.IndexerName for checking (Binding.IndexerName has value "Item[]" ).

Why issue arises only in Visual Studio debugger is much more subtle and it is hidden in following method: PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid() .

Here is a disassembled code:

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);
}

Since item variable won't be null (it is actually an instance of WeakReference which holds MainViewModel instance), only condition for which failing method DetermineWhetherDBNullIsValid(item) is called is if System.Data.dll assembly is loaded, which is checked with AssemblyHelper.IsLoaded(UncommonAssembly.System_Data) .

Visual Studio debugger will always load System.Data.dll because project is referencing it, although it is not using it. Outside Visual Studio debugger, System.Data.dll gets loaded only if used, which is never, and that is why application doesn't fail outside Visual Studio.

You have following options to get rid of this issue:

  1. Rename property which is bound to ComboBox.SelectedItem to some name other than "Item" so that buggy .NET implementation doesn't assume anymore that property is indexer.
  2. Remove System.Data.dll from project references so it doesn't get loaded even in Visual Studio debugger.

I find option 2 more fragile, since situation may arise that System.Data.dll must be loaded, either directly by your application or indirectly by some other loaded assembly.

So I would go with option 1.

I'm able to reproduce this on my side. Add this to your combobox code:

IsTextSearchEnabled="False"

Anyways, who else is interested in this problem, the stacktrace for this exception is as follows

PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid(object item) + 0xc7 bytes
PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid() + 0x64 bytes
PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.IsDBNullValidForUpdate.get() + 0x2e bytes PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.IsDBNullValidForUpdate.get() + 0xa bytes
PresentationFramework.dll!System.Windows.Data.BindingExpression.ConvertProposedValue(object value) + 0x177 bytes
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.UpdateValue() + 0x92 bytes
PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateOverride() + 0x3d bytes
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Update() + 0x20 bytes
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.ProcessDirty() + 0x2f bytes PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Dirty() + 0x40 bytes
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.SetValue(System.Windows.DependencyObject d, System.Windows.DependencyProperty dp, object value) + 0x24 bytes
WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal) + 0x3c4 bytes
WindowsBase.dll!System.Windows.DependencyObject.SetCurrentValueInternal(System.Windows.DependencyProperty dp, object value) + 0x35 bytes
PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.UpdatePublicSelectionProperties() + 0x13f bytes
PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.SelectionChanger.End() + 0x80 bytes
PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.SelectionChanger.SelectJustThisItem(System.Windows.Controls.ItemsControl.ItemInfo info, bool assumeInItemsCollection) + 0x145 bytes
PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.OnSelectedIndexChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) + 0xd9 bytes
WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) + 0x4d bytes PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) + 0x50 bytes
WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args) + 0x3b bytes
WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) + 0x757 bytes
WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal) + 0x2ea bytes
WindowsBase.dll!System.Windows.DependencyObject.SetCurrentValueInternal(System.Windows.DependencyProperty dp, object value) + 0x35 bytes
PresentationFramework.dll!System.Windows.Controls.ComboBox.TextUpdated(string newText, bool textBoxUpdated) + 0x26e bytes
PresentationFramework.dll!System.Windows.Controls.ComboBox.OnEditableTextBoxTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) + 0x2e bytes PresentationFramework.dll!System.Windows.Controls.TextChangedEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) + 0x2c bytes
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) + 0x33 bytes
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x44 bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1a8 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x73 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) + 0x29 bytes PresentationFramework.dll!System.Windows.Controls.Primitives.TextBoxBase.OnTextChanged(System.Windows.Controls.TextChangedEventArgs e) + 0x5 bytes
PresentationFramework.dll!System.Windows.Controls.Primitives.TextBoxBase.OnTextContainerChanged(object sender, System.Windows.Documents.TextContainerChangedEventArgs e) + 0xe0 bytes
PresentationFramework.dll!System.Windows.Controls.TextBox.OnTextContainerChanged(object sender, System.Windows.Documents.TextContainerChangedEventArgs e) + 0x17d bytes PresentationFramework.dll!System.Windows.Documents.TextContainer.EndChange(bool skipEvents) + 0xb6 bytes
PresentationFramework.dll!System.Windows.Documents.TextContainer.System.Windows.Documents.ITextContainer.EndChange(bool skipEvents) + 0xb bytes PresentationFramework.dll!System.Windows.Documents.TextRangeBase.EndChange(System.Windows.Documents.ITextRange thisRange, bool disableScroll, bool skipEvents) + 0x59 bytes PresentationFramework.dll!System.Windows.Documents.TextRange.System.Windows.Documents.ITextRange.EndChange(bool disableScroll, bool skipEvents) + 0x11 bytes
PresentationFramework.dll!System.Windows.Documents.TextRange.ChangeBlock.System.IDisposable.Dispose() + 0x15 bytes
PresentationFramework.dll!System.Windows.Documents.TextEditorTyping.OnDelete(object sender, System.Windows.Input.ExecutedRoutedEventArgs args) + 0x1a7 bytes
PresentationCore.dll!System.Windows.Input.CommandBinding.OnExecuted(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) + 0x65 bytes PresentationCore.dll!System.Windows.Input.CommandManager.ExecuteCommandBinding(object sender, System.Windows.Input.ExecutedRoutedEventArgs e, System.Windows.Input.CommandBinding commandBinding) + 0x92 bytes
PresentationCore.dll!System.Windows.Input.CommandManager.FindCommandBinding(System.Windows.Input.CommandBindingCollection commandBindings, object sender, System.Windows.RoutedEventArgs e, System.Windows.Input.ICommand command, bool execute) + 0x105 bytes
PresentationCore.dll!System.Windows.Input.CommandManager.FindCommandBinding(object sender, System.Windows.RoutedEventArgs e, System.Windows.Input.ICommand command, bool execute) + 0x15e bytes PresentationCore.dll!System.Windows.Input.CommandManager.OnExecuted(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) + 0x25 bytes PresentationCore.dll!System.Windows.UIElement.OnExecutedThunk(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) + 0x46 bytes
PresentationCore.dll!System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(System.Delegate genericHandler, object target) + 0x3c bytes
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) + 0x33 bytes
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x44 bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1a8 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x73 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) + 0x3d bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) + 0x40 bytes
PresentationCore.dll!System.Windows.Input.RoutedCommand.ExecuteImpl(object parameter, System.Windows.IInputElement target, bool userInitiated) + 0x105 bytes
PresentationCore.dll!System.Windows.Input.RoutedCommand.ExecuteCore(object parameter, System.Windows.IInputElement target, bool userInitiated) + 0x59 bytes PresentationCore.dll!System.Windows.Input.CommandManager.TranslateInput(System.Windows.IInputElement targetElement, System.Windows.Input.InputEventArgs inputEventArgs) + 0x59b bytes
PresentationCore.dll!System.Windows.UIElement.OnKeyDownThunk(object sender, System.Windows.Input.KeyEventArgs e) + 0x52 bytes
PresentationCore.dll!System.Windows.Input.KeyEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) + 0x2c bytes
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) + 0x33 bytes
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x44 bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1a8 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x73 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) + 0x3d bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) + 0x40 bytes
PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() + 0x1f8 bytes
PresentationCore.dll!System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs input) + 0x45 bytes PresentationCore.dll!System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport) + 0x62 bytes
PresentationCore.dll!System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawKeyboardActions actions, int scanCode, bool isExtendedKey, bool isSystemKey, int virtualKey) + 0xee bytes 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 modifiers) + 0x94 bytes
PresentationCore.dll!System.Windows.Interop.HwndSource.OnPreprocessMessage(object param) + 0x12c bytes
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x56 bytes WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x3a bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) + 0x10e bytes WindowsBase.dll!System.Windows.Threading.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority priority, System.Delegate method, object arg) + 0x3e bytes
PresentationCore.dll!System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(ref System.Windows.Interop.MSG msg, ref bool handled) + 0x93 bytes
PresentationCore.dll!System.Windows.Interop.HwndSource.WeakEventPreprocessMessage.OnPreprocessMessage(ref System.Windows.Interop.MSG msg, ref bool handled) + 0x33 bytes
WindowsBase.dll!System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(ref System.Windows.Interop.MSG msg) + 0x3c bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0x9a bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4b bytes
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes WpfApplication1.exe!WpfApplication1.App.Main() + 0x59 bytes C# [Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x6b bytes
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x27 bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x6f bytes
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0xa7 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x16 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x41 bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes
[Native to Managed Transition]

Try This:

  1. Write a converter

     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; } } 
  2. in 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}}"/> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM