Click here to Skip to main content
15,884,472 members
Articles / Desktop Programming / WPF

Visual Expression Builder for Dynamic Linq

Rate me:
Please Sign up or sign in to vote.
4.73/5 (14 votes)
2 Feb 2012CPOL4 min read 111.8K   2.4K   96  
Let your users create their own Linq filters with this MVVM WPF tool you can include in your application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Linq.Expressions;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Controls;

namespace ChabadOnCampus.DynamicLinq
{
    public class ExpressionVM: INotifyPropertyChanged
    {

        IEnumerable<PropertyInfo> _AvailableProperties;
        public IEnumerable<PropertyInfo> AvailableProperties
        {
            get { return _AvailableProperties; }
            set
            {
                _AvailableProperties = value;
                OnPropertyChanged("AvailableProperties");
            }
        }

        public Array AvailableCompareOperators
        {
            get 
            {
                if (PropertyType == typeof(string))
                {
                    return new ComparisonOperators[] { ComparisonOperators.Equal, ComparisonOperators.NotEqual, ComparisonOperators.Contains };
                }
                else
                {
                    var operators = from o in Enum.GetValues(typeof(ComparisonOperators)).Cast<ComparisonOperators>()
                                    where o != ComparisonOperators.Contains // exclude "Contains" as only works for String
                                    select o;
                    return operators.ToArray();
                }
            }
        }
        public Array AvailableCombinationOperators
        {
            get { return Enum.GetValues(typeof(CombinationOperators)); }
        }

        private Type _ObjectType;
	    public Type ObjectType
	    {
		    get { return _ObjectType;}
            set { 
                _ObjectType = value;
                if (value == null)
                {
                    AvailableProperties = null;
                }
                else
                {
                    AvailableProperties = from p in value.GetProperties()
                                          where GetSupportedTypes().Contains(p.PropertyType) &&
                                          p.GetCustomAttributes(typeof(DoNotFilterOnAttribute), true).Length == 0 // in other words, where attribute is NOT applied
                                          select p;
                }
                OnPropertyChanged("ObjectType");
            }
	    }
	
        private ComparisonOperators _CompareOperator = ComparisonOperators.Equal;
        public ComparisonOperators CompareOperator
        {
            get { return _CompareOperator; }
            set { _CompareOperator = value; OnPropertyChanged("CompareOperator"); }
        }

        private CombinationOperators _CombineOperator = CombinationOperators.And;
        public CombinationOperators CombineOperator
        {
            get { return _CombineOperator; }
            set { _CombineOperator = value; OnPropertyChanged("CombineOperator"); }
        }

        private PropertyInfo _PropertyInfo;
        public PropertyInfo PropertyInfo
        {
            get { return _PropertyInfo; }
            set {
                _PropertyInfo = value;
                OnPropertyChanged("PropertyInfo");
                OnPropertyChanged("PropertyType");
                OnPropertyChanged("PropertyName");
                OnPropertyChanged("AvailableCompareOperators");
                OnPropertyChanged("Value2");
                OnPropertyChanged("ValueControl");
            }
        }
 	    public Type PropertyType
	    {
            get { return PropertyInfo == null? typeof(string) : PropertyInfo.PropertyType; }
	    }
        public string PropertyName
        {
            get { return PropertyInfo == null ? null : PropertyInfo.Name; }
        }

        public dynamic Value
        {
            get { return Value2.Value; }
            set
            {
                if (!Object.Equals(Value2.Value, value))
                {
                    Value2.Value = value;
                    OnPropertyChanged("Value");
                }
            }
        }
        private object _Value2;
        public dynamic Value2
        {
            get {
                Type specificType = typeof(Variant<>).MakeGenericType(new Type[] { PropertyType });
                if(_Value2 == null || _Value2.GetType() != specificType)
                {
                    _Value2 = Activator.CreateInstance(specificType);
                }
                return _Value2;
            }
        }

