2013-11-15 63 views
3

我想以編程方式將字典綁定到WPF(C#)中的GridView。字典的結構爲 -
詞典(串,詞典(字符串,整數))
我能夠主字典的鍵結合到GridView

綁定GridView與WPF C中的字典#

Dictionary<string, Dictionary<string, int>> result 
GridView myGrid = new GridView(); 
GridViewColumn gc = new GridViewColumn(); 
gc.Header = "File Name"; 
gc.DisplayMemberBinding = new Binding("Key"); 
myGrid.Columns.Add(gc); 

在GridView的源極被設置爲結果

我想創建與設置於內詞典的主要報頭中的柱,並將其綁定到內部詞典的價值

somethig like gc.DisplayMemberBinding = new Binding(「Value.Value」); 字典是這樣

{ 
'A', {(A1,1),(A2,2),(A3,3)} 
'B', {(A1,4),(A2,5),(A3,6)} 
'C', {(A1,7),(A2,8),(A3,9)} 
} 

所以在GridView會像

--------------------------- -----------
filename | A1 | A2 | A3
--------------------------------------
A | 1 | 2 | 3
B | 4 | 5 | 6
C | 7 | 8 | 9

+0

1.爲什麼哦爲什麼你想要在代碼中做到這一點? XAML有什麼問題?那麼,你的問題是什麼?有什麼異常?任何投訴?更具體的... – Noctis

+0

我只想將內部字典的值綁定到一列,我無法這樣做,因爲在如何引用內部字典的值(結果[xyz] .values()。value ) – shivank

+0

你能簡化你的問題嗎?目前還不清楚你想要什麼。你是否想爲字典中的每個鍵創建一個列(目前存儲的字典無關緊要)?那麼每個**行的值是多少? – elgonzo

回答

15

好的,在GridView中顯示嵌套字典...這可能需要一些時間和相當多的文本,所以你可能會自己煮一些新鮮的熱咖啡。準備?好吧,讓我們開始吧。

A GridView基本上只是一個ListView控件的視圖模式。 ListView控件可視化對象/值的集合(或列表)(不管這些對象可能是什麼)。 這與將要顯示的字典相關的內容將在一分鐘內解釋。

如前所述,要顯示的數據存儲在嵌套字典中。具體詞典日期類型被指定爲如下:

Dictionary< string, Dictionary<string, int > > 

(外)字典的鍵表示一個文件名和應在GridView的「文件名」列中顯示。 與每個文件名關聯的(內部)字典將包含第二列和其他列的值。

如上面指定的字典是一個實現這個接口

ICollection< KeyValuePair< string, Dictionary<string, int > > > 

的這實際上是需要的正是ListView控件 - 元素的集合。這個集合中的每個元素是這種類型的:

KeyValuePair< string, Dictionary<string, int > > 

在KeyValuePair的關鍵是文件名。 KeyValuePair的是包含第二個和更多列的鍵/值對的(內部)字典。 這意味着,每個KeyValuePair元素都包含恰好一個完整行的數據。

現在,更難的部分開始。對於每一列,都需要訪問來自KeyValuePair的適當數據。 不幸的是,它不適用於每一列的簡單綁定。

但是,KeyValuePair本身可以綁定到每一列,並且藉助定製的IValueConverter可以從KeyValuePair提取所需的信息。我們將不得不做出決定。我們可以使用靜態數量的不可修改列的相對簡單的方法。 或者,如果我們的目的是使動態設置,添加或刪除列變得容易,我們會投入更多的編碼工作。 下面將給出有關這兩種方法的概述。


1.簡單但靜態且不靈活的方式。

要訪問KeyValuePair(文件名)的,則不需要值轉換器。對密鑰屬性的簡單綁定就足夠了。

要訪問存儲在KeyValuePair(內部)字典中的值,將使用自定義IValueConverter。 值轉換器將需要知道要提取的值的關鍵。這是作爲一個公共財產DictionaryKey實施的,它允許在XAML中指定密鑰。

[ValueConversion(typeof(KeyValuePair<string, Dictionary<string, int>>), typeof(string))] 
    public class GetInnerDictionaryValueConverter : IValueConverter 
    { 
     public string DictionaryKey { get; set; } 

     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (!(value is KeyValuePair<string, Dictionary<string, int>>)) 
       throw new NotSupportedException(); 

      Dictionary<string, int> innerDict = ((KeyValuePair<string, Dictionary<string, int>>) value).Value; 
      int dictValue; 
      return (innerDict.TryGetValue(DictionaryKey, out dictValue)) ? (object) dictValue : string.Empty; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 


的XAML ListView控件可能再看看類似如下。假設要顯示的列是「FileName」,「A1」,「A2」,「A3」(後三個是內部字典中的鍵)。 還要注意三個自定義GetInnerDictionaryValueConverter實例,它們被創建爲靜態資源並且正在被相應的綁定使用。

<ListView x:Name="MyListGridView"> 
    <ListView.Resources> 
     <My:GetInnerDictionaryValueConverter x:Key="ConverterColum_A1" DictionaryKey="A1" /> 
     <My:GetInnerDictionaryValueConverter x:Key="ConverterColum_A2" DictionaryKey="A2" /> 
     <My:GetInnerDictionaryValueConverter x:Key="ConverterColum_A3" DictionaryKey="A3" /> 
    </ListView.Resources> 
    <ListView.View> 
     <GridView AllowsColumnReorder="true"> 
      <GridViewColumn Header="FileName" DisplayMemberBinding="{Binding Key}" /> 
      <GridViewColumn Header="A1" DisplayMemberBinding="{Binding Converter={StaticResource ConverterColum_A1}}" /> 
      <GridViewColumn Header="A2" DisplayMemberBinding="{Binding Converter={StaticResource ConverterColum_A2}}" /> 
      <GridViewColumn Header="A3" DisplayMemberBinding="{Binding Converter={StaticResource ConverterColum_A3}}" /> 
     </GridView> 
    </ListView.View> 
</ListView> 

所有剩下要做的就是分配的實際字典的數據到ListView控件的的ItemsSource財產。這可以通過XAML中的數據綁定或代碼隱藏完成。

如前所述,這種方法相當直接且易於實現。缺點是列的數量是固定的,因爲列數據綁定所需的值轉換器被聲明爲靜態資源。 如果需要動態設置,添加或刪除任意DictionaryKeys的列,我們需要取消那些靜態轉換器。這導致我們採用第二種方法...


2。多一點點複雜,但允許動態容易設置/添加/刪除列

允許動態設置,添加,刪除任意DictionaryKeys列,自定義GetInnerDictionaryValueConverter可能被使用之前出臺,但是代碼 - 後面可能會變得有些複雜。 更好的方法是定義自定義的GridViewColumn類型,它也可以實現任何所需的IValueConverter邏輯並負責設置綁定。 不再需要單獨的自定義值轉換器類型,這將簡化對這些列的處理。

關於我們的示例,只需要兩個自定義列。第一個自定義列是文件名,它看起來很簡單:

public class GridViewColumnFileName : GridViewColumn 
{ 
    public GridViewColumnFileName() 
    { 
     DisplayMemberBinding = new Binding("Key") 
     { 
      Mode = BindingMode.OneWay 
     }; 
    } 
} 

它所做的只是在代碼隱藏中設置綁定。 您可能會指出,我們也可以像前面的示例一樣使用「{綁定鍵}」綁定來保留簡單的GridViewColumn - 並且您完全正確。 我只在這裏展示這個實現來說明可能的方法。

表示來自內部詞典的值的列的自定義列類型稍微複雜一些。 作爲之前的自定義值轉換器,此自定義列需要知道正在顯示的值的鍵。 通過將此接口作爲此自定義列類型的一部分實現,此列類型可將其自身用作綁定的值轉換器。

[ValueConversion(typeof(KeyValuePair<string, Dictionary<string, int>>), typeof(string))] 
public class GridViewColumnInnerDictionaryValue : GridViewColumn, IValueConverter 
{ 
    public string InnerDictionaryKey 
    { 
     get { return _key; } 
     set 
     { 
      if (_key == value) return; 

      _key = value; 

      if (string.IsNullOrWhiteSpace(value)) 
      { 
       DisplayMemberBinding = null; 
      } 
      else if (DisplayMemberBinding == null) 
      { 
       DisplayMemberBinding = new Binding() 
       { 
        Mode = BindingMode.OneWay, 
        Converter = this 
       }; 
      } 
     } 
    } 

    private string _key = null; 

    #region IValueConverter 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if (value is KeyValuePair<string, Dictionary<string, int>>) 
     { 
      Dictionary<string, int> innerDict = ((KeyValuePair<string, Dictionary<string, int>>) value).Value; 
      int dictValue; 
      return (innerDict.TryGetValue(InnerDictionaryKey, out dictValue)) ? (object) dictValue : string.Empty; 
     } 

     throw new NotSupportedException(); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    #endregion IValueConverter 
} 

IValueConverter接口的實現與以前完全相同。 當然,從之前GetInnerDictionaryValueConverter類型可以被用來代替執行其邏輯GridViewColumnInnerDictionaryValue, 的一部分,但再次,我想證明不同的可能性。

使用自定義字段類型看起來像這樣在創建XAML ListView控件:

<ListView x:Name="MyListGridView"> 
    <ListView.View> 
     <GridView AllowsColumnReorder="true"> 
      <My:GridViewColumnFileName Header="FileName" /> 
      <My:GridViewColumnInnerDictionaryValue Header="A1" InnerDictionaryKey="A1" /> 
      <My:GridViewColumnInnerDictionaryValue Header="A2" InnerDictionaryKey="A2" /> 
      <My:GridViewColumnInnerDictionaryValue Header="A3" InnerDictionaryKey="A3" /> 
     </GridView> 
    </ListView.View> 
</ListView> 

相反的聲明在XAML列,添加和從GridView.Columns列移除到/財產也可以在代碼隱藏中輕鬆完成。 再次,不要忘記通過XAML中的數據綁定或代碼隱藏來將ListView的ItemsSource屬性設置爲字典。

+0

非常感謝這樣一個美麗的解釋,它只是不可能是更好的,謝謝噸... – shivank

+2

這必須是最徹底的答案我已經閱讀過stackoverflow! – pfeds

+1

+1,爲您提供完善的解決方案。 – Krishna