We are evaluating the Silverlight data visualization controls. So far we are very happy with how the pivot grid, charts and data selector controls works together in displaying and working with data. However, up to this point we have only worked with predefined (at compile time) object classes where the data properties are known.
As we understand the controls read the data source (we use the flat data source) and derives the available data properties from the object class present in the data set delivered.
But we also have another scenario where the properties of the object class to visualize is not known at compile time. Based on conditions in the database the data object to visualize must be built dynamically at run time, and can have any number of dimensions and measures.
.NET framework allows to add object class properties at run time using the TypeDescriptionProvider API in System.ComponentModel. This could solve our problem, but unfortunately this API is not available for Silverlight applications. Instead we have option of using the DynamicObject API and using TryGetMember function to return dynamic property values.
However, the latter requires that the Infragistics data controls know which properties are available to be able to query their values. Is there a way to tell for instance the data selector which dimension and measure properties are available on the data objects in the data source? Or is there another approach to supply a data source with data objects having a arbitrary number of dimensions and measures?
Hello,
Is there any chance to try the DynamicTypeBuilder in conjunction with DynamicTypePropertyInfo and DynamicCustomAttributeBuilder classes. You can find them in InfragisticsSL4.Olap.v10.2.dll / InfragisticsWPF4.Olap.v10.2.dll.
Your suggestion about DynamicObject is very interesting but I’m not sure when we can include functionality like this.
Here is a sample how you can use the DynamicTypeBuilder:
DynamicTypeBuilder typeBuilder =
new DynamicTypeBuilder
{
DynamicAssemblyName = "YourDynamicAssemblyName",
DynamicTypeName = "YourDynamicTypeName"
};
For each property you need you have to create instance of DynamicPropertyInfo class:
DynamicTypePropertyInfo propertyInfo =
new DynamicTypePropertyInfo
PropertyName = "YourDynamicTypePropertyName",
PropertyType = yourPropertyType
You also can decorate the property with attributes in order to propagate the metadata to the data selector. Display attribute like this one can be used for numeric types in order to set how the cell data is formatted.
DynamicCustomAttributeBuilder dynamicCustomAttributeBuilder =
new DynamicCustomAttributeBuilder(typeof(DisplayFormatAttribute))
NamedProperties = new[] { "DataFormatString" },
NamedPropertiesValues = new[] { "{0:C2}" }
For other type of properties where dimensions are created you can set DisplayAttribute in order to override the names displayed in data selector’s tree:
new DynamicCustomAttributeBuilder(typeof(DisplayAttribute))
NamedProperties = new[] { "Name" },
NamedPropertiesValues = new[] { "YourFriendlyPropertyName" }
Then add the attribute to attributes collection of the dynamic property
propertyInfo.DynamicCustomAttributeBuilders.Add(dynamicCustomAttributeBuilder);
Create an instance of your dynamic type:
Type dynamicType = typeBuilder.GenerateType(dynamicProperties);
Type listType = typeof(List<>);
Type genericListType = listType.MakeGenericType(dynamicType);
IList list = (IList)Activator.CreateInstance(genericListType);
Then for each data instance you have you need to create a instance of the dynamic type:
object myDynamicInstance = Activator.CreateInstance(dynamicType);
And set the values of its properties for each dynamic property you have:
PropertyInfo propertyInfo = dynamicType.GetProperty(propName);
propertyInfo.SetValue(myDynamicInstance, [yourPropertyValue], null);
Add the dynamic instance to the generic list we’ve created already:
list.Add(myDynamicInstance);
Set the list as FlatDataSource.ItemsSource.
I hope that will cover your needs but please let me know if you have any ideas or additional questions.
Regards.
Plamen.
Hi,
I created a behaviour using dynamic properties using examples in this thread. XamPivotGrid/XamPivotDataSelector still does not work(does not show anything in the selector). The documentation is not helpful too. Could you please look into the code below and suggest if i am missing something.
Thanks!
public class XamPivotDataSelectorBehavior : Behavior<XamPivotDataSelector> {
public Type ModelType { get { return (Type)(GetValue(ModelTypeProperty));} set { SetValue(ModelTypeProperty, value);} }
public static readonly DependencyProperty ModelTypeProperty = DependencyProperty.Register("ModelType", typeof(Type), typeof(XamPivotDataSelectorBehavior));
public IEnumerable DataSource { get { return (IEnumerable)GetValue(DataSourceProperty); } set { SetValue(DataSourceProperty, value); } }
public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", typeof(IEnumerable), typeof(XamPivotDataSelectorBehavior));
private static void DataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is XamPivotDataSelectorBehavior) { var obj = d as XamPivotDataSelectorBehavior; if (obj != null) { try { var grid = obj.AssociatedObject as XamPivotDataSelector; obj.DataSourceChanged(grid); // point datasource to flat source, no need to rebind
} catch (Exception ex) { } } } }
public static readonly DependencyProperty GridDataChangedProperty = DependencyProperty.Register("GridDataChanged", typeof(bool), typeof(XamPivotDataSelectorBehavior), new PropertyMetadata(DataSourceChanged));
public bool GridDataChanged { get { return (bool)GetValue(GridDataChangedProperty); } set { SetValue(GridDataChangedProperty, value); } }
protected virtual void DataSourceChanged(XamPivotDataSelector selector) { if (selector.DataSource == null) { var dataSource = new FlatDataSource();
selector.DataSource = dataSource;
}
((FlatDataSource)selector.DataSource).ItemsSource = GenerateTypedList(DataSource);
protected virtual IList GenerateTypedList(IEnumerable dataSource) {
DynamicTypeBuilder typeBuilder = new DynamicTypeBuilder { DynamicTypeName = ModelType.FullName, DynamicAssemblyName = "TrendsAssembly" };
var dynamicPropertyList = new List<DynamicTypePropertyInfo>(); foreach (PropertyInfo propertyInfo in ModelType.GetProperties(BindingFlags.Public)) { DynamicTypePropertyInfo dtpi = new DynamicTypePropertyInfo(); dtpi.PropertyName = propertyInfo.Name; dtpi.PropertyType = propertyInfo.PropertyType; dynamicPropertyList.Add(dtpi);
var dynamicType = typeBuilder.GenerateType(dynamicPropertyList);
//var type = dataSource.GetType().GetGenericArguments().First();
Type listType = typeof(List<>); Type genericListType = listType.MakeGenericType(dynamicType); var list = (IList)Activator.CreateInstance(genericListType);
foreach (var item in dataSource) {
var dynamicInstance = Activator.CreateInstance(dynamicType);
foreach (var pi in dynamicType.GetProperties(BindingFlags.Public)) { var modelPI = ModelType.GetProperty(pi.Name); pi.SetValue(dynamicInstance, modelPI.GetValue(item,null),null);
list.Add(dynamicInstance); }
return list;