Hello,
I'm getting always a exception when I'm changing the DataGrid. The DataGrids ItemsSource is bound to an ObservableCollection.
I have the following code (only the important code is shown, if something is missing, I will complete it):
Models
Locale.cs:
public class Locale
{
private ObservableCollection<LocaleEntry> entries;
public Locale()
{
this.entries = new ObservableCollection<LocaleEntry>();
this.entries.CollectionChanged += new NotifyCollectionChangedEventHandler(this.entries_CollectionChanged);
}
public ObservableCollection<LocaleEntry> Entries
{
get { return this.entries; }
set
{
if (this.entries.Equals(value))
{
return;
}
this.entries = value;
this.OnPropertyChanged("Entries");
}
}
public override bool Equals(object obj)
{
if (!obj.GetType().Equals(this.ObjectType))
{
return false;
}
Locale locale = (Locale)obj;
if (!this.Name.Equals(locale.Name))
{
return false;
}
if (!this.Entries.Count.Equals(locale.Entries.Count))
{
return false;
}
foreach (LocaleEntry localeEntry in this.Entries)
{
if (!locale.Entries.Contains(localeEntry))
{
return false;
}
}
return true;
}
}
LocaleEntry.cs
public class LocaleEntry
{
private string language;
private string value;
public LocaleEntry()
{
this.language = string.Empty;
this.value = string.Empty;
}
public string Language
{
get { return this.language; }
set
{
if (value == this.language || string.IsNullOrEmpty(value))
{
return;
}
this.language = value;
this.OnPropertyChanged("Language");
}
}
public string Value
{
get { return this.value; }
set
{
if (value == this.value || string.IsNullOrEmpty(value))
{
return;
}
this.value = value;
this.OnPropertyChanged("Value");
}
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != typeof(LocaleEntry))
{
return false;
}
LocaleEntry locale = (LocaleEntry)obj;
if (!locale.Language.Equals(this.Language))
{
return false;
}
if (!locale.Value.Equals(this.Value))
{
return false;
}
return true;
}
}
ViewModel
class UserControlLocaleEntryBase : ViewModelBase
{
private Locale locale;
private RelayCommand addLocaleEntry;
private RelayCommand removeLocaleEntry;
private int selectedIndex;
public UserControlLocaleEntryBase(Locale locale)
{
this.Locale = locale;
this.selectedIndex = -1;
}
public int SelectedIndex
{
get
{
return this.selectedIndex;
}
set
{
if (this.selectedIndex.Equals(value))
{
return;
}
this.selectedIndex = value;
this.OnPropertyChanged("SelectedIndex");
}
}
public Locale Locale
{
get { return this.locale; }
set
{
if (this.locale != null && this.locale.Equals(value))
{
return;
}
this.locale = value;
}
}
public ICommand AddLocaleEntry
{
get
{
if (this.addLocaleEntry == null)
{
this.addLocaleEntry = new RelayCommand(() => this.addLocaleEntryFunc());
}
return this.addLocaleEntry;
}
}
public ICommand RemoveLocaleEntry
{
get
{
if (this.removeLocaleEntry == null)
{
this.removeLocaleEntry = new RelayCommand(() => this.removeLocaleEntryFunc(), () => this.canRemoveLocaleEntry);
}
return this.removeLocaleEntry;
}
}
private void addLocaleEntryFunc()
{
LocaleEntry localeEntry = new LocaleEntry();
if (!this.Locale.Entries.Contains(localeEntry))
{
this.Locale.Entries.Add(localeEntry);
}
}
private void removeLocaleEntryFunc()
{
this.Locale.Entries.RemoveAt(this.SelectedIndex);
}
private bool canRemoveLocaleEntry
{
get
{
if (this.selectedIndex.Equals(-1))
{
return false;
}
return true;
}
}
}
View
UserControlLocaleEntry.xaml.cs:
public partial class UserControlLocaleEntry : UserControl
{
private readonly UserControlLocaleEntryBase userControlLocaleEntryBase;
public UserControlLocaleEntry(Locale locale)
{
InitializeComponent();
this.userControlLocaleEntryBase = new UserControlLocaleEntryBase(locale);
base.DataContext = this.userControlLocaleEntryBase;
}
}
UserControlLocaleEntry.xaml:
<UserControl x:Class="SAP_Installation_Designer_2.UserControlLocaleEntry"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:t="clr-namespace:TestApp.Translation"
Height="Auto" Width="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="6" VerticalAlignment="Center" Text="{t:Translate strUserControlLocaleEntryName}"/>
<TextBox Grid.Column="1" Margin="6" VerticalAlignment="Center">
<TextBox.Text>
<Binding Path="Locale.Name" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</Grid>
<DataGrid Grid.Row="1" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="FullRow" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.ItemsSource>
<Binding Path="Locale.Entries" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</DataGrid.ItemsSource>
<DataGrid.SelectedIndex>
<Binding Path="SelectedIndex" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</DataGrid.SelectedIndex>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Language, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="{t:Translate strUserControlLocaleEntryLanguage}" Width="Auto"/>
<DataGridTextColumn Binding="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="{t:Translate strUserControlLocaleEntryValue}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Height="25" MinWidth="75" Margin="6" Command="{Binding Path=AddLocaleEntry}">
<StackPanel Orientation="Horizontal">
<Image Source="../../Resources/GreenPlus16.png" Margin="6,0,0,0" VerticalAlignment="Center"/>
<TextBlock Text="{t:Translate strUserControlLocaleEntryAdd}" Margin="6,0,6,0" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button Grid.Column="2" Height="25" MinWidth="75" Margin="6" Command="{Binding Path=RemoveLocaleEntry}">
<StackPanel Orientation="Horizontal">
<Image Source="../../Resources/Redx16.png" Margin="6,0,0,0" VerticalAlignment="Center"/>
<TextBlock Text="{t:Translate strUserControlLocaleEntryRemove}" Margin="6,0,6,0" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</Grid>
</Grid>
</UserControl>
I'm able to see all rows in the DataGrid and also inserting or removing items from the collection is working without any problems. But if I change an item (Click in a row and change the text inside) I always get this ArgumentException:
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.HandleSelectionForCellInput(DataGridCell cell, Boolean startDragging, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)
at System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDown(MouseButtonEventArgs e)
at System.Windows.Controls.DataGridCell.OnAnyMouseLeftButtonDownThunk(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 Test.App.Main() in C:\Source\App.g.cs:line 0
InnerException:
I'm confused with this exception, as I'm not adding a new item, there is only a change in a existing item. What am I missing? Whats wrong with my code? Can someone help me?
Thanks in advance,
Libor