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