2010-11-14 63 views
4

我將一個Horizo​​ntal ItemsControl轉換爲一個Listbox,這樣我就可以選擇單個項目,但發現選擇已被打破。花了一些時間來提煉出有問題的位。列表項是值類型/結構並且包含重複項的列表框的選擇錯誤?

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變成一個類(具有非值類型的語義),則選擇是固定的。

選擇(到目前爲止,不喜歡其中的任何):

  • 我選擇結構,因爲它的一個小的數據結構和值的類型的語義是在比較實例2的相等是有用的。將其更改爲類會導致我失去值類型的語義..我無法再使用默認的Equals或者爲了成員比較而重寫它。
  • 僅爲列表框選擇添加區分書屬性(例如索引)。
  • 消除重複..不可能。

WPF listbox : problem with selection:指出列表框設置的SelectedItem並同時更新UI對於這一點,它只是點亮列表中的所有物品Equal(SelectedItem)。不知道爲什麼..突出SelectedIndex會使這個問題消失;也許我錯過了一些東西。 ListBox is selecting many items even in SelectionMode="Single":顯示在列表項是字符串(值類型語義)

+1

你說如果你使用'class',你不能重寫'Equals'。爲什麼不?強烈建議在創建'struct'時強制推薦'Equals',所以你不應該僅僅使用'struct'來獲得一個具有值語義的'Equals'實現。順便說一下,這種行爲不僅限於結構。如果綁定到覆蓋Equals的類類型,並且兩個不同的實例相同,則會看到相同的行爲。 – 2010-11-14 09:43:34

+0

@Kent - 確切地說。我擁有的是一個簡單的數據結構,如果兩個實例具有相同的成員,則它們是等效的。直到我需要一個帶有可選項目的列表框,我才能把它變成一個類......但是如果我重寫Equals來進行成員比較,我會回到方塊1(正如我用第二個SO q鏈接)字符串列表框顯示相同的問題)。如果我不覆蓋,我需要一個customEquals方法來進行成員比較。所以它似乎像添加一個區分參數(如獨特的時間戳或索引)是最好的選擇.. – Gishu 2010-11-15 02:46:43

回答

3

爲什麼不能簡單地用一個更好的集合類爲您的數據源來克服這個問題

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

字典將失去集合的順序...但我明白了..將結構包裝在類中。 – Gishu 2010-11-18 15:17:55

0

感謝院長的粉筆他的想法。

我擴展它,使它更易於用戶的其他結構

的想法是使用轉換到原來的結構集合轉換爲自定義集合,進而覆蓋等於比較與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> 
0

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

我不是你爲什麼有重複您的列表中明確,如果他們是完全一致的(即,如果重複具有完全相同的內容,並從的Equals返回true)。您將無法辨別用戶選擇了哪些重複項。ListBox也不會,這可能就是爲什麼你有問題。

也許,而不是直接綁定到一個結構集合,你可以將每個結構包裝在一個類中?只需定義一個包含Book結構的BookWrapper類,並將其綁定到BookWrapper集合而不是Books集合。您修復了WPF無法區分實例的問題,但您的代碼的其餘部分可能會繼續享有結構的好處。

+0

在我的情況下,我有一個包含單元測試運行結果的結構,因此它具有[Result = Red/Green,NumberOfTests = int]。因此,運行一次測試運行兩次,導致兩個結構出現在列表框中(該位置是我區分它們的方式 - 最上面的列表項是最近的)。我結束了添加另一個屬性的結構(一個時間戳)..但是是啊對Q的答覆似乎是包裝他們在ref型,使他們不平等。 – Gishu 2010-11-22 12:31:16