Combobox SelectedItem DataBinding NullReference异常

时间:2013-07-18 20:32:58

标签: c# wpf data-binding

我现在对combobox感到有点沮丧,希望有人能回答我的问题。问题出在SelectedItem上。当我在调试器中运行我的应用程序时,如果我在ComboBox中输入匹配项目中的项目(即.. a,b或c)的文本,则会抛出空引用异常,然后删除文本。如果我在ComboBox中输入文本并且与项目中的项目(即.. z)不匹配,然后删除文本,则不会崩溃。此行为仅在调试器中发生。如果我在外面运行应用程序,我不会崩溃。我使用的是mvvmlight,但我并不认为它与此有任何关系。我的代码在

之下

查看:

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

3 个答案:

答案 0 :(得分:26)

这是.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发生的地方,因为{{1}因为属性不是索引器,所以argsnull。它碰巧被命名为"Item"

.NET实现者应该在info上使用PropertyInfo.GetIndexParameters(),如果返回的数组不包含零元素,请确保属性是索引器。或者使用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外部不会失败的原因。

您有以下选项可以解决此问题:

  1. 将绑定到ComboBox.SelectedItem的属性重命名为"Item"以外的某个名称,以便错误的.NET实现不再假设该属性是索引器。
  2. 从项目引用中删除System.Data.dll,以便即使在Visual Studio调试器中也不会加载它。
  3. 我发现选项2更脆弱,因为可能会出现System.Data.dll必须直接由您的应用程序加载或者由其他一些加载的程序集间接加载的情况。

    所以我会选择1。

答案 1 :(得分:3)

试试这个:

  1. 写一个转换器

    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. XAML中的
  3. <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}}"/>
    

答案 2 :(得分:0)

我通过将访问修饰符设为 public 解决了这个问题,这样它就可以双向绑定。