En una aplicación de Silverlight 3.0 yo estoy tratando de crear un rectángulo en un lienzo y tienen que estirar todo el ancho de la tela. He intentado hacerlo mediante la unión a la ActualWidth propiedad de un contenedor primario (parecen muestra a continuación), sin embargo, aunque no veo ningún enlace de errores el valor es no estar obligado. El rectángulo no es visible como su anchura es igual a cero. Además del tratado de la unión a la ActualWidth del lienzo que contiene mi rectángulo, pero este no hizo ninguna diferencia.

Hice encontrar este error se registra en Microsoft Connect pero no hubo soluciones enumeradas.

Nadie ha sido capaz de resolver este problema o puede que apuntan a la solución?

Edit: El código original de la muestra no fue precisa de lo que estoy tratando de lograr, actualizado para más claridad.

<UserControl>
    <Border BorderBrush="White"
            BorderThickness="1"
            CornerRadius="4"
            HorizontalAlignment="Center">
        <Grid x:Name="GridContainer">
            <Rectangle Fill="Aqua"
                       Width="150"
                       Height="400" />
            <Canvas>
                <Rectangle Width="{Binding Path=ActualWidth, ElementName=GridContainer}"
                           Height="30"
                           Fill="Red" />
            </Canvas>

            <StackPanel>
                <!-- other elements here -->
            </StackPanel>
        </Grid>
    </Border>
</UserControl>

