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
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.
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<string, object> attributes = new Dictionary<string, object>(); |
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 { get; set; } |
public string Description { get; set; } |
public List<LayerData> Data { get; set; } |
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 { get; set; } |
public Dictionary<string, object> Attributes { get; set; } |
public LayerData() { } |
public LayerData(bool isSelectecd, Dictionary<string, object> 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
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.
Works 100%. Such a simple, but magic conversion.
Thanks.
Great example.
Can it be updated to support twoway binding?
Thanks,
Rick
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
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
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
Have you tried the other way around:
radSourceObjectGridView.ItemsSource = _originalObjectFeatureSet.Features
;
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.
Sincerely yours,
Ross
the Telerik team
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
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
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;
}
}
}
if you ever read this, THANK YOU for sharing your tip! this was exactly was i was looking for.
stephan
How can i add Grouping functionality here.
clubsGrid.GroupDescriptors.Add(new GroupDescriptor() { Member = "My Col" });
This seems not working.