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

[Solved] Dynamic GridViewExpressionColumn

4 Answers 193 Views
GridView
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Richard Harrigan
Top achievements
Rank 1
Richard Harrigan asked on 17 Jan 2011, 12:13 AM

Hi
In the demo we have the following:

 

 

Expression<Func<Products, double>> expression = prod => prod.UnitPrice * prod.UnitsInStock;

How would you do this when the ItemsSource is generated dynamically as in the DataTable class?

Thanks
Rich

 

4 Answers, 1 is accepted

Sort by
0
Yavor Georgiev
Telerik team
answered on 18 Jan 2011, 03:19 PM
Hello Richard Harrigan,

 Could you please tell me which implementation of DataTable for Silverlight you use?

Kind regards,
Yavor Georgiev
the Telerik team
Let us know about your Windows Phone 7 application built with RadControls and we will help you promote it. Learn more>>
0
Richard Harrigan
Top achievements
Rank 1
answered on 18 Jan 2011, 05:22 PM

I downloaded it a few month ago.  I don't know the version.  I will paste all the DataTable code files.  For later use how would I send you a zipped file.

The files are: DataTable.cs, DataRow.cs, DataColumn.cs, DynamicObject.cs, DynamicObjectBuilder.cs, TypeSignature.cs

Thanks
Rich

******************************************* DataTable.cs *************************************************************
  
using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Collections.ObjectModel;
  
namespace Telerik.Data
{
    public class DataTable : IEnumerable, INotifyCollectionChanged
    {
        private IList<DataColumn> columns;
        private ObservableCollection<DataRow> rows;
        private IList internalView;
        private Type elementType;
  
        public event NotifyCollectionChangedEventHandler CollectionChanged;
  
        public IList<DataColumn> Columns
        {
            get
            {
                if (columns == null)
                {
                    columns = new List<DataColumn>();
                }
  
                return columns;
            }
        }
  
          
        public IList<DataRow> Rows
        {
            get
            {
                if (this.rows == null)
                {
                    this.rows = new ObservableCollection<DataRow>();
                    this.rows.CollectionChanged += OnRowsCollectionChanged;
                }
  
                return rows;
            }
        }
  
        public DataRow NewRow()
        {
            return new DataRow(this);
        }
  
  
        private void OnRowsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    this.InternalView.Insert(e.NewStartingIndex, ((DataRow)e.NewItems[0]).RowObject);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    this.InternalView.RemoveAt(e.OldStartingIndex);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    this.InternalView.Remove(((DataRow) e.OldItems[0]).RowObject);
                    this.InternalView.Insert(e.NewStartingIndex, ((DataRow) e.NewItems[0]).RowObject);
                    break;
                case NotifyCollectionChangedAction.Reset:
                default:
                    this.InternalView.Clear();
                    this.Rows.Select(r => r.RowObject).ToList().ForEach(o => this.InternalView.Add(o));
                    break;
            }
        }
  
        private IList InternalView
        {
            get
            {
                if (this.internalView == null)
                {
                    this.CreateInternalView();
                }
  
                return this.internalView;
            }
        }
  
        private void CreateInternalView()
        {
            this.internalView = (IList) Activator.CreateInstance(typeof(ObservableCollection<>).MakeGenericType(this.ElementType));
            ((INotifyCollectionChanged) internalView).CollectionChanged += (s, e) => this.OnCollectionChanged(e);
        }
  
        internal Type ElementType
        {
            get
            {
                if (this.elementType == null)
                {
                    this.InitializeElementType();
                }
  
                return this.elementType;
            }
        }
  
        private void InitializeElementType()
        {
            this.Seal();
            this.elementType = DynamicObjectBuilder.GetDynamicObjectBuilderType(this.Columns);
        }
  
        private void Seal()
        {
            this.columns = new ReadOnlyCollection<DataColumn>(this.Columns);
        }
  
        public IEnumerator GetEnumerator()
        {
            return this.InternalView.GetEnumerator();
        }
  
        public IList ToList()
        {
            return this.InternalView;
        }
  
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var handler = this.CollectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }
}
  
