代码之家  ›  专栏  ›  技术社区  ›  Gishu

列表项是值类型/结构且包含重复项的列表框的选择错误?

  •  4
  • Gishu  · 技术社区  · 14 年前

    我将水平项控件转换为列表框,以便能够选择单个项,但发现选择已中断。花了一些时间提炼出有问题的部分。

    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是一个结构,那么如果选择了列表中具有等效结构的项,则列表框选择(默认模式:single)将出错。e、 g第3册

    如果Book被转换为一个类(具有非值类型语义),则选择是固定的。

    选择(到目前为止,都不喜欢):

    • 我之所以选择structs,是因为它是一个小数据结构,值类型语义对于比较两个实例是否相等很有用。将其更改为类会导致我丢失值类型语义。。我不能再使用默认等于或重写它进行成员级比较。
    • 只为列表框选择添加一个差异化的Book属性(例如索引)。
    • 消除重复。。不可能。

    WPF listbox : problem with selection :表示列表框正在设置SelectedItem,在为此更新UI时,它只会点亮列表中 Equal(SelectedItem) . 不知道为什么。。突出显示SelectedIndex会使这个问题消失;也许我遗漏了一些东西。 ListBox is selecting many items even in SelectionMode="Single" :当列表项是字符串时显示相同的问题(值类型语义)

    4 回复  |  直到 7 年前
        1
  •  3
  •   Dean Chalk    14 年前

    为什么不简单地使用一个更好的collection类作为数据源来解决这个问题呢

    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;
    

    这将是您的数据模板

    <ListBox ItemsSource="{Binding}">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding Value.Name}"/>
        </DataTemplate>
      </ListBox.ItemTemplate>
    
        2
  •  3
  •   Joe White    14 年前

    我不清楚为什么你的列表中有重复项,如果它们完全相同(即,如果重复项的内容都相同,并且从Equals返回true)。您将无法知道用户选择了哪些重复项。列表框也不会,这可能就是你有问题的原因。

    也许,您可以将每个结构包装在一个类中,而不是直接绑定到一个结构集合?只需定义一个包含Book结构的BookWrapper类,并绑定到一个BookWrappers集合而不是一个Books集合。您修复了WPF无法区分实例的问题,但是您的其余代码可以继续具有结构的优点。

        3
  •  0
  •   GaryX    14 年前

    感谢迪恩·乔克的主意。

    我对它进行了扩展,以便更容易使用其他结构

    其思想是使用转换器将原始结构集合强制转换为自定义集合,从而重写equal以与Guid ID进行比较。您仍然具有原始顺序

    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>
    
        4
  •  0
  •   Dean Chalk    14 年前

    花环

    也许是简单点的?

    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));
        }
    }