Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wpf DataGrid - item with the same key has already been added

I saw many posts related to this problem but I couldn't find solution in non of them

I have the next WPF DataGrid:

<DataGrid Grid.Column="1"
          Grid.Row="4"
          AutoGenerateColumns="False"
          ItemsSource="{Binding MapsGrid}"
          SelectedItem="{Binding DataContext.CategoriesSelectedRow, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=OneWayToSource}"
          SelectionMode="Single"
          CanUserReorderColumns="False"
          CanUserResizeColumns="False"
          CanUserDeleteRows="False"
          CanUserResizeRows="False"
          CanUserAddRows="False">
  <DataGrid.Columns>
    <DataGridTemplateColumn Header="Main Category"
                            Width="*">
      <DataGridTemplateColumn.HeaderStyle>
        <Style TargetType="DataGridColumnHeader">
          <Setter Property="HorizontalContentAlignment"
                  Value="Center" />
          <Setter Property="FontSize"
                  Value="16" />
        </Style>
      </DataGridTemplateColumn.HeaderStyle>
      <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
          <ComboBox ItemsSource="{Binding MainCategoriesColl}"
                    DisplayMemberPath="Category"
                    SelectedItem="{Binding MainCategorySelectedItem,UpdateSourceTrigger=LostFocus, Mode=OneWayToSource}" />
        </DataTemplate>
      </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

    <DataGridTemplateColumn Header="Sub Category"
                            Width="*">
      <DataGridTemplateColumn.HeaderStyle>
        <Style TargetType="DataGridColumnHeader">
          <Setter Property="HorizontalContentAlignment"
                  Value="Center" />
          <Setter Property="FontSize"
                  Value="16" />
        </Style>
      </DataGridTemplateColumn.HeaderStyle>
      <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
          <ComboBox ItemsSource="{Binding SubCategoriesColl}"
                    DisplayMemberPath="Category"
                    SelectedItem="{Binding SubCategorySelectedItem,  UpdateSourceTrigger=LostFocus, Mode=TwoWay}" />
        </DataTemplate>
      </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
  </DataGrid.Columns>
</DataGrid>

This DataGrid containes 2 ComboBoxes. The ItemSource object is the next one:

    public class MapsDescModel : NotificationObject, IEqualityComparer<MapsDescModel>, IEquatable<MapsDescModel>
{
    public MapsDescModel(ObservableCollection<MainCategories> mainCategoty)
    {
        MainCategoriesColl = mainCategoty;

    }

    public static event Action OnSelectionHandler;

    private static void OnSelection()
    {
        if (OnSelectionHandler != null)
        {
            OnSelectionHandler();
        }
    }

    private MainCategories _mainCategorySelectedItem;
    public MainCategories MainCategorySelectedItem
    {
        get { return _mainCategorySelectedItem; }
        set
        {
            if (Equals(value, _mainCategorySelectedItem)) return;
            _mainCategorySelectedItem = value;
            RaisePropertyChanged("MainCategorySelectedItem");
            RaisePropertyChanged("SubCategoriesColl");
        }
    }

    private SubCategories _subCategorySelectedItem;
    public SubCategories SubCategorySelectedItem
    {
        get { return _subCategorySelectedItem; }
        set
        {
            if (Equals(value, _subCategorySelectedItem)) return;
            _subCategorySelectedItem = value;
            RaisePropertyChanged("SubCategorySelectedItem");
            OnSelection();

        }
    }


    private ObservableCollection<MainCategories> _mainCategoriesColl;
    public ObservableCollection<MainCategories> MainCategoriesColl
    {
        get { return _mainCategoriesColl; }
        set
        {
            if (Equals(value, _mainCategoriesColl)) return;
            _mainCategoriesColl = value;
            RaisePropertyChanged("MainCategoriesColl");
        }
    }


    //Allow user see N/A category just if is the only subCategory that exists
    public ObservableCollection<SubCategories> SubCategoriesColl
    {
        get
        {
            if (MainCategorySelectedItem != null && MainCategorySelectedItem.SubCategory.Any())
                if (MainCategorySelectedItem.SubCategory.Count() > 1)
                {
                    return Infrastructure.Helpers.ExtensionMethods.ToObservableCollection(
                        MainCategorySelectedItem.SubCategory.AsEnumerable().Where(p => p.Category != Infrastructure.Constants.Constants.NoSubCategory));
                }
                else return Infrastructure.Helpers.ExtensionMethods.ToObservableCollection(
                        MainCategorySelectedItem.SubCategory.AsEnumerable());
            else return new ObservableCollection<SubCategories>();
        }
    }


