使用ListView SelectedItem的属性和TextBox绑定

时间:2017-03-03 12:17:41

标签: c# wpf xaml data-binding

编辑:

我发现此问题的根源是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(IDictionary TextBox 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,则不会引发异常。在这种情况下,SelectionModeListView的值从Multiple更改的List中的项目以及我之后可能选择的任何其他项目都会被选中。< / p>

注意:我正在使用Fody/PropertyChanged来实施FooBar

2 个答案:

答案 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集合来测试该理论。这应该使用正确的新密钥清除并重新插入内部字典中的项目。