Sí, he hecho esto en el pasado con las propiedades ActualWidth
y ActualHeight
, las cuales son de solo lectura. Creé un comportamiento adjunto que tiene ObservedWidth
y ObservedHeight
propiedades adjuntas. También tiene una Observe
propiedad 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 Width
y Height
propiedades que están siempre en sincronía con las ObservedWidth
y ObservedHeight
unidos propiedades. La Observe
propiedad simplemente se adjunta al SizeChanged
evento de la FrameworkElement
. En el identificador, actualiza sus ObservedWidth
y sus ObservedHeight
propiedades. Ergo, el Width
y Height
del modelo de vista siempre está sincronizado con el ActualWidth
y ActualHeight
del UserControl
.
Quizás no sea la solución perfecta (estoy de acuerdo, los DP de solo lectura deberían admitir OneWayToSource
enlaces), pero funciona y mantiene el patrón MVVM. Obviamente, los ObservedWidth
y los ObservedHeight
DP 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);
}
}
Size
propiedad, combinando Altura y Ancho. Aprox. 50% menos de código.ActualSize
propiedadFrameworkElement
. Si desea la vinculación directa de las propiedades adjuntas, debe crear dos propiedades a las que vincularActualWidth
yActualHeight
respectivamente.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
Target
propiedad 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
PushBinding
y 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:
fuente
Creo que se puede hacer un poco más simple:
xaml:
cs:
fuente