wpf binding dependency-properties coercion

wpf - ¿Cómo hago que Binding respete la coerción de valor de DependencyProperty?



dependency-properties coercion (3)

No creo que la devolución de llamada coercitiva sea una calle de doble sentido. Una solución alternativa sería actualizar el valor del modelo dentro de la devolución de llamada coercitiva.

Tengo un control con DependencyProperty con CoerceValueCallback. Esta propiedad está vinculada a una propiedad en un objeto modelo.

Al establecer la propiedad de control en un valor que causa la coerción, la vinculación empuja el valor no coercitivo al objeto modelo. El valor de la propiedad en el control se coacciona correctamente.

¿Cómo obtengo que el Enlace presione el valor forzado al objeto modelo?

void Initialize() { UIObject ui = new UIObject(); ModelObject m = new ModelObject(); m.P = 4; Binding b = new Binding("P"); b.Source = m; b.Mode = BindingMode.TwoWay; Debug.WriteLine("SetBinding"); // setting the binding will push the model value to the UI ui.SetBinding(UIObject.PProperty, b); // Setting the UI value will result in coercion but only in the UI. // The value pushed to the model through the binding is not coerced. Debug.WriteLine("Set to -4"); ui.P = -4; Debug.Assert(ui.P == 0); // The binding is TwoWay, the DP value is coerced to 0. Debug.Assert(m.P == 0); // Not true. This will be -4. Why??? } class UIObject : FrameworkElement { public static readonly DependencyProperty PProperty = DependencyProperty.Register("P", typeof(int), typeof(UIObject), new FrameworkPropertyMetadata( new PropertyChangedCallback(OnPChanged), new CoerceValueCallback(CoerceP))); public int P { get { return (int)GetValue(PProperty); } set { SetValue(PProperty, value); } } private static void OnPChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Debug.WriteLine(typeof(UIObject) + ".P changed from " + e.OldValue + " to " + e.NewValue); } private static object CoerceP(DependencyObject sender, object value) { int p = (int)value; if (p < 0) { Debug.WriteLine(typeof(UIObject) + ".P coerced from " + p + " to 0"); p = 0; } return p; } } class ModelObject { private int p; public int P { get { Debug.WriteLine(this + ".P returned " + this.p); return this.p; } set { Debug.WriteLine(this + ".P changed from +" + this.p + " to " + value); this.p = value; } } }


Creo que esa es la idea de la coacción: el valor correcto sobre la marcha sin activar la modificación de otras dependencias. Puede usar el siguiente código en lugar de los mecanismos de coerción nativa:

OnPChanged(/* ... */) { // ... var coercedP = CoerceP(P); if (P != coercedP) P = coercedP; // ... }

HTH.


Aquí está el método de extensión donde establece el valor en el objeto de destino

public static void SetTargetValue<T>(this FrameworkElement element, DependencyProperty dp, T value) { var binding = BindingOperations.GetBinding(element, dp); if (binding == null) return; var name = binding.Path.Path; var splits = name.Split(''.''); var target = element.DataContext; for (var i = 0; i < splits.Length; i++) { PropertyInfo property; if (i == splits.Length - 1) { property = target.GetType().GetProperty(splits[i]); property.SetValue(target, value); } else { property = target.GetType().GetProperty(splits[i]); target = property.GetValue(target); } } }

Entonces, en este método, usando el enlace, puede establecer el valor en el origen. Of source source Path puede tener muchos nombres: Property1.Property2.Property3 y etc. En el método de coerción, solo necesita llamar a este método:

private static object CoerceProperty(DependencyObject d, object baseValue) { if (!Check) { var sender = (FrameworkElement)d; sender.SetTargetValue(MyPropertyProperty, myValue); return needValue; } return baseValue; }