Ошибка выбора для списка, где элементы списка являются типами / структурами значений и содержат дубликаты? - PullRequest
4 голосов
/ 14 ноября 2010

Я превратил горизонтальный элемент ItemsControl в список, чтобы иметь возможность выбирать отдельные элементы, но обнаружил, что выбор был нарушен. Потребовалось время, чтобы отобрать проблемный бит.

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}"/>

Если Book является структурой, выбор списка (режим по умолчанию: одиночный) будет неправильным, если вы выберете элемент, имеющий эквивалентную структуру в списке. например, Book3

Если Book превращается в класс (с семантикой не значащего типа), выбор фиксируется.

Выбор (пока что ни один из них не нравится):

  • Я выбрал структуры, потому что их небольшая структура данных и семантика типа значений полезны при сравнении двух экземпляров на равенство. Изменение его на класс приводит к тому, что я теряю семантику типа-значения. Я больше не могу использовать значение Equals по умолчанию или переопределить его для сравнения по элементам.
  • Добавьте отличительный атрибут Book исключительно для работы с выбранным списком (например, индекс).
  • Устранить дубликаты .. Не возможно.

WPF listbox: проблема с выбором : указывает, что Listbox настраивает SelectedItem, и при обновлении интерфейса для этого он просто подсвечивает все элементы в списке, которые Equal(SelectedItem). Не уверен, почему .. выделение SelectedIndex устранит эту проблему; может быть, я что-то упустил. ListBox выбирает много элементов, даже в SelectionMode = "Single" : показывает ту же проблему, когда элементы списка являются строками (семантика типа значения)

Ответы [ 4 ]

3 голосов
/ 22 ноября 2010

Мне непонятно, почему у вас есть дубликаты в вашем списке, если они абсолютно идентичны (то есть, если дубликаты имеют одинаковое содержимое и возвращают true из Equals). У вас не будет никакого способа сказать, какие из дубликатов выбрал пользователь. Также не будет ListBox, поэтому, вероятно, у вас возникли проблемы.

Может быть, вместо привязки непосредственно к коллекции структур, вы могли бы обернуть каждую структуру в классе? Просто определите класс BookWrapper, который содержит структуру Book, и привяжите его к коллекции BookWrappers вместо коллекции книг. Вы исправили проблему, заключающуюся в том, что WPF не может различать экземпляры, но остальная часть кода может по-прежнему иметь преимущества структуры.

3 голосов
/ 18 ноября 2010

Почему бы просто не использовать лучший класс сбора в качестве источника данных для решения проблемы

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;

И это будет ваш DataTemplate

<ListBox ItemsSource="{Binding}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Value.Name}"/>
    </DataTemplate>
  </ListBox.ItemTemplate>
0 голосов
/ 19 ноября 2010

Garyx

Может быть, что-то попроще?

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 голосов
/ 19 ноября 2010

Спасибо Дину Чоку за его идею.

Я расширяю его, чтобы пользователю было проще для других структур

Идея состоит в том, чтобы использовать преобразователь для приведения исходной коллекции структур в пользовательскую коллекцию, которая, в свою очередь, переопределяет значение равенства для сравнения с идентификатором Guid. У вас еще есть оригинальный заказ

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>
...