La vinculación a ActualWidth no funciona

En una aplicación de Silverlight 3.0, bash crear un rectángulo en un canvas y hacer que se extienda todo el ancho del canvas. ActualWidth hacer esto atando a la propiedad ActualWidth de un contenedor padre (parece ejemplo a continuación), sin embargo, mientras no veo ningún error vinculante, el valor no está vinculado. El rectángulo no es visible ya que su ancho es cero. Además intenté enlazar con el ActualWidth del canvas que contiene mi rectángulo, pero esto no hizo diferencia.

Encontré este error registrado en Microsoft Connect pero no había soluciones alternativas.

¿Alguien ha podido resolver este problema o pueden señalar una solución?

Editar: El ejemplo del código original no era exacto de lo que bash lograr, actualizado para mayor claridad.

              

¿Qué estás tratando de hacer que requiere una ActualWidth datos a la propiedad ActualWidth ? Este es un problema conocido con Silverlight, y no hay una solución simple.

Una cosa que se puede hacer es configurar el árbol visual de tal forma que no necesite establecer el ancho del rectángulo, y simplemente permitir que se extienda hasta el tamaño apropiado. Por lo tanto, en el ejemplo anterior, si quita el canvas (o cambia el canvas a otro panel) y deja la Rectangle HorizontalAlignment del Rectangle establecida en Stretch , ocupará todo el ancho disponible (efectivamente el ancho de la rejilla).

Sin embargo, esto puede no ser posible en su caso particular, y realmente puede ser necesario configurar el enlace de datos. Ya se ha establecido que esto no es posible directamente, pero con la ayuda de un objeto proxy, podemos configurar el enlace requerido. Considera este código:

 public class ActualSizePropertyProxy : FrameworkElement, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public FrameworkElement Element { get { return (FrameworkElement)GetValue(ElementProperty); } set { SetValue(ElementProperty, value); } } public double ActualHeightValue { get{ return Element == null? 0: Element.ActualHeight; } } public double ActualWidthValue { get { return Element == null ? 0 : Element.ActualWidth; } } public static readonly DependencyProperty ElementProperty = DependencyProperty.Register("Element", typeof(FrameworkElement), typeof(ActualSizePropertyProxy), new PropertyMetadata(null,OnElementPropertyChanged)); private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ActualSizePropertyProxy)d).OnElementChanged(e); } private void OnElementChanged(DependencyPropertyChangedEventArgs e) { FrameworkElement oldElement = (FrameworkElement)e.OldValue; FrameworkElement newElement = (FrameworkElement)e.NewValue; newElement.SizeChanged += new SizeChangedEventHandler(Element_SizeChanged); if (oldElement != null) { oldElement.SizeChanged -= new SizeChangedEventHandler(Element_SizeChanged); } NotifyPropChange(); } private void Element_SizeChanged(object sender, SizeChangedEventArgs e) { NotifyPropChange(); } private void NotifyPropChange() { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("ActualWidthValue")); PropertyChanged(this, new PropertyChangedEventArgs("ActualHeightValue")); } } } 

Podemos usar esto en xaml de la siguiente manera:

       

Así que estamos Vinculando TextBlock.Text al ActualWidthValue en el objeto proxy. El objeto proxy a su vez proporciona el ancho real del elemento, que es proporcionado por otro enlace.

Esta no es una solución simple al problema, pero es lo mejor que se me ocurre para cómo enlazar datos con ActualWidth.

Si explicó su escenario un poco más, puede ser posible encontrar una solución más simple. DataBinding puede no ser necesario en absoluto; ¿sería posible simplemente configurar la propiedad del código en un controlador de eventos SizeChanged?

Usando el mecanismo de las propiedades adjuntas , se pueden definir las propiedades que representan ActualHeight y ActualWidth y se actualizan mediante el evento SizeChanged . Su uso se verá como el siguiente.

 ...  

Los detalles técnicos se pueden encontrar en lo siguiente:

http://darutk-oboegaki.blogspot.com/2011/07/binding-actualheight-and-actualwidth.html

La ventaja de esta solución en comparación con otras es que las propiedades adjuntas definidas en la solución (SizeChange.ActualHeight y SizeChange.ActualWidth) se pueden usar para cualquier FrameworkElement sin crear ninguna subclase. Esta solución es reutilizable y menos invasiva.