*************************************************************  DataRow *************************************************************
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
  
namespace Telerik.Data
{
    public class DataRow
    {
        private readonly DataTable owner;
        private DynamicObject rowObject;
  
        protected internal DataRow(DataTable owner)
        {
            this.owner = owner;
        }
  
        public object this[string columnName]
        {
            get
            {
                return this.RowObject.GetValue<object>(columnName);
            }
            set
            {
                this.RowObject.SetValue(columnName, value);
            }
        }
  
        internal DynamicObject RowObject
        {
            get
            {
                this.EnsureRowObject();
                return this.rowObject; 
            }
        }
  
        private void EnsureRowObject()
        {
            if (this.rowObject == null)
            {
                this.rowObject = (DynamicObject) Activator.CreateInstance(this.owner.ElementType);
            }
        }
    }
}
  
  
*************************************************************** DataColumn.cs *******************************************************
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
  
namespace Telerik.Data
{
    public class DataColumn
    {
        public DataColumn()
        {
            this.DataType = typeof(object);
        }
  
        public Type DataType { get; set; }
        public string ColumnName { get; set; }
    }
}
  
**************************************************************** DynamicObject.cs *************************************************************
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
  
namespace Telerik.Data
{
    public abstract class DynamicObject : INotifyPropertyChanged
    {
        private readonly Dictionary<string, object> valuesStorage;
  
        public event PropertyChangedEventHandler PropertyChanged;
  
        protected DynamicObject()
        {
            this.valuesStorage = new Dictionary<string,object>();
        }
  
        protected internal virtual T GetValue<T>(string propertyName)
        {
            object value;
            if (!this.valuesStorage.TryGetValue(propertyName, out value))
            {
                return default(T);
            }
  
            return (T) value;
        }
  
        protected internal virtual void SetValue<T>(string propertyName, T value)
        {
            this.valuesStorage[propertyName] = value;
  
            this.RaisePropertyChanged(propertyName);
        }
  
        protected void RaisePropertyChanged(string propertyName)
        {
            this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
  
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            var hanlder = this.PropertyChanged;
            if (hanlder != null)
            {
                hanlder(this, args);
            }
        }
    }
}
  
  
*************************************************************** DynamicObjectBuilder.cs *************************************************
  
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
  
namespace Telerik.Data
{
    internal class DynamicObjectBuilder
    {
        private static readonly Dictionary<TypeSignature, Type> TypesCache = new Dictionary<TypeSignature, Type>();
  
        private static readonly AssemblyBuilder MicroModelAssemblyBuilder =
            AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DynamicObjects"), AssemblyBuilderAccess.Run);
  
        private static readonly ModuleBuilder MicroModelModuleBuilder =
            MicroModelAssemblyBuilder.DefineDynamicModule("DynamicObjectsModule", true);
  
        private static readonly MethodInfo GetValueMethod =
            typeof(DynamicObject).GetMethod("GetValue", BindingFlags.Instance | BindingFlags.NonPublic);
        private static readonly MethodInfo SetValueMethod =
            typeof(DynamicObject).GetMethod("SetValue", BindingFlags.Instance | BindingFlags.NonPublic);
  
        public static Type GetDynamicObjectBuilderType(IEnumerable<DataColumn> properties)
        {
            Type type;
            var signature = new TypeSignature(properties);
  
            if (!TypesCache.TryGetValue(signature, out type))
            {
                type = CreateDynamicObjectBuilderType(properties);
                TypesCache.Add(signature, type);
            }
  
            return type;
        }
  
