using System; using System.Linq.Expressions; using System.Reflection; namespace Artemis.Core { /// /// Represents a data binding converter that acts as the bridge between a /// and a /// public abstract class DataBindingConverter : IDataBindingConverter { /// /// Gets a dynamically compiled getter pointing to the data bound property /// public Func GetExpression { get; private set; } /// /// Gets a dynamically compiled setter pointing to the data bound property used for value types /// public Action ValueTypeSetExpression { get; private set; } /// /// Gets a dynamically compiled setter pointing to the data bound property used for reference types /// public Action ReferenceTypeSetExpression { get; private set; } /// /// Gets the data binding this converter is applied to /// public DataBinding DataBinding { get; private set; } /// /// Gets whether or not this data binding converter supports the method /// public bool SupportsSum { get; protected set; } /// /// Gets whether or not this data binding converter supports the method /// public bool SupportsInterpolate { get; protected set; } /// public Type SupportedType => typeof(TProperty); /// /// Returns the sum of and /// public abstract TProperty Sum(TProperty a, TProperty b); /// /// Returns the the interpolated value between and on a scale (generally) /// between 0.0 and 1.0 defined by the /// Note: The progress may go be negative or go beyond 1.0 depending on the easing method used /// /// The value to interpolate away from /// The value to interpolate towards /// The progress of the interpolation between 0.0 and 1.0 /// public abstract TProperty Interpolate(TProperty a, TProperty b, double progress); /// /// Applies the to the layer property /// /// public virtual void ApplyValue(TProperty value) { if (ReferenceTypeSetExpression != null) ReferenceTypeSetExpression(DataBinding.LayerProperty.CurrentValue, value); else if (ValueTypeSetExpression != null) ValueTypeSetExpression(value); } /// /// Returns the current base value of the data binding /// public virtual TProperty GetValue() { return GetExpression(DataBinding.LayerProperty.CurrentValue); } /// /// Converts the provided object to a type of /// public virtual TProperty ConvertFromObject(object? source) { return (TProperty) Convert.ChangeType(source, typeof(TProperty)); } /// /// Called when the data binding converter has been initialized and the is available /// protected virtual void OnInitialized() { } internal void Initialize(DataBinding dataBinding) { DataBinding = dataBinding; GetExpression = dataBinding.Registration.PropertyExpression.Compile(); CreateSetExpression(); OnInitialized(); } private void CreateSetExpression() { // If the registration does not point towards a member of LayerProperty.CurrentValue, assign directly to LayerProperty.CurrentValue if (DataBinding.Registration.Member == null) { CreateSetCurrentValueExpression(); return; } // Ensure the member of LayerProperty.CurrentValue has a setter MethodInfo setterMethod = null; if (DataBinding.Registration.Member is PropertyInfo propertyInfo) setterMethod = propertyInfo.GetSetMethod(); // If there is no setter, the built-in data binding cannot do its job, stay null if (setterMethod == null) return; // If LayerProperty.CurrentValue is a value type, assign it directly to LayerProperty.CurrentValue after applying the changes if (typeof(TLayerProperty).IsValueType) CreateSetValueTypeExpression(); // If it is a reference type it can safely be updated by its reference else CreateSetReferenceTypeExpression(); } private void CreateSetReferenceTypeExpression() { ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); ParameterExpression parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue"); MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, DataBinding.Registration.Member); BinaryExpression assignment = Expression.Assign(memberAccess, propertyValue); Expression> referenceTypeLambda = Expression.Lambda>(assignment, parameter, propertyValue); ReferenceTypeSetExpression = referenceTypeLambda.Compile(); } private void CreateSetValueTypeExpression() { ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); ParameterExpression variableCurrent = Expression.Variable(typeof(TLayerProperty), "current"); ConstantExpression layerProperty = Expression.Constant(DataBinding.LayerProperty); MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); BlockExpression body = Expression.Block( new[] {variableCurrent}, Expression.Assign(variableCurrent, layerPropertyMemberAccess), Expression.Assign(Expression.MakeMemberAccess(variableCurrent, DataBinding.Registration.Member), propertyValue), Expression.Assign(layerPropertyMemberAccess, variableCurrent) ); Expression> valueTypeLambda = Expression.Lambda>(body, propertyValue); ValueTypeSetExpression = valueTypeLambda.Compile(); } private void CreateSetCurrentValueExpression() { ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); ConstantExpression layerProperty = Expression.Constant(DataBinding.LayerProperty); MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); BinaryExpression body = Expression.Assign(layerPropertyMemberAccess, propertyValue); Expression> lambda = Expression.Lambda>(body, propertyValue); ValueTypeSetExpression = lambda.Compile(); } } }