Sí, he hecho esto en el pasado con las propiedades ActualWidthy ActualHeight, las cuales son de solo lectura. Creé un comportamiento adjunto que tiene ObservedWidthy ObservedHeightpropiedades adjuntas. También tiene una Observepropiedad que se utiliza para hacer la conexión inicial. El uso se ve así:
<UserControl ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"
Por lo que el modelo de vista tiene Widthy Heightpropiedades que están siempre en sincronía con las ObservedWidthy ObservedHeightunidos propiedades. La Observepropiedad simplemente se adjunta al SizeChangedevento de la FrameworkElement. En el identificador, actualiza sus ObservedWidthy sus ObservedHeightpropiedades. Ergo, el Widthy Heightdel modelo de vista siempre está sincronizado con el ActualWidthy ActualHeightdel UserControl.
Quizás no sea la solución perfecta (estoy de acuerdo, los DP de solo lectura deberían admitir OneWayToSourceenlaces), pero funciona y mantiene el patrón MVVM. Obviamente, los ObservedWidthy los ObservedHeightDP no son de solo lectura.
ACTUALIZACIÓN: aquí hay un código que implementa la funcionalidad descrita anteriormente:
public static class SizeObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(SizeObserver),
new FrameworkPropertyMetadata(OnObserveChanged));
public static readonly DependencyProperty ObservedWidthProperty = DependencyProperty.RegisterAttached(
"ObservedWidth",
typeof(double),
typeof(SizeObserver));
public static readonly DependencyProperty ObservedHeightProperty = DependencyProperty.RegisterAttached(
"ObservedHeight",
typeof(double),
typeof(SizeObserver));
public static bool GetObserve(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (bool)frameworkElement.GetValue(ObserveProperty);
}
public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObserveProperty, observe);
}
public static double GetObservedWidth(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedWidthProperty);
}
public static void SetObservedWidth(FrameworkElement frameworkElement, double observedWidth)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedWidthProperty, observedWidth);
}
public static double GetObservedHeight(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedHeightProperty);
}
public static void SetObservedHeight(FrameworkElement frameworkElement, double observedHeight)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedHeightProperty, observedHeight);
}
private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;
if ((bool)e.NewValue)
{
frameworkElement.SizeChanged += OnFrameworkElementSizeChanged;
UpdateObservedSizesForFrameworkElement(frameworkElement);
}
else
{
frameworkElement.SizeChanged -= OnFrameworkElementSizeChanged;
}
}
private static void OnFrameworkElementSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateObservedSizesForFrameworkElement((FrameworkElement)sender);
}
private static void UpdateObservedSizesForFrameworkElement(FrameworkElement frameworkElement)
{
// WPF 4.0 onwards
frameworkElement.SetCurrentValue(ObservedWidthProperty, frameworkElement.ActualWidth);
frameworkElement.SetCurrentValue(ObservedHeightProperty, frameworkElement.ActualHeight);
// WPF 3.5 and prior
////SetObservedWidth(frameworkElement, frameworkElement.ActualWidth);
////SetObservedHeight(frameworkElement, frameworkElement.ActualHeight);
}
}
Sizepropiedad, combinando Altura y Ancho. Aprox. 50% menos de código.ActualSizepropiedadFrameworkElement. Si desea la vinculación directa de las propiedades adjuntas, debe crear dos propiedades a las que vincularActualWidthyActualHeightrespectivamente.Utilizo una solución universal que funciona no solo con ActualWidth y ActualHeight, sino también con cualquier dato que pueda vincular al menos en modo de lectura.
El marcado tiene este aspecto, siempre que ViewportWidth y ViewportHeight sean propiedades del modelo de vista
Aquí está el código fuente de los elementos personalizados.
fuente
Targetpropiedad debe hacerse escribible aunque no se debe cambiar desde el exterior: - /Si alguien más está interesado, codifiqué una aproximación de la solución de Kent aquí:
Siéntase libre de usarlo en sus aplicaciones. Funciona bien. (¡Gracias Kent!)
fuente
Aquí hay otra solución a este "error" sobre el que escribí en un blog aquí:
Enlace OneWayToSource para propiedad de dependencia de solo lectura
Funciona utilizando dos propiedades de dependencia, Listener y Mirror. Listener está vinculado OneWay a TargetProperty y en PropertyChangedCallback actualiza la propiedad Mirror que está vinculada a OneWayToSource a lo que se haya especificado en el enlace. Lo llamo
PushBindingy se puede configurar en cualquier propiedad de dependencia de solo lectura como estaDescargue el proyecto de demostración aquí .
Contiene código fuente y uso de muestra breve, o visite mi blog WPF si está interesado en los detalles de implementación.
Una última nota, desde .NET 4.0 estamos aún más lejos del soporte incorporado para esto, ya que OneWayToSource Binding lee el valor de la Fuente después de haberlo actualizado.
fuente
¡Me gusta la solución de Dmitry Tashkinov! Sin embargo, se estrelló mi VS en modo de diseño. Es por eso que agregué una línea al método OnSourceChanged:
vacío estático privado OnSourceChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { if (! ((bool) DesignerProperties.IsInDesignModeProperty.GetMetadata (typeof (DependencyObject)). DefaultValue)) ((DataPipe) d). OnSourceChanged (e); }fuente
Creo que se puede hacer un poco más simple:
xaml:
cs:
fuente