繁体   English   中英

具有ListView SelectedItem的属性和TextBox绑定的ArgumentException

[英]ArgumentException with ListView SelectedItem's property and TextBox binding

编辑:

我发现此问题的根源是BarEquals()GetHashCode()实现。

特别是参与GetHashCode()并绑定到TextBox的属性(如Bar Name )。 在删除这些覆盖方法时,一切正常(除了我想保留它们)

我不明白为什么会这样?


我有一个TextBoxListView和一些带有以下ViewModel的数据绑定:

[PropertyChanged.ImplementPropertyChanged]
public class ViewModel
{
    public ObservableCollection<Foo> Foos { get; set; }

    public Foo SelectedFoo { get; set; }
}

[PropertyChanged.ImplementPropertyChanged]
public class Foo
{
    public Bar FooBar { get; set; }
}

[PropertyChanged.ImplementPropertyChanged]
public class Bar
{
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as Bar;

        if (other != null)
        {
            return other.Name == Name;
        }

        return false;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}

这是我的清单:

<ListView x:Name="V_List" SelectedItem="{Binding SelectedFoo}"  ItemsSource="{Binding Path=Foos}" SelectionMode="Single">
    ...
</ListView>

这是我的TextBox:

<TextBox Text="{Binding Path=SelectedFoo.FooBar.Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />

这是东西:

当我从List选择第一个Foo ,绑定起作用,并且所选FooFooBar属性的Name出现在TextBox 无论我更改选择多少次,相应的值都会出现在TextBox

但是现在,如果我使用TextBox更改Name (在失去焦点之后,由于TwoWay数据绑定,该Name正在起作用,并通过Debugging进行了检查),然后从列表中更改选择,则TextBox仍显示先前选择的项目的值。

此外,再次选择相同的项目,然后再选择其他一些项目时,我得到以下异常(令人惊讶的是,调试器没有报告,我不得不将其记录到文件中。可能是因为异常不是从我的代码。)

这是日志:

