/// <summary> /// Initializes a new instance of the <see cref="DependencyPropertyValue{T}"/> class. /// </summary> /// <param name="owner">The dependency object that owns the property value.</param> /// <param name="property">The dependency property which has its value represented by this object.</param> public DependencyPropertyValue(DependencyObject owner, DependencyProperty property) { Contract.Require(owner, nameof(owner)); Contract.Require(property, nameof(property)); this.owner = owner; this.property = property; this.comparer = BindingExpressions.GetComparisonFunction(typeof(T)); this.metadata = property.GetMetadataForOwner(owner.GetType()); this.flags = DependencyPropertyValueFlags.None; this.defaultValue = (T)(metadata.DefaultValue ?? default(T)); if (typeof(T).GetInterfaces().Any(x => x == typeof(IResourceWrapper))) { flags |= DependencyPropertyValueFlags.IsResourceWrapper; } if (typeof(T).IsClass) { flags |= DependencyPropertyValueFlags.IsReferenceType; } if (typeof(T).IsValueType) { flags |= DependencyPropertyValueFlags.IsValueType; } if (metadata.HasDefaultValue && IsCoerced) { this.coercedValue = metadata.CoerceValue(owner, this.defaultValue); this.useDefaultValue = false; } UpdateRequiresDigest(GetValue()); }
/// <inheritdoc/> public override void Mutate(UltravioletContext uv, Object instance, Object value, UvmlInstantiationContext context) { var dobj = instance as DependencyObject; if (dobj == null) { return; } var expression = ProcessPrecomputedValue <String>(value, context); if (expression == null) { throw new UvmlException(PresentationStrings.InvalidBindingExpression.Format("(null)")); } var compiled = context.GetCompiledBindingExpression(dpropID.PropertyType, expression); if (compiled == null) { throw new UvmlException(PresentationStrings.CompiledExpressionNotFound.Format(expression)); } var fmtString = BindingExpressions.GetBindingFormatStringPart(expression); if (fmtString != null) { dobj.BindValue(dpropID, context.DataSourceType, "{{" + compiled.Name + "}}[" + fmtString + "]"); } else { dobj.BindValue(dpropID, context.DataSourceType, "{{" + compiled.Name + "}}"); } }
/// <summary> /// Initializes a new instance of the <see cref="DependencyBoundValueConverting{TDependency, TBound}"/> class. /// </summary> /// <param name="value">The dependency property value which created this object.</param> /// <param name="expressionType">The type of the bound expression.</param> /// <param name="dataSourceType">The type of the data source.</param> /// <param name="expression">The binding expression.</param> /// <param name="coerceToString">A value indicating whether to coerce Object values to String values if no valid type conversion exists.</param> public DependencyBoundValueConverting(IDependencyPropertyValue value, Type expressionType, Type dataSourceType, String expression, Boolean coerceToString) : base(value, expressionType, dataSourceType, expression) { var expressionFormatString = BindingExpressions.GetBindingFormatStringPart(expression); SetFormatString(expressionFormatString); this.coerceToString = coerceToString; this.cachedConvertedValue = GetFresh(); }
/// <summary> /// Creates a handler delegate for an event. /// </summary> /// <param name="name">The name of the method which will handle the event.</param> /// <param name="type">The type of delegate which handles the event.</param> /// <param name="context">The current instantiation context.</param> /// <returns>The delegate which was created to handle the event.</returns> protected static Delegate CreateEventHandlerDelegate(String name, Type type, UvmlInstantiationContext context) { var dataSource = context.DataSource; var dataSourceType = context.DataSourceType; var templatedParent = context.TemplatedParent as UIElement; if (templatedParent != null) { dataSource = templatedParent; PresentationFoundation.Instance.ComponentTemplates.Get(templatedParent, out dataSourceType); } return(BindingExpressions.CreateBoundEventDelegate(dataSource, dataSourceType, type, name)); }
/// <summary> /// Initializes a new instance of the <see cref="DependencyBoundValue{TDependency}"/> class. /// </summary> /// <param name="value">The dependency property value which created this object.</param> /// <param name="expressionType">The type of the bound expression.</param> /// <param name="dataSourceType">The type of the data source.</param> /// <param name="expression">The binding expression.</param> public DependencyBoundValue(IDependencyPropertyValue value, Type expressionType, Type dataSourceType, String expression) { this.dependencyValue = value; this.getter = (DataBindingGetter <T>)BindingExpressions.CreateBindingGetter(expressionType, dataSourceType, expression); this.setter = (DataBindingSetter <T>)BindingExpressions.CreateBindingSetter(expressionType, dataSourceType, expression); this.comparer = BindingExpressions.GetComparisonFunction(expressionType); this.cachedValue = GetUnderlyingValue(); this.dpropReference = BindingExpressions.GetSimpleDependencyProperty(dataSourceType, expression); if (dpropReference != null) { var dataSource = value.Owner.DependencyDataSource; if (dataSource != null) { HookDependencyProperty(dataSource); } } }
/// <summary> /// Initializes a new instance of the <see cref="BoundEventBuilder"/> type. /// </summary> /// <param name="dataSource">The data source to which the event is being bound.</param> /// <param name="dataSourceType">The type of the data source to which the expression is being bound.</param> /// <param name="delegateType">The type of delegate that will be created to bind to the event.</param> /// <param name="expression">The binding expression that represents the method to bind to the event.</param> public BoundEventBuilder(Object dataSource, Type dataSourceType, Type delegateType, String expression) : base(dataSourceType) { CreateParameters(delegateType); CreateReturnTarget(); var path = BindingExpressions.GetBindingMemberPathPart(expression, false); var current = AddDataSourceReference(expression, dataSource, dataSourceType); current = AddMethodInvocation(expression, current, path); AddReturn(); AddReturnLabel(); var lambdaBody = Expression.Block(variables, expressions); var lambda = Expression.Lambda(delegateType, lambdaBody, parameters); lambdaExpression = lambda; }
/// <summary> /// Creates the dependency property's cached bound value object. /// </summary> /// <param name="dataSourceType">The type of the data source to which the dependency property is bound.</param> /// <param name="expression">The dependency property's binding expression.</param> private void CreateCachedBoundValue(Type dataSourceType, String expression) { var expressionType = BindingExpressions.GetExpressionType(dataSourceType, expression); if (expressionType != null && TypesRequireSpecialConversion(expressionType, typeof(T))) { var coerce = typeof(T) == typeof(Object) && metadata.CoerceObjectToString; var valueType = typeof(DependencyBoundValueConverting <,>).MakeGenericType(typeof(T), expressionType); var valueCtor = default(Delegate); lock (cachedBoundValueCtors) cachedBoundValueCtors.TryGetValue(valueType, out valueCtor); if (valueCtor == null) { valueCtor = CreateDependencyPropertyBoundValueConvertingCtor(valueType); } var valueInstance = ((DependencyBoundValueConvertingCtor <T>)valueCtor)(this, expressionType, dataSourceType, expression, coerce); cachedBoundValue = valueInstance; } else { var valueType = typeof(DependencyBoundValueNonConverting <>).MakeGenericType(typeof(T)); var valueCtor = default(Delegate); lock (cachedBoundValueCtors) cachedBoundValueCtors.TryGetValue(valueType, out valueCtor); if (valueCtor == null) { valueCtor = CreateDependencyPropertyBoundValueNonConvertingCtor(valueType); } var valueInstance = ((DependencyBoundValueNonConvertingCtor <T>)valueCtor)(this, typeof(T), dataSourceType, expression); cachedBoundValue = valueInstance; } }
/// <summary> /// Initializes a new instance of the <see cref="DataBindingGetterBuilder"/> class. /// </summary> /// <param name="expressionType">The type of the bound expression.</param> /// <param name="dataSourceType">The type of the data source to which the expression is being bound.</param> /// <param name="expression">The binding expression with which to bind the dependency property.</param> public DataBindingGetterBuilder(Type expressionType, Type dataSourceType, String expression) : base(dataSourceType) { this.delegateType = typeof(DataBindingGetter <>).MakeGenericType(expressionType); if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { CreateReturnTarget(expressionType); var path = BindingExpressions.GetBindingMemberPathPart(expression); var pathParts = path.Contains(".") ? path.Split('.') : null; var pathFinalPart = (pathParts == null) ? path : pathParts[pathParts.Length - 1]; var current = AddDataSourceReference(); if (pathParts != null) { for (int i = 0; i < pathParts.Length - 1; i++) { current = AddSafeReference(expression, current, pathParts[i]); } } current = AddSafeReference(expression, current, pathFinalPart); var result = Expression.Convert(current, expressionType); AddReturn(result); AddReturnLabel(); var lambdaBody = Expression.Block(variables, expressions); var lambda = Expression.Lambda(delegateType, lambdaBody, parameters); lambdaExpression = lambda; } else { var expParamDataSource = Expression.Parameter(typeof(Object), "dataSource"); var implMethod = typeof(DataBindingGetterBuilder).GetMethod(nameof(ReflectionBasedImplementation), BindingFlags.NonPublic | BindingFlags.Static); var path = BindingExpressions.GetBindingMemberPathPart(expression); var pathParts = path.Contains(".") ? path.Split('.') : null; var pathFinalPart = (pathParts == null) ? path : pathParts[pathParts.Length - 1]; var expDataSource = (Expression)Expression.Convert(expParamDataSource, dataSourceType); var expDataSourceType = dataSourceType; var current = dataSourceType; if (pathParts != null) { for (int i = 0; i < pathParts.Length - 1; i++) { expDataSourceType = BindingExpressions.GetMemberType(BindingExpressions.FindPropertyOrField(expDataSourceType, pathParts[i])); expDataSource = Expression.PropertyOrField(expDataSource, pathParts[i]); } } var member = BindingExpressions.FindPropertyOrField(expDataSourceType, pathFinalPart); if (!BindingExpressions.CanReadMember(member)) { return; } var expImplMethodCall = Expression.Call(implMethod, Expression.Constant(member), Expression.Convert(expDataSource, typeof(Object))); lambdaExpression = Expression.Lambda(delegateType, Expression.Convert( Expression.Convert(expImplMethodCall, BindingExpressions.GetMemberType(member)), expressionType), expParamDataSource); } }
/// <summary> /// Searches the specified XML element tree for binding expressions and adds them to the specified collection. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="dataSourceDefinition">The data source definition for the data source which is being compiled.</param> /// <param name="dataSourceWrappedType">The type for which a data source wrapper is being compiled.</param> /// <param name="element">The root of the XML element tree to search.</param> /// <param name="expressions">The list to populate with any binding expressions that are found.</param> private static void FindBindingExpressionsInDataSource(IExpressionCompilerState state, DataSourceDefinition dataSourceDefinition, Type dataSourceWrappedType, XElement element, List <BindingExpressionInfo> expressions) { var templateAnnotation = element.Annotation <FrameworkTemplateNameAnnotation>(); if (templateAnnotation != null) { return; } var elementName = element.Name.LocalName; var elementType = UvmlTypeAnalysis.GetPlaceholderType(dataSourceWrappedType, elementName); if (elementType != null || state.GetKnownType(elementName, out elementType)) { var attrs = Enumerable.Union( element.Attributes().Select(x => new { Object = (XObject)x, Name = x.Name.LocalName, Value = x.Value }), element.Elements().Where(x => x.Name.LocalName.StartsWith(elementName + ".")).Select(x => new { Object = (XObject)x, Name = x.Name.LocalName, Value = x.Value })); foreach (var attr in attrs) { var attrValue = attr.Value; if (!BindingExpressions.IsBindingExpression(attrValue)) { continue; } var dprop = FindDependencyOrAttachedPropertyByName(state, attr.Name, elementType); if (dprop == null) { throw new BindingExpressionCompilationErrorException(attr.Object, dataSourceDefinition.DefinitionPath, CompilerStrings.OnlyDependencyPropertiesCanBeBound.Format(attr.Name)); } var expText = BindingExpressions.GetBindingMemberPathPart(attrValue); var expProp = GetBindablePropertyOnDataSource(dataSourceWrappedType, expText); var expType = expProp?.PropertyType ?? dprop.PropertyType; if (typeof(DataTemplate).IsAssignableFrom(expType)) { continue; } expressions.Add(new BindingExpressionInfo(attr.Object, attrValue, expType) { GenerateGetter = true }); } if (element.Nodes().Count() == 1) { var singleChild = element.Nodes().Single(); if (singleChild.NodeType == XmlNodeType.Text) { var elementValue = ((XText)singleChild).Value; if (BindingExpressions.IsBindingExpression(elementValue)) { String defaultProperty; if (!state.GetElementDefaultProperty(elementType, out defaultProperty) || defaultProperty == null) { throw new BindingExpressionCompilationErrorException(singleChild, dataSourceDefinition.DefinitionPath, CompilerStrings.ElementDoesNotHaveDefaultProperty.Format(elementType.Name)); } var dprop = FindDependencyOrAttachedPropertyByName(state, defaultProperty, elementType); if (dprop == null) { throw new BindingExpressionCompilationErrorException(singleChild, dataSourceDefinition.DefinitionPath, CompilerStrings.OnlyDependencyPropertiesCanBeBound.Format(defaultProperty)); } var expText = BindingExpressions.GetBindingMemberPathPart(elementValue); var expProp = GetBindablePropertyOnDataSource(dataSourceWrappedType, expText); expressions.Add(new BindingExpressionInfo(singleChild, elementValue, expProp?.PropertyType ?? dprop.PropertyType) { GenerateGetter = true }); } } } } var children = element.Elements(); foreach (var child in children) { FindBindingExpressionsInDataSource(state, dataSourceDefinition, dataSourceWrappedType, child, expressions); } }
/// <summary> /// Writes a property which wraps a binding expression. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="dataSourceWrapperInfo">A <see cref="DataSourceWrapperInfo"/> describing the data source for which to write an expression property.</param> /// <param name="expressionInfo">The binding expression for which to write a property.</param> /// <param name="id">The expression's identifier within the view model.</param> public void WriteExpressionProperty(ExpressionCompilerState state, DataSourceWrapperInfo dataSourceWrapperInfo, BindingExpressionInfo expressionInfo, Int32 id) { var isDependencyProperty = false; var isSimpleDependencyProperty = false; var expText = BindingExpressions.GetBindingMemberPathPart(expressionInfo.Expression); var expTarget = default(String); var expTargetType = dataSourceWrapperInfo.DataSourceType; var expFormatString = BindingExpressions.GetBindingFormatStringPart(expressionInfo.Expression); var targeted = GetExpressionTargetInfo(state, dataSourceWrapperInfo, expressionInfo.Source, ref expText, out expTarget, out expTargetType); var dprop = DependencyProperty.FindByName(expText, expTargetType); var dpropField = default(FieldInfo); if (dprop != null) { isDependencyProperty = true; dpropField = (from prop in dprop.OwnerType.GetFields(BindingFlags.Public | BindingFlags.Static) where prop.FieldType == typeof(DependencyProperty) && prop.GetValue(null) == dprop select prop).SingleOrDefault(); if (dpropField == null) { throw new BindingExpressionCompilationErrorException(expressionInfo.Source, dataSourceWrapperInfo.DataSourceDefinition.DefinitionPath, PresentationStrings.CannotFindDependencyPropertyField.Format(dprop.OwnerType.Name, dprop.Name)); } if (String.IsNullOrEmpty(expFormatString) && !targeted) { isSimpleDependencyProperty = true; } } WriteLine("[{0}(@\"{1}\", SimpleDependencyPropertyOwner = {2}, SimpleDependencyPropertyName = {3})]", typeof(CompiledBindingExpressionAttribute).FullName, expressionInfo.Expression.Replace("\"", "\"\""), isSimpleDependencyProperty ? "typeof(" + GetCSharpTypeName(dprop.OwnerType) + ")" : "null", isSimpleDependencyProperty ? "\"" + dprop.Name + "\"" : "null"); WriteLine("public {0} __UPF_Expression{1}", GetCSharpTypeName(expressionInfo.Type), id); WriteLine("{"); if (expressionInfo.GenerateGetter) { expressionInfo.GetterLineStart = LineCount; var getexp = default(String); if (isDependencyProperty) { getexp = String.Format("{0}GetValue<{1}>({2}.{3})", expTarget, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name); } else { getexp = String.Format("{0}{1}", expTarget, expText); } var hasFormatString = !String.IsNullOrEmpty(expFormatString); expFormatString = hasFormatString ? String.Format("\"{{0:{0}}}\"", expFormatString) : "null"; if (IsStringType(expressionInfo.Type) || (expressionInfo.Type == typeof(Object) && hasFormatString)) { WriteLine("get"); WriteLine("{"); WriteLine("var value = {0};", getexp); WriteLine("return ({0})__UPF_ConvertToString(value, {1});", GetCSharpTypeName(expressionInfo.Type), expFormatString); WriteLine("}"); } else { WriteLine("get {{ return ({0})({1}); }}", GetCSharpTypeName(expressionInfo.Type), getexp); } expressionInfo.GetterLineEnd = LineCount - 1; } if (dprop != null && dprop.IsReadOnly) { expressionInfo.GenerateSetter = false; } if (expressionInfo.GenerateSetter) { var targetTypeName = expressionInfo.CS0266TargetType; var targetTypeSpecified = !String.IsNullOrEmpty(targetTypeName); expressionInfo.SetterLineStart = LineCount; if (isDependencyProperty) { if (IsStringType(expressionInfo.Type)) { WriteLine("set"); WriteLine("{"); WriteLine("var current = {0}GetValue<{1}>({2}.{3});", expTarget, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name); WriteLine("{0}SetValue<{1}>({2}.{3}, __UPF_ConvertFromString(value.ToString(), current));", expTarget, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name); WriteLine("}"); } else { if (expressionInfo.NullableFixup) { WriteLine(targetTypeSpecified ? "set {{ {0}SetValue<{1}>({2}.{3}, ({4})(value ?? default({1}))); }}" : "set {{ {0}SetValue<{1}>({2}.{3}, value ?? default({1})); }}", expTarget, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name, targetTypeName); } else { WriteLine(targetTypeSpecified ? "set {{ {0}SetValue<{1}>({2}.{3}, ({4})(value)); }}" : "set {{ {0}SetValue<{1}>({2}.{3}, value); }}", expTarget, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name, targetTypeName); } } } else { if (expressionInfo.Type == typeof(String) || expressionInfo.Type == typeof(VersionedStringSource)) { WriteLine("set"); WriteLine("{"); WriteLine("var current = {0}{1};", expTarget, expText); WriteLine("{0}{1} = __UPF_ConvertFromString(value.ToString(), current);", expTarget, expText); WriteLine("}"); } else { if (expressionInfo.NullableFixup) { WriteLine(targetTypeSpecified ? "set {{ {0}{1} = ({3})(value ?? default({2})); }}" : "set {{ {0}{1} = value ?? default({2}); }}", expTarget, expText, GetCSharpTypeName(Nullable.GetUnderlyingType(expressionInfo.Type)), targetTypeName); } else { WriteLine(targetTypeSpecified ? "set {{ {0}{1} = ({2})(value); }}" : "set {{ {0}{1} = value; }}", expTarget, expText, targetTypeName); } } } expressionInfo.SetterLineEnd = LineCount - 1; } WriteLine("}"); }
/// <summary> /// Determines whether the specified target is a dependency property, routed event, or standard property/event. /// </summary> private static UvmlMutatorTarget GetMutatorTarget(UltravioletContext uv, String name, String value, Type type, out Object target, out Type targetType) { var upf = uv.GetUI().GetPresentationFoundation(); // If this is an attached property/event, find the owner type. var depname = new DependencyName(name); if (depname.IsAttached) { var attachedOwnerType = default(Type); if (!upf.GetKnownType(depname.Owner, out attachedOwnerType)) { throw new UvmlException(PresentationStrings.UnrecognizedType.Format(depname.Owner)); } type = attachedOwnerType; } // Is it a dependency property? var dprop = DependencyProperty.FindByName(depname.Name, type); if (dprop != null) { target = dprop; if (value != null && BindingExpressions.IsBindingExpression(value)) { targetType = typeof(String); return(UvmlMutatorTarget.DependencyPropertyBinding); } targetType = dprop.PropertyType; return(UvmlMutatorTarget.DependencyProperty); } // Is it a routed event? var revt = EventManager.FindByName(depname.Name, type); if (revt != null) { target = revt; targetType = typeof(String); return(UvmlMutatorTarget.RoutedEvent); } // Is it a standard property? var clrprop = type.GetProperty(depname.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (clrprop != null) { target = clrprop; targetType = clrprop.PropertyType; return(UvmlMutatorTarget.StandardProperty); } // Is it a standard event? var clrevt = type.GetEvent(depname.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (clrevt != null) { target = clrevt; targetType = typeof(String); return(UvmlMutatorTarget.StandardEvent); } throw new UvmlException(PresentationStrings.EventOrPropertyDoesNotExist.Format(depname.Name, type.Name)); }