This is a migrated thread and some comments may be shown as answers.

Binding to a Dictionary

16 Answers 612 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Andries
Top achievements
Rank 1
Andries asked on 19 Mar 2009, 02:30 PM

What would the ultimate approach be for binding the gridview to a collection of Dictionary<string, object of which the key would be my column and object be my value? The reason for this approach was that my datasource is not always the same structure, meaning that depending on my selection, the first scenario could contain 5 columns with 10 rows, and my second scenario 10 columns with 100 rows. Unfortunately making using of an strong typed object as part of the INotifyableCollection is not an option, leaving me with constructing the columns and then the associated rows behind the scenes without binding?

For example :

First scenario

Dictionary<"name", "John"
Dictionary<"height", 1.82
Dictionary<"weight", 190

Dictionary<"name", "Mary"
Dictionary<"height", 1.72
Dictionary<"weight", 160>

Needs to render like this:

name    height    weight
------------------------------
John    1.82        190
Mary    1.72        160

Second scenario

Dictionary<"name", "John"
Dictionary<"dog's name", "Winston"

Dictionary<"name", "Mary"
Dictionary<"dog's name", "Spotty"

Needs to render like this:

name    dog's name
-------------------------
John     Winston
Mary     Spotty

I went through your documentation, but could not find any alternative ways of constructing columns and rows programmatically since the scenarios is too complex for standard binding operations? So the question would be how to convert a collection which contains a dictionary of column names and values at runtime to bind to the gridview?

Regards
Andries

 

 

 

 

 

 

16 Answers, 1 is accepted

Sort by
0
Pavel Pavlov
Telerik team
answered on 20 Mar 2009, 11:14 AM
Hi Andries,

Indeed this is not an ordinary scenario. However I am pretty sure we can think of something.
 
Can you please send me a sample piece of code that mocks your data (e.g. a c# file with some implementation of your dictionary and some data  populated in it).  Thus I can provide you with a sample application to display your data in RadGridView.

Greetings,
Pavel Pavlov
the Telerik team


Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
0
Andries
Top achievements
Rank 1
answered on 23 Mar 2009, 11:35 AM
Hi Pavel,

Thanks for coming back to me. Unfortunately we are not allowed to post attachments to the forum, so I'll just paste some code here:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Net;  
using System.Windows;  
using System.Windows.Controls;  
using System.Windows.Documents;  
using System.Windows.Input;  
using System.Windows.Media;  
using System.Windows.Media.Animation;  
using System.Windows.Shapes;  
using Telerik.Windows.Controls;  
using Telerik.Windows.Controls.GridView;  
 
namespace columns_sample  
{  
    public partial class GridProxy : UserControl  
    {  
        RadGridView DataGridObject;  
        List<Layer> Layers;  
 
        public GridProxy()  
        {  
            InitializeComponent();  
 
            this.Loaded += new RoutedEventHandler(GridProxy_Loaded);  
        }  
 
        void GridProxy_Loaded(object sender, RoutedEventArgs e)  
        {  
            this.DataGridObject = this.testGridView;  
 
            // Create the columns based on the data and binding happens here..  
            // The columns to be displayed will be all the attributes  
            // For example purposes, only the last column of the atreributes are different.  
            // Also important to note that the attributes (which will be the columns and rows for the grid) will all be the same for a given layer  
 
            Layers = GetLayers();  
 
            // This binding method is not correct and should return all the attributes for this layer  
            // NOT BINDABLE {this.DataGridObject.ItemsSource = Layers[0];}  
 
            // need to itterate through the data structure and construct the columns and rows manually  
            foreach (LayerData ld in Layers[0].Data)  
            {  
                // First create the columns (Only once)  
 
                // Then add the rows..   
 
                //GridRowView row = ld.Attributes;  
                //this.DataGridObject.Items.Add(row);  
            }  
        }  
 
        private List<Layer> GetLayers()  
        {  
            // 1)  First get the data as below:  
 
            List<Layer> layers = new List<Layer>();  
 
            for (int i = 1; i <= 2; i++)  
            {  
                layers.Add(  
                    new Layer(  
                        i,  
                        string.Format("Layer {0}", i),  
                        GetLayerData(i)  
                    )  
                );  
 
            }  
 
            return layers;  
        }  
 
        private List<LayerData> GetLayerData(int layerId)  
        {  
            List<LayerData> data = new List<LayerData>();  
 
            for (int j = 1; j <= 2; j++)  
            {  
                Dictionary<stringobject> attributes = new Dictionary<stringobject>();  
 
                attributes.Add("ID", j);  
                attributes.Add("Description"string.Format("Description: {0}", j));  
 
                switch (layerId)  
                {  
                    case 1:  
                        attributes.Add("Quantity"new Random().Next(500));  
                        break;  
 
                    case 2:  
                        attributes.Add("Address"string.Format("Address: {0}", j));  
                        break;  
                }  
 
                data.Add(new LayerData(true, attributes));  
            }  
 
            return data;  
        }  
    }  
 
    public class Layer  
    {  
        public int LayerId { getset; }  
 
        public string Description { getset; }  
 
        public List<LayerData> Data { getset; }  
 
        public Layer() { }  
 
        public Layer(int layerId, string description, List<LayerData> data)  
        {  
            this.LayerId = layerId;  
            this.Description = description;  
            this.Data = data;  
        }  
    }  
 
    public class LayerData  
    {  
        public bool IsSelected { getset; }  
 
        public Dictionary<stringobject> Attributes { getset; }  
 
        public LayerData() { }  
 
        public LayerData(bool isSelectecd, Dictionary<stringobject> attributes)  
        {  
            this.IsSelected = isSelectecd;  
            this.Attributes = attributes;  
        }  
    }  
      
}  
 

Depending on which layer is selected, the grid must display the associated attributes for that layer which can contain multiple rows. Let me know if there is anything else you would require? Please note that the attributes (which is suppose to render the columns and rows) will always be same per selected layer. The example above assumes that only the first layer is selected.

Regards
Andries 
0
Pavel Pavlov
Telerik team
answered on 25 Mar 2009, 12:17 PM
Hello Andries,

Thanks for the source code provided.
Please find the attached sample. It is based on your code. 
It will build columns runtime based on the contents of dictionaries. Also it demonstrates how to bind directly to your data. The conversion "magic" is hidden in a simple custom value converter.


Sincerely yours,
Pavel Pavlov
the Telerik team

Check out Telerik Trainer , the state of the art learning tool for Telerik products.
0
Andries
Top achievements
Rank 1
answered on 25 Mar 2009, 03:53 PM
Hi Pavel,

Works 100%. Such a simple, but magic conversion. 

Thanks.
0
Richard Pense
Top achievements
Rank 1
answered on 24 Jun 2010, 06:09 PM
Pavel Pavlov,

Great example. 
Can it be updated to support twoway binding?

Thanks,
Rick
0
Kurt Mang
Top achievements
Rank 1
answered on 24 Jun 2010, 06:43 PM
Yes, great example. Now: what if the Dictionary looks like: <string, ObservableCollection<MyBusinessObject>>?

That's my problem, and I cannot get it working. In my case the collection is just OC<string>, but the grid just tries to bind the entire collection into the individual cells. No good.

ViewModel: FieldItems = ObservableCollection<FieldItem> [I want this to be my grid's ItemsSource]

FieldItem: Name = string [I want this to be the column header]
Results = ObservableCollection<string> [I want these to be the individual row column data items]

At runtime, I loop through the MainCollection items, create a column for each and create the binding in code

Thanks for any help / insight.

Kurt - Vancouver BC
0
Kurt Mang
Top achievements
Rank 1
answered on 24 Jun 2010, 11:23 PM
I solved this problem by using Vlad's DataTable solution: http://blogs.telerik.com/vladimirenchev/posts/09-04-23/lightweight_datatable_for_your_silverlight_applications.aspx

Works great in SL4, Q12010 controls. Awesome!

Kurt
0
Benjamin Piepiora
Top achievements
Rank 1
answered on 23 Jul 2010, 04:36 PM
Hello Pavel,

very nice solution. Binding is quite a powerful technique. Now, is it possible to use a RadDataFilter on your specially bound grid view? I've tried a few things, but it didn't seem to work. Is there something I have to care for when I use the RadDataFilter?

Regards,
Benjamin
0
Rossen Hristov
Telerik team
answered on 29 Jul 2010, 08:16 AM
Hello Benjamin Piepiora,

What have you tried? Could you be more specific?

By the way have you seen our online examples and documentation concerning RadDataFilter?

Best wishes,
Ross
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Benjamin Piepiora
Top achievements
Rank 1
answered on 29 Jul 2010, 09:13 AM

Hello Ross,

 

thanks for your reply. I have already seen your documentation and examples. They helped me to try some things out but I was not able to get the binding work properly.

The following things I tried:

private Binding PrepareDataMemberBinding(string key)
{
    return new Binding()
    {
        Path = new PropertyPath("Attributes"),
        Converter = new DictionaryConverter(key)
    };
}
private function BindDataGrid()
{
    // radSourceObjectGridView is RadDataGrid
    // radDataFilter is RadDataFilter ;)
  
    foreach (var item in _originalObjectFeatureSet.Features[0].Attributes)
    {
        var dataColumn = new GridViewDataColumn()
        {
            Header = item.Key,
            DataType = item.Value.GetType(),
            DataMemberBinding = PrepareDataMemberBinding(item.Key)
        };
        radSourceObjectGridView.Columns.Add(dataColumn);
    }
  
    //_originalObjectFeatureSet.Features contains a Dictionary<string, object> Attributes from where the data comes
    radSourceObjectGridView.ItemsSource = _originalObjectFeatureSet.Features;
      
      
    //Now I want to use the RadDataFilter to filter the RadGridView but I have no idea how to do this
      
      
    // 1. Binding doesn't work the right way
    radDataFilter.Source = _originalObjectFeatureSet.Features
    radSourceObjectGridView.ItemsSource = radDataFilter.FilteredSource;
      
      
    // 2. I also tried to generate a special binding without success
    radDataFilter.Source = MakeSpecialBinding(...);
      
      
    // 3. The next idea was to implement a self-written filter logic ...
    var filterPropertyList = new List<ItemPropertyInfo>();
      
    foreach (var item in _originalObjectFeatureSet.Features[0].Attributes)
    {
        var filterFieldInfo = new ItemPropertyInfo(item.Key, item.Value.GetType(), null);
        filterPropertyList.Add(filterFieldInfo);
    }
      
    radDataFilter.ItemProperties = filterPropertyList;
    radDataFilter.FilterDescriptors.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FilterDescriptors_CollectionChanged);
    radDataFilter.FilterDescriptors.ItemChanged += new EventHandler<ItemChangedEventArgs<IFilterDescriptor>>(FilterDescriptors_ItemChanged);
      
    // ... but Silverlight hangs if I use code like this
    radSourceObjectGridView.FilterDescriptors.Clear();
    foreach (var item in radDataFilter.FilterDescriptors)
    {
        radSourceObjectGridView.FilterDescriptors.Add(item);
    }
}

Now I try to go another way by using an anonymous object class where the data from the dictionary is copied to:

class AnonObject
{
    StringOrDouble Field01;
    StringOrDouble Field02;
    StringOrDouble Field03;
    ...
}

It is not a very nice solution because we lose a bit of the dynamic behaviour.

Regards,

Benjamin

0
Rossen Hristov
Telerik team
answered on 29 Jul 2010, 09:38 AM
Hi Benjamin Piepiora,

Have you tried the other way around:

radSourceObjectGridView.ItemsSource = _originalObjectFeatureSet.Features;
radDataFilter.Source = radSourceObjectGridView.Items;

Also, do not listen to RadDataFilter.FilterDescriptors.CollectionChanged. You should not need that. This is only used when RadDataFilter is in Unbound Mode, i.e. has no Source, just like in the Domain Data Source online example. By listening to the collection changed and touching the grid's filters you are effectively creating an infinite loop.

Also, if you decide to use RadDataFilter -- turn off the grid's filtering. They should not be mixed together since they are two alternative ways to filter.

Maybe this can helps.

Sincerely yours,
Ross
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Benjamin Piepiora
Top achievements
Rank 1
answered on 29 Jul 2010, 01:54 PM
Hi Ross,

I tried the other way but the filter uses the fields of

_originalObjectFeatureSet.Features

and not the Attributes Field of Features Property. I thought the RadDataFilter uses the resulting data in the RadDataGrid to filter the grid but maybe for performance or design reasons its not implemented this way and the filter uses the source data.

The other problem is that there are many Attribute Dictionaries in the Features Field so it's not clear which one has to be used for the filter dropdown box.

Any other ideas? Maybe I should try the version with the Anonymous Object...

Regards,
Benjamin
0
Rossen Hristov
Telerik team
answered on 29 Jul 2010, 02:07 PM
Hello Benjamin Piepiora,

The filter is not aware of any data. It's only job is to add and remove filter descriptors. It "understands" the IQueryableCollectionView interface which has two important properties -- ItemProperties and FilterDescriptors. From the ItemProperties property RadDataFilter understands what to show in its combo-boxes. So this is kind of input for it. On the other hand the "output" is written to the FilterDescriptors property.

Since RadGridView.Items implement this interface -- you can link the filter directly to the Items. This will cause it to read the ItemProperties from the ItemsSource of the grid. Also, when you perform filtering, it will filter the ItemsSource of the grid.

In case you cannot use this architecture, my advice is to use the filter in its UnboundMode. This is clearly demonstrated in the DomainDataSource example. Have you checked it?

When the filter is unbound you have absolute freedom to pass-in any kind of ItemProperties. The control will adjust its drop-downs according to them. The you will wait for the output which is coming through the FilterDescriptors property and from then on -- it is your call what to do with that information. This is clearly demonstrated in the example I have mentioned several times.

The important thing to remember is: RadDataFilter is completely unaware of the actual data. It "accepts" ItemProperties and it "produces" FilterDescriptors. It does not care about data. This makes it totally de-coupled from RadGridView.

I hope this helps.

After reading the docs and getting acquainted with the online demos, in case you have a specific question let us know.

Greetings,
Ross
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Jürgen De Commer
Top achievements
Rank 1
answered on 05 Jan 2011, 04:24 PM
Just something I wanted to share on the subject.

In SL4 it's possible to bind on expressions with square brackets, so a simple GridViewDataColumn with a binding expression like Lookup[key] will do. Filtering, grouping and sorting work out of the box then.
One issue I had to deal with in our specific scenario was that not every object in the result set had the specified key, which resulted in a KeyNotFoundException. This was easily fixed by using a wrapper around the standard Dictionary, where we specified the [] operator.

public class LookupDictionary
    {
 
        private IDictionary<string, string> internalDictionary = new Dictionary<string, string>();
 
        public string this[string key]
        {
            get
            {
                string result = string.Empty;
                internalDictionary.TryGetValue(key, out result);
                return result;
            }
            set
            {
                internalDictionary[key] = value;
            }
        }

}
0
Marc
Top achievements
Rank 1
answered on 24 Aug 2011, 04:38 PM
hi jürgen,

if you ever read this, THANK YOU for sharing your tip! this was exactly was i was looking for.

stephan
0
Ilias
Top achievements
Rank 1
answered on 18 Dec 2012, 10:06 PM
Really impressing.

How can i add Grouping functionality here.

clubsGrid.GroupDescriptors.Add(new GroupDescriptor() { Member = "My Col" });

This seems not working.
Tags
GridView
Asked by
Andries
Top achievements
Rank 1
Answers by
Pavel Pavlov
Telerik team
Andries
Top achievements
Rank 1
Richard Pense
Top achievements
Rank 1
Kurt Mang
Top achievements
Rank 1
Benjamin Piepiora
Top achievements
Rank 1
Rossen Hristov
Telerik team
Jürgen De Commer
Top achievements
Rank 1
Marc
Top achievements
Rank 1
Ilias
Top achievements
Rank 1
Share this question
or