5
votes

I'm having trouble binding my Dictionary<string, List<string>> to my WPF datagrid with the correct layout.

This is my data object:

public class Result
{
    public Result(string type, string name, Dictionary<string, List<string>> partners)
    {
        Type = type;
        Name = name;
        Partners = partners;
    }

    public string Type { get; set; }
    public string Name { get; set; }
    public Dictionary<string, List<string>> Partners { get; set; }
}

Which I have populated with dummy data and exposed as a public property of my viewmodel:

public class MainViewModel
{
    public MainViewModel()
    {   
        var partners = new Dictionary<string, List<string>>
        {
            {"Company", new List<string> {"company1", "company2"}},
            {"Operator", new List<string> {"false", "true"}},
            {"Interest", new List<string> {"40%", "60%"}},
            {"Type", new List<string> {"type1", "type2"}}
        };
        Asset = new Result("TestType", "TestName", partners);
    }

    public Result Asset { get; set; }
}

I am trying to bind the Partners dictionary to my datagrid. The Key of each dictionary entry (e.g. Company / Operator / Interest / Type) should make up the headers of my datagrid, with the respective values filling the columns. Like this:

Company | Operator | Interest | Type
company1   false       40%      type1
company2   true        60%      type2

How can I achieve this? I've been looking at this question but the data is the other way round from how I want it. I want my keys as the datagrid column headers (I don't mind hardcoding the headers as I believe the header property can't use data binding). This is my XAML so far, itemsource is bound correctly, but I don't know how to get the data in the format I need:

<DataGrid x:Name="dg" ItemsSource="{Binding Asset.Partners}" AutoGenerateColumns="false">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Company" Binding="{Binding ?}"/>
        <DataGridTextColumn Header="Operator" Binding="{Binding ?}"/>
        <DataGridTextColumn Header="Interest" Binding="{Binding ?}"/>
        <DataGridTextColumn Header="Type" Binding="{Binding ?}"/>
    </DataGrid.Columns>
</DataGrid>

If I use Binding="{Binding Value[0]} for the first column, then I get a "row" of values in my column, but I want it the other way round. I've also tried using Binding="{Binding Asset.Partners[Company]}" but that didn't work.

Any help appreciated.

2
Don't use dictionaries in bindings. They suck for a number of different reasons.. Create a specific type to hold your key/value pairs and put them into an ObservableCollection. If you're a nerdly programmer, you can create an implementation of KeyedCollection<T> that implements INotifyCollectionChanged, which gives you the benefits of a dictionary but without the massive suck.\user1228
@Will - it's not particularly helpful to tell me they "suck" without telling me why.Courlu
They suck for a number of reasons, at least one of which you've encountered. Others include hobbling the power of DataTemplates, no change notification, etc. They suck, as you have learned. Frankly, what's more important is my advice about the keyed collection. Go look into that, and don't waste your time with dictionaries.user1228

2 Answers

3
votes

Personally I would create an object model to represent your data, and bind to a collection of those objects.

public class MyObject
{
    public string Company { get; set; }
    public string Operator { get; set; }
    public string Interest { get; set; }
    public string Type { get; set; }
}

and

public MainViewModel()
{   
    var partners = new ObservableCollection<MyObject>()
    {
        new MyObject("company1", "false", "40%", "type1"),
        new MyObject("company2", "true", "60%", "type2")
    };
    ...
}

I don't think what you're asking for is possible with a normal DataGrid because it reads things as "for each row in collection, create a DataRow and assign the DataContext to that row". In your case, each row represents the entire grid of data, so this does not work for you.

1
votes

@courlu Everything is possible with dictionary also but most of people prefer ObservableCollection. Dictionary also inherited with IEnumerable and ICollection which tell you that dictionary also able to assign as ItemSource for wpf controls.

Genneric code of Dictionary provided by .Net : public class Dictionary : IDictionary, ICollection>, IEnumerable>, IEnumerable, IDictionary, ICollection, IReadOnlyDictionary, IReadOnlyCollection>, ISerializable, IDeserializationCallback { }

so you need to do below changes in your code

<DataGrid x:Name="dg" ItemsSource="{Binding Asset.Partners.Values}" AutoGenerateColumns="false">
<DataGrid.Columns>
    <DataGridTextColumn Header="Company" Binding="{Binding Company}"/>
    <DataGridTextColumn Header="Operator" Binding="{Binding Operator}"/>
    <DataGridTextColumn Header="Interest" Binding="{Binding Interest}"/>
    <DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
</DataGrid.Columns>

and if you want TwoWay mode binding then inherit dictionary class with this 2 interface INotifyCollectionChanged, INotifyPropertyChanged it will support 2 way binding also like ObservableCollection.