C# 在WPF C中用字典绑定GridView#

C# 在WPF C中用字典绑定GridView#,c#,wpf,visual-studio,gridview,dictionary,C#,Wpf,Visual Studio,Gridview,Dictionary,我试图以编程方式将字典绑定到WPF(C#)中的GridView中。字典的结构是- 字典(字符串,字典(字符串,int)) 我能够将主词典的键绑定到GridView Dictionary<string, Dictionary<string, int>> result GridView myGrid = new GridView(); GridViewColumn gc = new GridViewColumn(); gc.Header = "File Name";

我试图以编程方式将字典绑定到WPF(C#)中的GridView中。字典的结构是-
字典(字符串,字典(字符串,int))
我能够将主词典的键绑定到GridView

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就像

--------------------------------------
文件名| A1 | A2 | A3
--------------------------------------
A | 1 | 2 | 3
B|4|5|6

C | 7 | 8 | 9

好的,在GridView中显示嵌套字典。。。这可能需要一些时间和大量的文字,所以你可以自己煮一些新鲜的热咖啡。准备好的好的,让我们开始吧

A基本上只是ListView控件的视图模式。ListView控件可视化对象/值的集合(或列表)(无论这些对象可能是什么)。 这与将要显示的词典的关系将在一分钟内解释

如前所述,要显示的数据存储在嵌套字典中。具体的字典日期类型指定如下:

Dictionary< string, Dictionary<string, int > >
它所做的只是在代码隐藏中设置绑定。 您可能会指出,我们还可以像前面的示例中那样使用“{Binding Key}”绑定来保持简单的GridViewColumn,您是绝对正确的。 我在这里展示这个实现只是为了说明可能的方法

表示内部字典中的值的列的自定义列类型要复杂一些。 与之前的自定义值转换器一样,此自定义列需要知道要显示的值的键。 通过将IValueConverter接口作为此自定义列类型的一部分实现,此列类型可以将自身用作绑定的值转换器

[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
}
[ValueConversion(typeof(KeyValuePair)、typeof(string))]
公共类GridViewColumnInnerDictionaryValue:GridViewColumn,IValueConverter
{
公共字符串InnerDictionaryKey
{
获取{return\u key;}
设置
{
if(_key==value)返回;
_键=值;
if(string.IsNullOrWhiteSpace(value))
{
DisplayMemberBinding=null;
}
else if(DisplayMemberBinding==null)
{
DisplayMemberBinding=新绑定()
{
Mode=BindingMode.OneWay,
转换器=此
};
}
}
}
私有字符串_key=null;
#区域变换器
公共对象转换(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
如果(值为KeyValuePair)
{
字典innerDict=((KeyValuePair)值).value;
int值;
返回(innerDict.TryGetValue(InnerDictionaryKey,out-dictValue))?(对象)dictValue:string.Empty;
}
抛出新的NotSupportedException();
}
公共对象转换回(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
抛出新的NotImplementedException();
}
#端区变换器
}
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属性设置为字典。

如果列不是固定的,并且不想在XAML中硬编码,另一个选项是将数据打包到DataTable中,并用自动列显示DataGrid

    private static DataTable DictionaryToDataTable(
        Dictionary<string, Dictionary<string, int>> values, 
        string fixedColumn)
    {

        DataTable result = new DataTable();
        result.Columns.Add(fixedColumn);

        IEnumerable<string> headers = values.SelectMany(row => row.Value)
            .Select(cell => cell.Key).Distinct();
        headers.ForEach(b => result.Columns.Add(b));

        foreach (KeyValuePair<string, Dictionary<string, int>> row in values)
        {
            DataRow dataRow = result.NewRow();
            dataRow[fixedColumn] = row.Key;
            foreach (KeyValuePair<string, int> cell in row.Value)
            {
                dataRow[cell.Key] = cell.Value;
            }

            result.Rows.Add(row);
        }

        return result;
    }
专用静态数据表字典(
字典值,
字符串(固定列)
{
DataTable结果=新DataTable();
result.Columns.Add(fixedColumn);
IEnumerable headers=values.SelectMany(行=>row.Value)
.Select(cell=>cell.Key).Distinct();
headers.ForEach(b=>result.Columns.Add(b));
foreach(值中的KeyValuePair行)
{
DataRow DataRow=result.NewRow();
dataRow[fixedColumn]=行.键;
foreach(行中的KeyValuePair单元格。值)
{
dataRow[cell.Key]=cell.Value;
}
结果.行.添加(行);
}
返回结果;
}
那么Xaml非常简单:

<DataGrid ItemsSource="{Binding MyDataTableProperty}" AutoGenerateColumns="True" />


1。为什么,为什么你想用代码来做?XAML有什么问题?2.那么,你有什么问题?有例外吗?有什么抱怨吗?更具体地说……我只想将内部字典的值绑定到一列,但我无法做到这一点,因为在如何引用内部字典的值(结果[xyz].values().value)中,您能稍微简化一下您的问题吗?你想要什么还不清楚。是否要为字典中的每个键创建一列(字典存储在何处现在无关紧要)?那么每行的值是多少?很抱歉,我在问题中给出了字典结构,这是一个错误。我用DataGrid做了类似的事情。这并不是简单而直接的,因为您不能直接绑定到字典的值(您需要这个键)。它涉及数据单元的转换器,该转换器使用与特定列相关联的键,提取字典(th)的相应值
<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>
public class GridViewColumnFileName : GridViewColumn
{
    public GridViewColumnFileName()
    {
        DisplayMemberBinding = new Binding("Key")
        {
            Mode = BindingMode.OneWay
        };
    }
}
[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
}
<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>
    private static DataTable DictionaryToDataTable(
        Dictionary<string, Dictionary<string, int>> values, 
        string fixedColumn)
    {

        DataTable result = new DataTable();
        result.Columns.Add(fixedColumn);

        IEnumerable<string> headers = values.SelectMany(row => row.Value)
            .Select(cell => cell.Key).Distinct();
        headers.ForEach(b => result.Columns.Add(b));

        foreach (KeyValuePair<string, Dictionary<string, int>> row in values)
        {
            DataRow dataRow = result.NewRow();
            dataRow[fixedColumn] = row.Key;
            foreach (KeyValuePair<string, int> cell in row.Value)
            {
                dataRow[cell.Key] = cell.Value;
            }

            result.Rows.Add(row);
        }

        return result;
    }
<DataGrid ItemsSource="{Binding MyDataTableProperty}" AutoGenerateColumns="True" />