En caso de que el enlace se vuelva obsoleto, aquí está la Clase de cambio de tamaño como se muestra en el enlace:

 // Declare SizeChange class as a sub class of DependencyObject // because we need to register attached properties. public class SizeChange : DependencyObject { #region Attached property "IsEnabled" // The name of IsEnabled property. public const string IsEnabledPropertyName = "IsEnabled"; // Register an attached property named "IsEnabled". // Note that OnIsEnabledChanged method is called when // the value of IsEnabled property is changed. public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached( IsEnabledPropertyName, typeof(bool), typeof(SizeChange), new PropertyMetadata(false, OnIsEnabledChanged)); // Getter of IsEnabled property. The name of this method // should not be changed because the dependency system // uses it. public static bool GetIsEnabled(DependencyObject obj) { return (bool)obj.GetValue(IsEnabledProperty); } // Setter of IsEnabled property. The name of this method // should not be changed because the dependency system // uses it. public static void SetIsEnabled(DependencyObject obj, bool value) { obj.SetValue(IsEnabledProperty, value); } #endregion #region Attached property "ActualHeight" // The name of ActualHeight property. public const string ActualHeightPropertyName = "ActualHeight"; // Register an attached property named "ActualHeight". // The value of this property is updated When SizeChanged // event is raised. public static readonly DependencyProperty ActualHeightProperty = DependencyProperty.RegisterAttached( ActualHeightPropertyName, typeof(double), typeof(SizeChange), null); // Getter of ActualHeight property. The name of this method // should not be changed because the dependency system // uses it. public static double GetActualHeight(DependencyObject obj) { return (double)obj.GetValue(ActualHeightProperty); } // Setter of ActualHeight property. The name of this method // should not be changed because the dependency system // uses it. public static void SetActualHeight(DependencyObject obj, double value) { obj.SetValue(ActualHeightProperty, value); } #endregion #region Attached property "ActualWidth" // The name of ActualWidth property. public const string ActualWidthPropertyName = "ActualWidth"; // Register an attached property named "ActualWidth". // The value of this property is updated When SizeChanged // event is raised. public static readonly DependencyProperty ActualWidthProperty = DependencyProperty.RegisterAttached( ActualWidthPropertyName, typeof(double), typeof(SizeChange), null); // Getter of ActualWidth property. The name of this method // should not be changed because the dependency system // uses it. public static double GetActualWidth(DependencyObject obj) { return (double)obj.GetValue(ActualWidthProperty); } // Setter of ActualWidth property. The name of this method // should not be changed because the dependency system // uses it. public static void SetActualWidth(DependencyObject obj, double value) { obj.SetValue(ActualWidthProperty, value); } #endregion // This method is called when the value of IsEnabled property // is changed. If the new value is true, an event handler is // added to SizeChanged event of the target element. private static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { // The given object must be a FrameworkElement instance, // because we add an event handler to SizeChanged event // of it. var element = obj as FrameworkElement; if (element == null) { // The given object is not an instance of FrameworkElement, // meaning SizeChanged event is not available. So, nothing // can be done for the object. return; } // If IsEnabled=True if (args.NewValue != null && (bool)args.NewValue == true) { // Attach to the element. Attach(element); } else { // Detach from the element. Detach(element); } } private static void Attach(FrameworkElement element) { // Add an event handler to SizeChanged event of the element // to take action when actual size of the element changes. element.SizeChanged += HandleSizeChanged; } private static void Detach(FrameworkElement element) { // Remove the event handler from the element. element.SizeChanged -= HandleSizeChanged; } // An event handler invoked when SizeChanged event is raised. private static void HandleSizeChanged(object sender, SizeChangedEventArgs args) { var element = sender as FrameworkElement; if (element == null) { return; } // Get the new actual height and width. var width = args.NewSize.Width; var height = args.NewSize.Height; // Update values of SizeChange.ActualHeight and // SizeChange.ActualWidth. SetActualWidth(element, width); SetActualHeight(element, height); } } 

Demasiado tarde, lo sé, pero he estado luchando con este problema. Mi solución es declarar mi propia DependencyProperty llamada RealWidth y actualizar su valor en el evento SizeChanged . A continuación, puede enlazar a RealWidth, que se actualizará, a diferencia de la propiedad ActualWidth .

 public MyControl() { InitializeComponent(); SizeChanged += HandleSizeChanged; } public static DependencyProperty RealWidthProperty = DependencyProperty.Register("RealWidth", typeof (double), typeof (MyControl), new PropertyMetadata(500D)); public double RealWidth { get { return (double) GetValue(RealWidthProperty); } set { SetValue(RealWidthProperty, value); } } private void HandleSizeChanged(object sender, SizeChangedEventArgs e) { RealWidth = e.NewSize.Width; } 