        public Control ValueControl 
        {
            get
            {
                Binding binding = new Binding("Value2.Value");
                binding.Source = this;

                if (PropertyType == typeof(DateTime) || PropertyType == typeof(DateTime?))
                {
                    DatePicker dp = new DatePicker();
                    dp.SetBinding(DatePicker.SelectedDateProperty, binding);
                    return dp;
                }
                else
                {
                    TextBox tb = new TextBox();
                    tb.SetBinding(TextBox.TextProperty, binding);
                    return tb;
                }
            }
        }

        public static object CreateGeneric(Type generic, Type innerType, params object[] args)
        {
            System.Type specificType = generic.MakeGenericType(new System.Type[] { innerType });
            return Activator.CreateInstance(specificType, args);
        }
        public LambdaExpression MakeExpression(ParameterExpression paramExpr = null)
        {
            if(paramExpr == null) paramExpr = Expression.Parameter(ObjectType, "left");
            
            var callExpr = Expression.MakeMemberAccess(paramExpr, PropertyInfo);
            var valueExpr = Expression.Constant(Value, PropertyType);
            Expression expr;

            if (CompareOperator == ComparisonOperators.Contains)
            {
                expr = Expression.Call(callExpr, PropertyType.GetMethod("Contains"), valueExpr);
            }
            else
            {
                expr = Expression.MakeBinary((ExpressionType)CompareOperator, callExpr, valueExpr);
            }
            
            return Expression.Lambda( expr, paramExpr);
        }

        public static IEnumerable<Type> GetSupportedTypes()
        {
            return new Type[] {typeof(DateTime), typeof(DateTime?),
                                          typeof(long), typeof(long?), typeof(short), typeof(short?), 
                                          typeof(ulong), typeof(ulong?), typeof(ushort), typeof(ushort?), 
                                          typeof(Single), typeof(Single?), typeof(Double), typeof(Double?), 
                                          typeof(Decimal), typeof(Decimal?), typeof(Boolean), typeof(Boolean?), 
                                          typeof(int), typeof(int?), typeof(uint), typeof(uint), 
                                          typeof(string)};
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string value)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(value));
        }
    }

    public class ExpressionList: ObservableCollection<ExpressionVM>
    {

        private Type _Type;
        public Type Type
        {
            get { return _Type; }
            set {
                _Type = value;
                
                foreach(ExpressionVM c in this)
                {
                    //c.AvailableProperties = props.ToArray().Clone() as PropertyInfo[];
                    c.ObjectType = value;
                }
            }
        }

        protected override void InsertItem(int index, ExpressionVM item)
        {
            base.InsertItem(index, item);
            if (item.ObjectType == null) item.ObjectType = Type;
        }

        public LambdaExpression CompleteExpression
        {
            get
            {
                var paramExp = Expression.Parameter(Type, "left");
                if (this.Count == 0) return Expression.Lambda(Expression.Constant(true), paramExp);
                LambdaExpression lambda1 = this.First().MakeExpression(paramExp);
                var ret = lambda1.Body;
                foreach (var c in this.Skip(1))
                    ret = Expression.MakeBinary((ExpressionType)c.CombineOperator, ret, c.MakeExpression(paramExp).Body);
                return Expression.Lambda(ret, paramExp);
            }
        }

        public Expression<Func<T, bool>> GetCompleteExpression<T>()
        {
            return this.CompleteExpression as Expression<Func<T, bool>>;
        }
    }

    public enum ComparisonOperators
    {
            Equal = ExpressionType.Equal,
            NotEqual = ExpressionType.NotEqual,
            LessThan = ExpressionType.LessThan,
            GreaterThan = ExpressionType.GreaterThan,
            LessThanOrEqual = ExpressionType.LessThanOrEqual,
            GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual,
            Contains
    }

    public enum CombinationOperators
    {
            Or = ExpressionType.Or,
            And = ExpressionType.And,
            Xor = ExpressionType.ExclusiveOr,
    }

    public struct Variant<T>
    {
        public T Value { get; set; }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Co-director of Chabad Student Center @ SUNY New Paltz
Lecturer of Computer Science @ SUNY New Paltz

Comments and Discussions