EDIT:
我发现这个问题的根源是 Equals()
中的 Equals()
和 GetHashCode()
实现 .
特别是参与 GetHashCode()
并且绑定到 TextBox
的属性(如 Bar
中的 Name
) . 删除这些覆盖方法一切正常(除了我想保留它们)
我不明白的是为什么会发生这种情况?
我有一个 TextBox
,一个 ListView
,以及一些带有以下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
时,绑定有效,并且 TextBox
中出现所选 Foo
的 FooBar
属性的 Name
. 无论我更改选择的次数, TextBox
中都会显示相应的值 .
但是现在如果我使用 TextBox
更改 Name
(在焦点丢失之后,由于TwoWay数据绑定,正在工作并且我使用调试检查)然后从列表中更改我的选择, TextBox
仍然显示先前选择的项目的值 .
此外,再次选择相同的项目,然后选择其他一些项目,我得到以下异常(令人惊讶的是,调试器没有报告,我不得不将其记录到文件 . 可能是因为异常没有从我的代码 . )
这是日志:
异常是:-Exception :: System.ArgumentException:已添加具有相同键的项 . System.ChrowHelper.ThrowArgumentException(ExceptionResource资源)位于System.Collections.Generic.Dictionary2.Insert(TKey键,TValue值,布尔加法)的System.Collections.Generic.Dictionary2..ctor(IDictionary2字典,IEqualityComparer1比较器)中的System.ThrowHelper.ThrowArgumentException(ExceptionResource资源) System.Windows.Controls.Primitives.Selector.SelectionChanger的System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple()中的.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage..ctor(InternalSelectedItemsStorage集合,IEqualityComparer`1 equalityComparer) System.Windows.Controls.Primitives.Selector.NotifyIsSelectedChanged(FrameworkElement container,Boolean selected,RoutedEventArgs e)中System.Windows.Controls.Primitives.Selector.SetSelectedHelper(Object item,FrameworkElement UI,Boolean selected)的.End() System.Windows.RoutedEventHandlerInfo.InvokeHandler中的.Windows.Controls.Primitives.Selector.OnSelected(Object sender,RoutedEventArgs e)(对象目标,Rout系统中的System.Windows.UIElement.RaiseEventImpl(DependencyObject sender,RoutedEventArgs args)处的System.Windows.EventRoute.InvokeHandlersImpl(Object source,RoutedEventArgs args,Boolean reRaised)中的edEventArgs routedEventArgs)系统的System.Windows.UIElement.RaiseEvent(RoutedEventArgs e) .Windows.Controls.ListBoxItem.OnSelected(RoutedEventArgs e)上System.Windows.Controls.ListBoxItem.OnIsSelectedChanged(的DependencyObject d,DependencyPropertyChangedEventArgs e)上System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)上System.Windows.FrameworkElement.OnPropertyChanged( DependencyPropertyChangedEventArgs e)上System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs参数)在System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex,的DependencyProperty DP,PropertyMetadata元数据,EffectiveValueEntry oldEntry,EffectiveValueEntry&newEntry,布尔coerceWithDeferredReference,布尔coerceWithCurrentVal ue,OperationType operationType)System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp,Object value,PropertyMetadata metadata,Boolean coerceWithDeferredReference,Boolean coerceWithCurrentValue,OperationType operationType,Boolean isInternal)at System.Windows.DependencyObject.SetCurrentValueInternal(DependencyProperty dp,Object value)在System.Windows上的System.Windows.Controls.List.Imp中的System.Windows.Controls.ListBoxItem.HandleMouseButtonDown(MouseButton mouseButton)中的System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem项目,MouseButton mouseButton)处于System.Windows的System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e) . System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler,Object genericTarget)中的UIElement.OnMouseLeftButtonDownThunk(Object sender,MouseButtonEventArgs e)at at系统中System.Windows.EventRoute.InvokeHandlersImpl(Object source,RoutedEventArgs args,Boolean reRaised)的System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target,RoutedEventArgs routedEventArgs)中的System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler,Object target) . 系统中的System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler,Object genericTarget)中的System.Windows.UIElement.OnMouseDownThunk(Object sender,MouseButtonEventArgs e)中的Windows.UIElement.ReRaiseEventAs(DependencyObject sender,RoutedEventArgs args,RoutedEvent newEvent) . System.Windows上的System.Windows.EventRoute.InvokeHandlersImpl(Object source,RoutedEventArgs args,Boolean reRaised)中的System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target,RoutedEventArgs routedEventArgs)中的Windows.RoutedEventArgs.InvokeHandler(Delegate handler,Object target) . System.Windows.UIElement.Rais中的UIElement.RaiseEventImpl(DependencyObject sender,RoutedEventArgs args) System.Windows上的System.Windows.InputManager.ProcessInput(InputEventArgs输入)处的System.Windows.InputManager.ProcessStagingArea()处的System.Windows.UIElement.RaiseEvent(RoutedEventArgs args,布尔值为受信任)的eTrustedEvent(RoutedEventArgs args) System.Windows.Interop.HwndMouseInputProvider上System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd,InputMode mode,Int32 timestamp,RawMouseActions actions,Int32 x,Int32 y,Int32 wheel)的.Input.InputProviderSite.ReportInput(InputReport inputReport) MS.Win32.HwndWrapper的System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd,Int32 msg,IntPtr wParam,IntPtr lParam,Boolean&handling)中的.FilterMessage(IntPtr hwnd,WindowMessage msg,IntPtr wParam,IntPtr lParam,Boolean&handling) .WndProc(IntPtr hwnd,Int32 msg,IntPtr wParam,IntPtr lParam,Boolean&handling)位于System.Windows.Threading.ExceptionWrapper.InternalRealCall(委托callb)的MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象源,委托回调,对象args,Int32 numArgs,Delegate catchHandler)中的ack,Object args,Int32 numArgs)
我注意到的另一件值得一提的是,如果我将 ListView
的 SelectionMode
设置为 Multiple
,则不会引发异常 . 在这种情况下, List
中 FooBar
的值从 TextBox
更改的项目保持选中状态,以及我之后可能选择的任何其他项目 .
注意:我使用Fody/PropertyChanged来实现 INotifyCollectionChanged
.
2 回答
我不能给你一个确定的答案,但看看错误,看起来ListView保持控件项目的内部字典 . 此外,我敢打赌,内部字典的关键是基于GetHashCode的值 . 问题是,当对象仍然是控件的一部分时,您允许用户更改该键(通过更改name属性) . 我想在选择时更改它是通过添加和删除项目来维护字典 . 由于您项目的有效“密钥”已更改,因此可能尝试将其读取到内部字典,但只发现该项目已在字典中(但在原始密钥下) .
您可以通过在名称更改时删除并读取已更改的项目到Foos集合来测试该理论 . 这应该使用正确的新密钥清除并重新插入内部字典中的项目 .
如果要更新Name,Bar还需要实现PropertyChanged .
此外,通常最好通过VM上的属性完成所选项目绑定 .
例如:
然后将ListView绑定更改为:
你的TextBox变成了: