Binding to a Dictionary

17 posts, 0 answers
  1. Andries
    Andries avatar
    5 posts
    Member since:
    Mar 2009

    Posted 19 Mar 2009 Link to this post

    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

     

     

     

     

     

     

  2. Pavel Pavlov
    Admin
    Pavel Pavlov avatar
    2039 posts

    Posted 20 Mar 2009 Link to this post

    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.
  3. DevCraft banner
  4. Andries
    Andries avatar
    5 posts
    Member since:
    Mar 2009

    Posted 23 Mar 2009 Link to this post

    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 
  5. Pavel Pavlov
    Admin
    Pavel Pavlov avatar
    2039 posts

    Posted 25 Mar 2009 Link to this post

    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.
  6. Andries
    Andries avatar
    5 posts
    Member since:
    Mar 2009

    Posted 25 Mar 2009 Link to this post

    Hi Pavel,

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

    Thanks.
  7. Richard Pense
    Richard Pense avatar
    12 posts
    Member since:
    Apr 2009

    Posted 24 Jun 2010 Link to this post

    Pavel Pavlov,

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

    Thanks,
    Rick
  8. Kurt Mang
    Kurt Mang avatar
    12 posts
    Member since:
    Mar 2010

    Posted 24 Jun 2010 Link to this post

    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
  9. Kurt Mang
    Kurt Mang avatar
    12 posts
    Member since:
    Mar 2010

    Posted 24 Jun 2010 Link to this post

    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
  10. Benjamin Piepiora
    Benjamin Piepiora avatar
    5 posts
    Member since:
    Aug 2009

    Posted 23 Jul 2010 Link to this post

    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
  11. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 29 Jul 2010 Link to this post

    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
  12. Benjamin Piepiora
    Benjamin Piepiora avatar
    5 posts
    Member since:
    Aug 2009

    Posted 29 Jul 2010 Link to this post

    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

  13. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 29 Jul 2010 Link to this post

    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
  14. Benjamin Piepiora
    Benjamin Piepiora avatar
    5 posts
    Member since:
    Aug 2009

    Posted 29 Jul 2010 Link to this post

    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
  15. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 29 Jul 2010 Link to this post

    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
  16. Jürgen De Commer
    Jürgen De Commer avatar
    1 posts
    Member since:
    Mar 2010

    Posted 05 Jan 2011 Link to this post

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

    }
  17. Marc
    Marc avatar
    88 posts
    Member since:
    Jul 2011

    Posted 24 Aug 2011 Link to this post

    hi jürgen,

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

    stephan
  18. Ilias
    Ilias avatar
    9 posts
    Member since:
    Dec 2008

    Posted 18 Dec 2012 Link to this post

    Really impressing.

    How can i add Grouping functionality here.

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

    This seems not working.
Back to Top
DevCraft banner