8 Comentarios

  1. 31

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

    Una cosa que se podría hacer es configurar el árbol visual de tal manera que usted no necesita realmente para establecer el Ancho del Rectángulo, y sólo permitir que se extienden hasta el tamaño adecuado. Así, en el ejemplo anterior, si se quita el Lienzo (o cambiar la Lona a algún otro Panel) y dejar el Rectangle‘s HorizontalAlignment conjunto para Stretch, que ocupará todo el ancho disponible (efectivamente el Ancho de la Rejilla).

    Sin embargo, esto puede no ser posible en su caso particular, y en realidad, puede ser necesario establecer 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 necesario. Considere 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"));
    }
    }
    }

    Que podemos usar en xaml de la siguiente manera:

    <Grid x:Name="LayoutRoot">
    <Grid.Resources>
    <c:ActualSizePropertyProxy Element="{Binding ElementName=LayoutRoot}" x:Name="proxy" />
    </Grid.Resources>
    <TextBlock x:Name="tb1" Text="{Binding ActualWidthValue, ElementName=proxy}"  />
    </Grid>

    Así que son Vinculantes TextBlock.Texto para el ActualWidthValue en el objeto proxy. El objeto de proxy en vez proporciona la ActualWidth del Elemento, la cual es proporcionada por otro Enlace.

    Esto no es una solución simple para el problema, pero es la mejor que puedo pensar, cómo enlazar a ActualWidth.

    Si usted ha explicado su escenario un poco más, puede ser posible llegar a una solución más simple. El enlace de datos puede no ser necesaria en todos; sería posible establecer la propiedad desde el código de un controlador de evento SizeChanged?

    • Tomé el enfoque que se sugiere con el controlador de evento SizeChanged y estoy consiguiendo el efecto deseado. Para explicar el escenario un poco más que yo necesitaba para unirse a la ActualWidth propiedad como tengo un poco de un extraño diseño de interfaz de usuario que requiere de algunos elementos que aparecen fuera de los límites de control.
    • Más de 5 años de edad y todavía funciona bajo WinRT 😉 – con un pequeño cambio: nueva SizeEventHandler(Element_SizeChanged) tiene que ser sustituido por Element_SizeChanged directamente.
  2. 21

    Utilizando el mecanismo de las propiedades adjuntas, propiedades que representan ActualHeight y ActualWidth y son actualizados por SizeChanged evento puede ser definido. Su uso se verá como la siguiente.

    <Grid local:SizeChange.IsEnabled="True" x:Name="grid1">...</Grid>
    <TextBlock Text="{Binding ElementName=grid1,
    Path=(local:SizeChange.ActualHeight)}"/>

    Detalles técnicos se pueden encontrar en el siguiente:

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

    La ventaja de esta solución en comparación con los demás está en que las propiedades asociadas se definen en la solución (SizeChange.ActualHeight y SizeChange.ActualWidth) puede ser utilizado para cualquier FrameworkElement sin crear ninguna clase sub. Esta solución es reutilizable y menos invasiva.


    En el caso de que el enlace se convierte en obsoletos, aquí está la SizeChange Clase, 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);
    }
    }
    • Esto funciona en tiempo de ejecución, pero me da un «valor no se encuentra dentro del rango esperado» error en el diseñador de visual studio
  3. 8

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

    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;
    }
  4. 5

    ¿Por qué no crear un sencillo panel de control que hereda de ContentPresenter y en realidad 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;
    }
    }

    Debe ser utilizado como contenedor para el contenido real.

    <local:SizeNotifyPanel x:Name="Content">
    <TextBlock Text="{Binding Size.Height, ElementName=Content}" />
    </local:SizeNotifyPanel>

    Trabajado para mí como un encanto y se ve limpio.

    • muy bien, usando el StackPanel como base resuelto mi problema. Cuando se utiliza ContentPresenter tiene problemas para llegar al interior de los elementos por el nombre y la configuración de sus valores.
    • Muy bueno y limpio solución, se utiliza la Cuadrícula como padre en mi caso
  5. 2

    Basa en @darutk del respuesta, aquí es una propiedad asociada a la solución basada en la que hace el trabajo muy elegante.

    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);
    }
    }

    Usarlo como este:

        <Grid>
    <Border x:Name="Border" behaviors:SizeBindings.IsEnabled="True"/>
    <Border MinWidth="{Binding (behaviors:SizeBindings.ActualWidth), ElementName=Border}"/>
    </Grid>
  6. 1

    He probado el código xaml actualizado que su publicación mediante un TestConverter ver qué valor se pasa a la anchura y a la que se está trabajando para mí (estoy usando VS 2010 B2). El uso de la TestConverter acaba de establecer 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;
    }
    }

    Un valor de 150 fue aprobada en y el Rectángulo tenían una anchura de 150.

    Esperabas algo diferente?

    • Estás en lo correcto acerca de que a partir de 0, sin embargo, desde ActualWidth es una propiedad de dependencia debe haber una notificación cuando cambia. No puedo obligar a la Anchura como nunca es establecida para el control y como resultado devuelve el doble.NaN
    • Usted está en lo correcto, es una propiedad de dependencia por lo que debe cambiar. Sin embargo, se establece el Ancho en el Xaml de arriba.
    • Mis disculpas, que fue descuidado en mi final como me acaba de copiar el fragmento de código de la caja de arena donde yo estaba trabajando para resolver el problema que estoy teniendo. He corregido el error y actualizada de la muestra para describir mejor mi situación.
    • He hecho como usted sugiere y mi punto de interrupción es sólo golpear una vez, el valor dado en el convertidor es de 0.
  7. 0

    Este es un como un aparte respuesta que puede ayudar a alguien para unirse a la ActualWidth.

    Mi proceso no necesita un evento de cambio, es necesario un resultado final de un valor en su estado actual. Así que he creado una propiedad de dependencia llamado Target en mi control personalizado/proceso como un FrameworkElement y el consumidor xaml se une a la real del objeto en cuestión.

    Cuando era el momento para el cálculo del código sacó el objeto real y la extrajo del ActualWidth de ella.


    Propiedad de dependencia en el Control de

    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 de los Consumidores, mostrando un enlace a un Rectángulo

    <local:ThicknessWrapper Target="{Binding ElementName=thePanel}"/>
    <Rectangle x:Name="thePanel" HorizontalAlignment="Stretch" Height="20"  Fill="Blue"/>

    Código para Adquirir

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

    Basado en KeithMahoney‘s respuesta, funciona bien en mi UWP Aplicación y solucionó mi problema. Sin embargo, no puedo ver a mi control en tiempo de Diseño, ya que tanto los valores iniciales de ActualWidthValue y ActualHeightValue no se proporciona en tiempo de Diseño. Aunque funciona bien en tiempo de ejecución, es inconveniente para el diseño de la presentación de mi control. Con una pequeña modificación, este problema puede ser resuelto.

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

      conjunto {;}

      a dejar que nos puede proporcionar valores ficticios de código XAML. Aunque no es un uso para el tiempo de ejecución, puede ser utilizado para el tiempo de diseño.

    2. En la declaración de Recursos de su código XAML, proporcionar c:ActualSizePropertyProxy valores adecuados para ActualWidthValue y ActualHeightValue como

      ActualHeightValue=»800″ ActualWidthValue=»400″

      A continuación se le mostrará una 400×800 control en tiempo de diseño.

Dejar respuesta

Please enter your comment!
Please enter your name here