异常为:-Exception :: System.ArgumentException:已经添加了具有相同键的项。 在System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add) at System.Collections.Generic.Dictionary (ExceptionResource resource)处在System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add) at System.Collections.Generic.Dictionary 2.ctor(IDictionary 2 dictionary, IEqualityComparer 1比较器),位于System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple(),位于System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple(),位于System.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage..ctor(InternalSelectedItemsStorage集合,IEqualityComparer`1 equalComparer)。 System.Windows.Controls.Primitives.Selector.SetSelectedHelper(对象项,FrameworkElement UI,布尔值选择)处的.Selector.SelectionChanger.End()(System.Windows.Controls.Primitives.Selector.NotifyIsSelectedChanged(FrameworkElement容器,已选择布尔值,RoutedEventArgs) e)位于System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target,对象发件人,RoutedEventArgs e)处,位于System.Windows.Controls.Primitives.Selector.OnSelected(Object sender,RoutedEventArgs e) System.Windows.EventRoute.InvokeHandlersImpl(Object source,RoutedEventArgs args,Boolean reRaised)at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender,RoutedEventArgs args)at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e) .Windows.Controls.ListBoxItem.OnSelected(RoutedEventArgs e)
在System.Windows.Controls.ListBoxItem.OnIsSelectedChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)在System.Windows.DependworkObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)在System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEvent。 System.Windows.DependencyObject.UpdateEffectiveValueValue(EntryIndex entryIndex,DependencyProperty dp,PropertyMetadata元数据,EffectiveValueEntry oldEntry,EffectiveValueEntry&newEntry,布尔值coerceWithDeferredReference,布尔值coerceWithCurrentValue,操作类型操作类型依赖关系(System.Windows.DependencyObject.UpdateEffectiveValue) ,PropertyMetadata元数据,位于System.Windows.DependencyObject.SetCurrentValueInternal(DependencyProperty dp,Ob)的布尔coerceWithDeferredReference,布尔coerceWithCurrentValue,OperationType operationType,布尔isInternal) System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem item,MouseButton mouseButton)在System.Windows.Controls.ListBoxItem.HandleMouseButtonDown(MouseButton mouseButton)在System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e) .Windows.UIElement.OnMouseLeftButtonDownThunk(Object sender,MouseButtonEventArgs e)在System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler,Object genericTarget)在System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler,Object target)在System.Windows。 System.Windows.Event.System.Windows.EventRoute.InvokeHandlersImpl(Object source,RoutedEventArgs args,Boolean reRaised)处System.Windows.UI.UIElement.ReRaiseEventAs(DependencyObject sender,RoutedEventArgs args,RoutedWindows newEvent)处的RoutedEventHandlerInfo.InvokeHandler(Object target,RoutedEventArgs routedEventArgs)。 System.Windows.Input.Mouse上的.UIElement.OnMouseDownThunk(Object sender,MouseButtonEventArgs e) System.Windows.RoutedEventArgs.InvokeHandler(委托处理程序,对象目标)处的ButtonEventArgs.InvokeEventHandler(Delegate genericHandler,Object genericTarget)在System.Windows.EventRoute.InvokeHandlersImpl(Object来源,位于System.Windows.UIElement.RaiseEventImpl(DependencyObject sender,RoutedEventArgs args)的System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args),位于System.Windows.UIElement.RaiseEvent(RoutedEventArgs args,布尔值受信任)在System.Windows.Input.InputManager.ProcessStagingArea()在System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)在System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)在System.Windows.Interop.HwndMouseInputProvider.ReportInput (IntPtr hwnd,InputMode模式,Int32时间戳,RawMouseActions操作,Int32 x,Int32 y,Int32滚轮)在System.Windows.Inter op.HwndMouseInputProvider.FilterMessage(IntPtr hwnd,WindowMessage msg,IntPtr wParam,IntPtr lParam,布尔值和处理)位于System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd,Int32 msg,IntPtr wParam,IntPtr lParam,布尔值。 Win32.HwndWrapper.WndProc(IntPtr hwnd,Int32 msg,IntPtr wParam,IntPtr lParam,布尔值和已处理)在MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)在System.Windows.Threading.ExceptionWrappers.InternalRealCall(Delegate回调, System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象源,委托回调,对象args,Int32 numArgs,委托catchHandler的Int32 numArgs)

值得注意的另一件事是,如果将ListViewSelectionMode设置为Multiple ,则不会引发异常。 在这种情况下, ListFooBar的值我从TextBox更改的项目以及我随后可能选择的其他任何项目均保持选中状态。

注意:我正在使用Fody / PropertyChanged实现INotifyCollectionChanged

如果要更新名称,Bar还需要实现PropertyChanged。

[PropertyChanged.ImplementPropertyChanged]
public class Bar
{
    public string Name { get; set; }
}

另外,通常最好通过VM上的属性完成选定项的绑定。

例如:

[PropertyChanged.ImplementPropertyChanged]
public class ViewModel
{
    public ObservableCollection<Foo> Foos { get; set; }
    //This is the property to hold the selected item.
    public Foo SelectedFoo { get; set; }
}

然后将您的ListView绑定更改为:

<ListView x:Name="V_List" SelectedItem="{Binding SelectedFoo}" ItemsSource="{Binding Path=Foos}" SelectionMode="Single">
    ...
</ListView>

并且您的TextBox变为:

<!--No need for binding the DataContext of the Grid.-->
<Grid>
    <TextBox Text="{Binding Path=SelectedFoo.FooBar.Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
</Grid>

我不明白为什么会这样?

我无法给出确切的答案,但是看着错误,看起来ListView保留了控件项的内部Dictionary。 此外,我敢打赌该内部字典的键是基于GetHashCode的值的。 问题是您允许用户更改该键(通过更改name属性),而对象仍是控件的一部分。 我猜想在选择更改时,它通过添加和删除必要项来维护字典。 由于项目的有效“键”已更改,因此它可能试图将其读取到内部词典中,只是发现该项目已在词典中(但在原始键下)。

您可以通过在名称更改时将更改的项目删除并将其读入Foos集合来检验该理论。 这应该使用正确的新密钥清除并将该项目重新插入内部字典中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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