【问题标题】:ArgumentException with ListView SelectedItem's property and TextBox binding具有 ListView SelectedItem 的属性和 TextBox 绑定的 ArgumentException
【发布时间】:2017-07-23 13:16:48
【问题描述】:

编辑:

我发现这个问题的根源是Bar 中的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 Text="{Binding Path=SelectedFoo.FooBar.Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />

事情是这样的:

当我从List 中选择第一个Foo 时,绑定有效,并且所选Foo 的FooBar 属性的Name 出现在TextBox 中。无论我更改选择多少次,TextBox 中都会出现相应的值。

但是现在,如果我使用 TextBox 更改 Name(在失去焦点后,由于 TwoWay 数据绑定,它正在工作,我使用调试进行了检查)然后从列表中更改我的选择,@987654343 @ 仍然显示之前选择的项目的值。

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

这是日志:

异常是:-Exception :: System.ArgumentException:一个带有 已添加相同的密钥。在 System.ThrowHelper.ThrowArgumentException(异常资源资源) 在 System.Collections.Generic.Dictionary2.Insert(TKey key, TValue value, Boolean add) at System.Collections.Generic.Dictionary2..ctor(IDictionary2 dictionary, IEqualityComparer1 比较器) 在 System.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage..ctor(InternalSelectedItemsStorage 集合,IEqualityComparer`1 equalComparer) 在 System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple() 在 System.Windows.Controls.Primitives.Selector.SelectionChanger.End() 在 System.Windows.Controls.Primitives.Selector.SetSelectedHelper(对象 项目,FrameworkElement UI,布尔选择)在 System.Windows.Controls.Primitives.Selector.NotifyIsSelectedChanged(FrameworkElement 容器,布尔选择,RoutedEventArgs e) at System.Windows.Controls.Primitives.Selector.OnSelected(对象发送者, RoutedEventArgs e) 在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(对象目标, RoutedEventArgs routedEventArgs) 在 System.Windows.EventRoute.InvokeHandlersImpl(对象源, RoutedEventArgs 参数,布尔 reRaised)在 System.Windows.UIElement.RaiseEventImpl(DependencyObject 发送者, RoutedEventArgs 参数)在 System.Windows.UIElement.RaiseEvent(RoutedEventArgs e) 在 System.Windows.Controls.ListBoxItem.OnSelected(RoutedEventArgs e)
在 System.Windows.Controls.ListBoxItem.OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 在 System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs 吃 System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs 吃 System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs 参数)在 System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex,DependencyProperty dp,PropertyMetadata 元数据, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference,布尔 coerceWithCurrentValue, 操作类型操作类型)在 System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, 对象值、PropertyMetadata 元数据、布尔值 coerceWithDeferredReference,布尔 coerceWithCurrentValue, OperationType operationType, Boolean isInternal) at System.Windows.DependencyObject.SetCurrentValueInternal(DependencyProperty dp,对象值)在 System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem 项目,鼠标按钮鼠标按钮)在 System.Windows.Controls.ListBoxItem.HandleMouseButtonDown(鼠标按钮 鼠标按钮)在 System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e) 在 System.Windows.UIElement.OnMouseLeftButtonDownThunk(Object 发件人,MouseButtonEventArgs e) 在 System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(委托 genericHandler, 对象 genericTarget) 在 System.Windows.RoutedEventArgs.InvokeHandler(委托处理程序,对象 目标)在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(对象目标, RoutedEventArgs routedEventArgs) 在 System.Windows.EventRoute.InvokeHandlersImpl(对象源, RoutedEventArgs 参数,布尔 reRaised)在 System.Windows.UIElement.ReRaiseEventAs(DependencyObject 发件人, RoutedEventArgs 参数,RoutedEvent 新事件)在 System.Windows.UIElement.OnMouseDownThunk(对象发送者, MouseButtonEventArgs e) 在 System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(委托 genericHandler, 对象 genericTarget) 在 System.Windows.RoutedEventArgs.InvokeHandler(委托处理程序,对象 目标)在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(对象目标, RoutedEventArgs routedEventArgs) 在 System.Windows.EventRoute.InvokeHandlersImpl(对象源, RoutedEventArgs 参数,布尔 reRaised)在 System.Windows.UIElement.RaiseEventImpl(DependencyObject 发送者, RoutedEventArgs 参数)在 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args) 在 System.Windows.UIElement.RaiseEvent(RoutedEventArgs 参数,布尔值 受信任)在 System.Windows.Input.InputManager.ProcessStagingArea() 在 System.Windows.Input.InputManager.ProcessInput(InputEventArgs 输入)在 System.Windows.Input.InputProviderSite.ReportInput(InputReport 输入报告)在 System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode 模式,Int32 时间戳,RawMouseActions 动作,Int32 x, Int32 y,Int32 轮)在 System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& 处理)在 System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd,Int32 msg,IntPtr wParam, IntPtr lParam,布尔值&处理)在 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) 在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(委托 回调,对象参数,Int32 numArgs)在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象源, 委托回调、Object args、Int32 numArgs、Delegate catchHandler)

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

注意:我使用Fody/PropertyChanged来实现INotifyCollectionChanged。

【问题讨论】:

  • 福迪?嗯……为什么?我看不出它有什么价值。无论如何..您的虚拟机和视图中有几个问题。 Foos 尚未初始化。列表视图没有SelectedItem 绑定。为什么不回到基础并尝试使用 INotifyPropertyChanged 实现正确的 MVVM 模型?一旦您知道您可以继续使用 Caliburn。
  • @KosalaW Fody 作为样板的节省时间基本上很好。我非常怀疑这与他的问题有关。
  • @KosalaW 我可能还不是实现 MVVM 的专家,但是,我已经尝试简化我的代码并在这里展示。我不能只是在这里复制粘贴确切的代码,它太长而且大部分是不必要的。
  • @BrandonKramer 基本没问题??还有很多更好的。 OP遇到的问题是非常基本的。 nugets 无法解决这个问题。
  • @KosalaW 我同意 BrandonKramer 的观点,我认为 Fody 不是问题所在。我只是认为人们应该在告诉我在看到 TwoWay 绑定时正确实施 INotifyPropertyChanged 之前了解它。

标签: c# wpf xaml data-binding


【解决方案1】:

我不明白为什么会这样?

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

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

【讨论】:

  • Yupppp.. 你的理论完全正确!当我更改密钥时,它不会允许。但是,当我将其改回时,它可以正常工作,这验证了您刚才所说的话。谢谢!
【解决方案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>

你的文本框变成:

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

【讨论】:

  • 糟糕.. 我错过了 Bar 也实现了 PropertyChanged。我将编辑问题。我现在就试试你的方法。
  • 不,还是一样的。
  • @VijayChavda 在这种情况下,您的问题很可能是由您没有向我们展示的东西引起的。您提到您的代码很大,您确定您的代码中没有其他内容可能会影响到这一点吗?
  • 我非常注意它,我很确定。但如果你这么认为,我会考虑再次检查..(嗯..这可能需要一段时间)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-05
  • 2018-05-25
  • 2015-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多