        private static Type CreateDynamicObjectBuilderType(IEnumerable<DataColumn> columns)
        {
            var typeBuilder =
                MicroModelModuleBuilder.DefineType("DynamicObjectBuilder_" + Guid.NewGuid(), TypeAttributes.Public, typeof(DynamicObject));
  
            foreach (var property in columns)
            {
                var propertyBuilder = typeBuilder.DefineProperty(property.ColumnName, PropertyAttributes.None, property.DataType, null);
  
                CreateGetter(typeBuilder, propertyBuilder, property);
                CreateSetter(typeBuilder, propertyBuilder, property);
            }
  
            return typeBuilder.CreateType();
  
        }
  
        private static void CreateGetter(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, DataColumn column)
        {
            var getMethodBuilder = typeBuilder.DefineMethod(
                "get_" + column.ColumnName,
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName,
                CallingConventions.HasThis,
                column.DataType, Type.EmptyTypes);
  
            var getMethodIL = getMethodBuilder.GetILGenerator();
            getMethodIL.Emit(OpCodes.Ldarg_0);
            getMethodIL.Emit(OpCodes.Ldstr, column.ColumnName);
            getMethodIL.Emit(OpCodes.Callvirt, GetValueMethod.MakeGenericMethod(column.DataType));
            getMethodIL.Emit(OpCodes.Ret);
  
            propertyBuilder.SetGetMethod(getMethodBuilder);
        }
  
        private static void CreateSetter(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, DataColumn column)
        {
            var setMethodBuilder = typeBuilder.DefineMethod(
                "set_" + column.ColumnName,
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName,
                CallingConventions.HasThis,
                null, new[] { column.DataType });
  
            var setMethodIL = setMethodBuilder.GetILGenerator();
            setMethodIL.Emit(OpCodes.Ldarg_0);
            setMethodIL.Emit(OpCodes.Ldstr, column.ColumnName);
            setMethodIL.Emit(OpCodes.Ldarg_1);
            setMethodIL.Emit(OpCodes.Callvirt, SetValueMethod.MakeGenericMethod(column.DataType));
            setMethodIL.Emit(OpCodes.Ret);
  
            propertyBuilder.SetSetMethod(setMethodBuilder);
        }
    }
}
  
  
****************************************************** TypeSignature ************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
  
namespace Telerik.Data
{
    internal class TypeSignature : IEquatable<TypeSignature>
    {
        private readonly int hashCode;
  
        public TypeSignature(IEnumerable<DataColumn> columns)
        {
            this.hashCode = 0;
            foreach (var column in columns.OrderBy(p => p.ColumnName))
            {
                this.hashCode ^= column.ColumnName.GetHashCode() ^ column.DataType.GetHashCode();
            }
        }
  
        public override bool Equals(object obj)
        {
            return ((obj is TypeSignature) && this.Equals((TypeSignature) obj));
        }
  
        public bool Equals(TypeSignature other)
        {
            return this.hashCode.Equals(other.hashCode);
        }
  
        public override int GetHashCode()
        {
            return this.hashCode;
        }
    }
}




0
Yavor Georgiev
Telerik team
answered on 20 Jan 2011, 01:06 PM
Hi Richard Harrigan,

 In this case you will have to build the expression tree to perform the calculation on your own, like this:
var parameter = Expression.Parameter(dataTable.ElementType);
var myPropertyAccess = Expression.Property("MyProperty", parameter);
var expressionLambda = Expression.Lambda(myPropertyAccess, parameter);
column.Expression = expressionLambda;

All the best,
Yavor Georgiev
the Telerik team
Let us know about your Windows Phone 7 application built with RadControls and we will help you promote it. Learn more>>
0
Richard Harrigan
Top achievements
Rank 1
answered on 20 Jan 2011, 03:40 PM
Hi,

This code snippet is over my pay grade.

What would the code look like for the following:

GridViewExpressionColumn  Balance = BudgetColumn - ActualColumn

Thanks
Rich
Tags
GridView
Asked by
Richard Harrigan
Top achievements
Rank 1
Answers by
Yavor Georgiev
Telerik team
Richard Harrigan
Top achievements
Rank 1
Share this question
or