¿Por qué no crear un control de panel simple que hereda de ContentPresenter y realmente puede proporcionar el tamaño actual?

 public class SizeNotifyPanel : ContentPresenter { public static DependencyProperty SizeProperty = DependencyProperty.Register("Size", typeof (Size), typeof (SizeNotifyPanel), null); public Size Size { get { return (Size) GetValue(SizeProperty); } set { SetValue(SizeProperty, value); } } public SizeNotifyPanel() { SizeChanged += (s, e) => Size = e.NewSize; } } 

Debería utilizarse como contenedor para el contenido real.

    

Trabajó para mí como un encanto y se ve limpio.

Según la respuesta de @darutk, aquí hay una solución adjunta basada en propiedades que hace el trabajo muy elegantemente.

 public static class SizeBindings { public static readonly DependencyProperty ActualHeightProperty = DependencyProperty.RegisterAttached("ActualHeight", typeof (double), typeof (SizeBindings), new PropertyMetadata(0.0)); public static readonly DependencyProperty ActualWidthProperty = DependencyProperty.RegisterAttached("ActualWidth", typeof (Double), typeof (SizeBindings), new PropertyMetadata(0.0)); public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof (bool), typeof (SizeBindings), new PropertyMetadata(false, HandlePropertyChanged)); private static void HandlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var element = d as FrameworkElement; if (element == null) { return; } if ((bool) e.NewValue == false) { element.SizeChanged -= HandleSizeChanged; } else { element.SizeChanged += HandleSizeChanged; } } private static void HandleSizeChanged(object sender, SizeChangedEventArgs e) { var element = sender as FrameworkElement; SetActualHeight(element, e.NewSize.Height); SetActualWidth(element, e.NewSize.Width); } public static bool GetIsEnabled(DependencyObject obj) { return (bool)obj.GetValue(IsEnabledProperty); } public static void SetIsEnabled(DependencyObject obj, bool value) { obj.SetValue(IsEnabledProperty, value); } public static Double GetActualWidth(DependencyObject obj) { return (Double) obj.GetValue(ActualWidthProperty); } public static void SetActualWidth(DependencyObject obj, Double value) { obj.SetValue(ActualWidthProperty, value); } public static double GetActualHeight(DependencyObject obj) { return (double)obj.GetValue(ActualHeightProperty); } public static void SetActualHeight(DependencyObject obj, double value) { obj.SetValue(ActualHeightProperty, value); } } 

Úselo así:

      

He probado el xaml actualizado que publica utilizando TestConverter para ver qué valor pasa al ancho y funciona para mí (estoy usando VS 2010 B2). Para usar TestConverter, simplemente configure un punto de interrupción en el método Convert.

  public class TestConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } 

Se pasó un valor de 150 y el rectángulo tuvo un ancho de 150.

¿Estabas esperando algo diferente?

Esta es una respuesta aparte que puede ayudar a alguien a unirse al ActualWidth .

Mi proceso no necesitaba un evento de cambio, necesitaba un resultado final de un valor en su estado actual. Así que creé una propiedad de dependencia llamada Target en mi control / proceso personalizado como FrameworkElement y la xaml del consumidor se vincularía al objeto real en cuestión.

Cuando llegó el momento del cálculo, el código pudo extraer el objeto real y extraer su ActualWidth .


Propiedad de dependencia en el control

 public FrameworkElement Target { get { return (FrameworkElement)GetValue(TargetProperty);} set { SetValue(TargetProperty, value);} } // Using a DependencyProperty as the backing store for Target. // This enables animation, styling, binding, general access etc... public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(FrameworkElement), typeof(ThicknessWrapper), new PropertyMetadata(null, OnTargetChanged)); 

XAML en el lado del consumidor que muestra un enlace a un rectángulo

   

Código para adquirir

 double width; if (Target != null) width = Target.ActualWidth; // Gets the current value. 

Basado en la respuesta de KeithMahoney , funciona bien en mi aplicación UWP y resuelve mi problema. Sin embargo, no puedo ver mi control en tiempo de diseño porque los valores iniciales de ActualWidthValue y ActualHeightValue no se proporcionan en tiempo de diseño. Aunque funciona bien en tiempo de ejecución, no es conveniente diseñar el diseño de mi control. Con una pequeña modificación, este problema puede ser resuelto.

  1. En su código c # para ambas propiedades, ActualWidthValue y ActualHeightValue , agregue

    establecer {;}

    para permitirnos proporcionar valores ficticios del código XAML. Aunque no sirve para el tiempo de ejecución, puede usarse para el tiempo de diseño.

  2. En la statement de Recursos de su código XAML, proporcione los valores adecuados de c: ActualSizePropertyProxy para ActualWidthValue y ActualHeightValue , como

    ActualHeightValue = “800” ActualWidthValue = “400”

    Luego le mostrará un control de 400×800 en tiempo de diseño.