/// <summary> /// Writes trace output for a binding failure plus triggers the event <see cref="BindingDiagnostics.BindingFailed"/>. /// The event will not be triggered if the TraceEventType is filtered out. This overload allows specific trace and event parameters to be included. /// </summary> /// <param name="binding">The binding is only part of the event, not the trace message.</param> public static void TraceAndNotify(TraceEventType eventType, AvTraceDetails traceDetails, BindingExpressionBase binding, object[] traceParameters, object[] eventParameters = null) { string traceOutput = _avTrace.Trace(eventType, traceDetails.Id, traceDetails.Message, traceDetails.Labels, traceParameters); if (traceOutput != null && BindingDiagnostics.IsEnabled) { BindingDiagnostics.NotifyBindingFailed(new BindingFailedEventArgs(eventType, traceDetails.Id, traceOutput, binding, eventParameters)); } }
void SetupPart(TypeInfo sourceType, BindingExpressionPart part) { part.Arguments = null; part.LastGetter = null; part.LastSetter = null; PropertyInfo property = null; if (part.IsIndexer) { if (sourceType.IsArray) { if (!int.TryParse(part.Content, out var index)) { BindingDiagnostics.SendBindingFailure(Binding, "Binding", ParseIndexErrorMessage, part.Content, sourceType); } else { part.Arguments = new object[] { index } }; part.LastGetter = sourceType.GetDeclaredMethod("Get"); part.LastSetter = sourceType.GetDeclaredMethod("Set"); part.SetterType = sourceType.GetElementType(); } string indexerName = "Item"; foreach (DefaultMemberAttribute attrib in sourceType.GetCustomAttributes(typeof(DefaultMemberAttribute), true)) { indexerName = attrib.MemberName; break; } part.IndexerName = indexerName; property = GetIndexer(sourceType, indexerName, part.Content); if (property != null) { ParameterInfo parameter = null; ParameterInfo[] array = property.GetIndexParameters(); if (array.Length > 0) { parameter = array[0]; } if (parameter != null) { try { object arg = Convert.ChangeType(part.Content, parameter.ParameterType, CultureInfo.InvariantCulture); part.Arguments = new[] { arg }; } catch (FormatException) { } catch (InvalidCastException) { } catch (OverflowException) { } } } } else { TypeInfo type = sourceType; while (type != null && property == null) { property = type.GetDeclaredProperty(part.Content); type = type.BaseType?.GetTypeInfo(); } } if (property != null) { if (property.CanRead && property.GetMethod.IsPublic && !property.GetMethod.IsStatic) { part.LastGetter = property.GetMethod; } if (property.CanWrite && property.SetMethod.IsPublic && !property.SetMethod.IsStatic) { part.LastSetter = property.SetMethod; var lastSetterParameters = part.LastSetter.GetParameters(); part.SetterType = lastSetterParameters[lastSetterParameters.Length - 1].ParameterType; if (Binding.AllowChaining) { FieldInfo bindablePropertyField = sourceType.GetDeclaredField(part.Content + "Property"); if (bindablePropertyField != null && bindablePropertyField.FieldType == typeof(BindableProperty) && sourceType.ImplementedInterfaces.Contains(typeof(IElementController))) { MethodInfo setValueMethod = null; #if NETSTANDARD1_0 foreach (MethodInfo m in sourceType.AsType().GetRuntimeMethods()) { if (m.Name.EndsWith("IElementController.SetValueFromRenderer")) { ParameterInfo[] parameters = m.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(BindableProperty)) { setValueMethod = m; break; } } } #else setValueMethod = typeof(IElementController).GetMethod("SetValueFromRenderer", new[] { typeof(BindableProperty), typeof(object) }); #endif if (setValueMethod != null) { part.LastSetter = setValueMethod; part.IsBindablePropertySetter = true; part.BindablePropertyField = bindablePropertyField.GetValue(null); } } } } #if !NETSTANDARD1_0 if (property != null && part.NextPart != null && property.PropertyType.IsGenericType && (property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <,>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <, ,>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <, , ,>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <, , , ,>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <, , , , ,>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <, , , , , ,>) || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple <, , , , , , ,>)) && property.GetCustomAttribute(typeof(TupleElementNamesAttribute)) is TupleElementNamesAttribute tupleEltNames) { //modify the nextPart to access the tuple item via the ITuple indexer var nextPart = part.NextPart; var name = nextPart.Content; var index = tupleEltNames.TransformNames.IndexOf(name); if (index >= 0) { nextPart.IsIndexer = true; nextPart.Content = index.ToString(); } } #endif } }
/// <summary> /// Applies the binding expression to a previously set source or target. /// </summary> void ApplyCore(object sourceObject, BindableObject target, BindableProperty property, bool fromTarget = false) { BindingMode mode = Binding.GetRealizedMode(_targetProperty); if ((mode == BindingMode.OneWay || mode == BindingMode.OneTime) && fromTarget) { return; } bool needsGetter = (mode == BindingMode.TwoWay && !fromTarget) || mode == BindingMode.OneWay || mode == BindingMode.OneTime; bool needsSetter = !needsGetter && ((mode == BindingMode.TwoWay && fromTarget) || mode == BindingMode.OneWayToSource); object current = sourceObject; BindingExpressionPart part = null; for (var i = 0; i < _parts.Count; i++) { part = _parts[i]; if (!part.IsSelf && current != null) { // Allow the object instance itself to provide its own TypeInfo TypeInfo currentType = current is IReflectableType reflectable?reflectable.GetTypeInfo() : current.GetType().GetTypeInfo(); if (part.LastGetter == null || !part.LastGetter.DeclaringType.GetTypeInfo().IsAssignableFrom(currentType)) { SetupPart(currentType, part); } if (i < _parts.Count - 1) { part.TryGetValue(current, out current); } } if (!part.IsSelf && current != null && ((needsGetter && part.LastGetter == null) || (needsSetter && part.NextPart == null && part.LastSetter == null))) { BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", PropertyNotFoundErrorMessage, part.Content, current, target.GetType(), property.PropertyName); break; } if (part.NextPart != null && (mode == BindingMode.OneWay || mode == BindingMode.TwoWay) && current is INotifyPropertyChanged inpc) { part.Subscribe(inpc); } } Debug.Assert(part != null, "There should always be at least the self part in the expression."); if (needsGetter) { if (part.TryGetValue(current, out object value) || part.IsSelf) { value = Binding.GetSourceValue(value, property.ReturnType); } else { value = Binding.FallbackValue ?? property.GetDefaultValue(target); } if (!TryConvert(ref value, property, property.ReturnType, true)) { BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", CannotConvertTypeErrorMessage, value, property.ReturnType); return; } target.SetValueCore(property, value, SetValueFlags.ClearDynamicResource, BindableObject.SetValuePrivateFlags.Default | BindableObject.SetValuePrivateFlags.Converted); } else if (needsSetter && part.LastSetter != null && current != null) { object value = Binding.GetTargetValue(target.GetValue(property), part.SetterType); if (!TryConvert(ref value, property, part.SetterType, false)) { BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", CannotConvertTypeErrorMessage, value, part.SetterType); return; } object[] args; if (part.IsIndexer) { args = new object[part.Arguments.Length + 1]; part.Arguments.CopyTo(args, 0); args[args.Length - 1] = value; } else if (part.IsBindablePropertySetter) { args = new[] { part.BindablePropertyField, value }; } else { args = new[] { value }; } part.LastSetter.Invoke(current, args); } }