Selection error for a list, where list items are value / structure types and contain duplicates?

I turned the Horizontal ItemsControl into a Listbox so that I could select individual items, but found that the selection was broken. It took some time to separate the problematic bit.

Books = new[] { new Book{Id=1, Name="Book1"}, new Book{Id=2, Name="Book2"}, new Book{Id=3, Name="Book3"}, new Book{Id=4, Name="Book4"}, new Book{Id=3, Name="Book3"}, }; <DataTemplate DataType="{x:Type WPF_Sandbox:Book}"> <TextBlock Text="{Binding Name}"/> </DataTemplate> <ListBox ItemsSource="{Binding Books}"/> 

If Book is a structure, selecting a list (default mode: one) goes wrong if you select an item with an equivalent structure in the list. e.g. Book3

If a book turns into a class (with semantics without meaning), the choice is fixed.

Choice (no one likes it yet):

  • I chose structures because a small data structure and value type semantics are useful when comparing two instances for equality. Changing it in a class makes me lose the semantics of type values. I can no longer use the default values ​​or redefine it for comparison by method.
  • Add the differentiating attribute of the book, only to select a list for work (for example, an index).
  • Eliminate duplicates. Impossible.

WPF list: selection problem : indicates that Listbox sets SelectedItem and when updating the user interface for this it just lights up all the items in the list that are Equal(SelectedItem) . Not sure why .. highlighting SelectedIndex would make this problem go away; maybe I'm missing something. ListBox selects many items even in SelectionMode = "Single" : shows the same problem when list items are strings (value type semantics)

+4
source share
4 answers

Why not just use the best collection class as a data source to solve the problem.

 var collection = new[] { new Book {Id = 1, Name = "Book1"}, new Book {Id = 2, Name = "Book2"}, new Book {Id = 3, Name = "Book3"}, new Book {Id = 4, Name = "Book4"}, new Book {Id = 3, Name = "Book3"}, }; var Books = collection.ToDictionary(b => Guid.NewGuid(), b => b); DataContext = Books; 

And this will be your DataTemplate

 <ListBox ItemsSource="{Binding}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Value.Name}"/> </DataTemplate> </ListBox.ItemTemplate> 
+3
source

I don’t understand why you have duplicates in your list if they are absolutely identical (i.e. if duplicates have the same content and return true from Equals). You will have no way to determine which of the duplicates the user has selected. Nor will there be a ListBox, which is probably why you have problems.

Maybe instead of directly linking to a collection of structures, you could wrap each structure in a class? Simply define the BookWrapper class that contains the Book structure and bind to the BookWrappers collection instead of the book collection. You are fixing a problem where WPF cannot split instances separately, but the rest of your code can continue to take advantage of the structure.

+3
source

Thanks to Dean Melk for his idea.

I am expanding it to make it easier for other structures

The idea is to use a converter to translate the original structure collection into a user collection, which in turn overrides the value compared to the Guid ID. Do you still have an initial order?

 public class StructListItem { private Guid _id = Guid.NewGuid(); public Guid ID { get { return _id; } set { _id = value; } } private object _core = default(object); public object Core { get { return _core; } set { _core = value; } } public StructListItem(object core) { Core = core; } public override bool Equals(object obj) { return ID.Equals(obj); } public override int GetHashCode() { return ID.GetHashCode(); } } public class StructToCollConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is IEnumerable) { List<StructListItem> _ret = new List<StructListItem>(); if (value != null) { IEnumerator i = ((IEnumerable)value).GetEnumerator(); while (i.MoveNext()) { _ret.Add(new StructListItem(i.Current)); } } return _ret.ToArray(); } return null; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } 

  <ListBox ItemsSource="{Binding Books, Converter={StaticResource converter}}" SelectionMode="Single"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding Core.Name}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> 
0
source

Garyx

Is something even simpler?

 public class StructListItem<T> where T : struct { public T Item { get; private set; } public readonly Guid Id = Guid.NewGuid(); public StructListItem(T item) { Item = item; } public static IEnumerable<StructListItem<U>> GetStructList<U>(IEnumerable<U> originalList) where U : struct { return originalList.Select(i => new StructListItem<U>(i)); } } 
0
source

All Articles