    public bool Equals(MapsDescModel x, MapsDescModel y)
    {
        try
        {
            if (x.MainCategorySelectedItem == null && y.MainCategorySelectedItem == null)
                return true;
            else if (x.MainCategorySelectedItem == null && y.MainCategorySelectedItem != null ||
                x.MainCategorySelectedItem != null && y.MainCategorySelectedItem == null)
                return false;
            return
                x.MainCategorySelectedItem.MainCatID == y.MainCategorySelectedItem.MainCatID &&
                x.SubCategorySelectedItem.SubCatID == y.SubCategorySelectedItem.SubCatID;
        }
        catch (Exception ex)
        {
            ServiceLocator.Current.GetInstance<Infrastructure.SharedServices.IEnterpriseLibraryLogger>().Log(ex.Message, Category.Exception, Priority.High);
            return false;
        }
    }

    public int GetHashCode(MapsDescModel obj)
    {
            if (Object.ReferenceEquals(obj, null)) return 0;

            if (obj.MainCategorySelectedItem == null || obj.SubCategorySelectedItem == null)
                return 0;
            else return obj.SubCategorySelectedItem.Category.GetHashCode() + obj.MainCategorySelectedItem.GetHashCode();
    }

    public bool Equals(MapsDescModel other)
    {
        return
        this.Equals(this, other);  // use definition from IEqualityComparer<T>
    }

    public override bool Equals(object obj)
    {
        MapsDescModel other = obj as MapsDescModel;
        if (other == null)
            return base.Equals(obj);
        else
            return this.Equals(other);
    }

    public override int GetHashCode()
    {
        MapsDescModel other = this as MapsDescModel;
        if (other == null)
            return base.GetHashCode();
        else
            return this.GetHashCode(other);
    }
}

Everything works fine except except one annoying problem. When I add 2+ rows, If I click couple of time on the marked area in the attached picture I`m getting the following error:

System.ArgumentException was unhandled
  HResult=-2147024809
  Message=An item with the same key has already been added.
  Source=mscorlib
  StackTrace:
       at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
       at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
       at System.Collections.Generic.Dictionary`2..ctor(IDictionary`2 dictionary, IEqualityComparer`1 comparer)
       at System.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage..ctor(InternalSelectedItemsStorage collection, IEqualityComparer`1 equalityComparer)
       at System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple()
       at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
       at System.Windows.Controls.SelectedItemCollection.EndUpdateSelectedItems()
       at System.Windows.Controls.Primitives.MultiSelector.EndUpdateSelectedItems()
       at System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo info, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)
       at System.Windows.Controls.DataGrid.HandleSelectionForRowHeaderAndDetailsInput(DataGridRow row, Boolean startDragging)
       at System.Windows.Controls.Primitives.DataGridRowHeader.OnClick()
       at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonDown(MouseButtonEventArgs e)
       at System.Windows.UIElement.OnMouseLeftButtonDownThunk(Object sender, MouseButtonEventArgs e)
       at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at 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(Delegate genericHandler, Object genericTarget)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       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.RaiseTrustedEvent(RoutedEventArgs args)
       at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
       at System.Windows.Input.InputManager.ProcessStagingArea()
       at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
       at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
       at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
       at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at 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, Boolean& handled)
       at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.Run()
       at System.Windows.Application.RunDispatcher(Object ignore)
       at System.Windows.Application.RunInternal(Window window)
       at System.Windows.Application.Run(Window window)
       at System.Windows.Application.Run()
       at Malaria.App.Main() in c:\My Projects\Hebrew University\Malaria Management Console\Malaria\obj\Debug\App.g.cs:line 0
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

I think it maybe related to my GetHashCode method but I'm not sure. I found this link http://leavinsprogramming.blogspot.co.il/2012/08/wpf-datagrid-and-random.html It describe well me problem but there is not solution there. Does anyone know why this exception occur?

The problematic area

like image 319
Ofir Avatar asked Apr 12 '13 08:04

Ofir


1 Answers

To quote MSDN's entry on Object.GetHashCode():

In general, for mutable reference types, you should override GetHashCode only if:

  • You can compute the hash code from fields that are not mutable; or

  • You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.

Your current GetHashCode() implementation calls GetHashCode(MapsDescModel obj), and that method depends on mutable fields that can change while in a collection.

Change your GetHashCode() implementation to return a value that does not depend on changeable fields.

like image 99
Dustin Leavins Avatar answered Nov 04 '22 14:11

Dustin Leavins