绑定到 ActualWidth 不起作用

发布于 2024-08-08 10:03:57 字数 1199 浏览 3 评论 0原文

在 Silverlight 3.0 应用程序中,我尝试在画布中创建一个矩形,并让它拉伸画布的整个宽度。我尝试通过绑定到父容器的 ActualWidth 属性(参见下面的示例)来实现此目的,但是,虽然我没有看到任何绑定错误,但该值并未被绑定。该矩形不可见,因为其宽度为零。此外,尝试绑定到包含我的矩形的画布的 ActualWidth ,但这没有什么区别。

我确实发现 Microsoft Connect 上记录了此错误,但没有列出了解决方法。

有谁能够解决这个问题或者他们可以指出解决方案吗?

编辑:原始代码示例与我想要实现的目标并不准确,为了更加清晰而进行了更新。

<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>

In a Silverlight 3.0 application I'm attempting to create a rectangle in a canvas and have it stretch the whole width of the canvas. I have attempted to do this by binding to the ActualWidth property of a parent container (seem sample below), however while I don't see any binding errors the value is not being bound. The rectangle is not visible as its width is zero. In addition tried binding to the ActualWidth of the canvas that contains my rectangle but this made no difference.

I did find this bug logged on Microsoft Connect but there were no workarounds listed.

Has anyone been able to solve this issue or can they point to solution?

Edit: The original code sample was not accurate of what I'm trying to achieve, updated for more clarity.

<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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

翻了热茶 2024-08-15 10:03:57

您想要做什么需要您将数据绑定到 ActualWidth 属性?
这是 Silverlight 的一个已知问题,没有简单的解决方法。

可以做的一件事是以这样一种方式设置可视化树:您不需要实际设置矩形的宽度,而只需让它拉伸到适当的大小。因此,在上面的示例中,如果删除 Canvas(或将 Canvas 更改为其他面板)并将 RectangleHorizo​​ntalAlignment 设置为 Stretch,它将占用所有可用宽度(实际上是网格的宽度)。

但是,在您的特定情况下这可能是不可能的,并且可能确实需要设置数据绑定。已经确定这是不可能直接实现的,但是在代理对象的帮助下,我们可以设置所需的绑定。考虑以下代码:

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

我们可以在 xaml 中使用它,如下所示:

<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>

因此,我们将 TextBlock.Text 绑定到代理对象上的 ActualWidthValue。代理对象又提供元素的实际宽度,该宽度由另一个绑定提供。

这不是问题的简单解决方案,但这是我能想到的关于如何将数据绑定到 ActualWidth 的最佳解决方案。

如果您进一步解释一下您的场景,可能会想出一个更简单的解决方案。可能根本不需要数据绑定;是否可以仅从 SizeChanged 事件处理程序中的代码设置属性?

What are you trying to do that requires you to databind to the ActualWidth property?
This is a known issue with Silverlight, and there is no simple workaround.

One thing that could be done is to set up the visual tree in such a way that you do not need to actually set the Width of the Rectangle, and just allow it to stretch to the appropriate size. So in the example above, if you remove the Canvas (or change the Canvas to some other Panel) and leave the Rectangle's HorizontalAlignment set to Stretch, it will take up all of the available width (effectively the Width of the Grid).

However, this may not be possible in your particular case, and it may really be necessary to set up the databinding. It has already been established that this is not possible directly, but with the help of a proxy object, we can set up the required binding. Consider this code:

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

We can use this in xaml as follows:

<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>

So we are Binding TextBlock.Text to the ActualWidthValue on the proxy object. The proxy object in turn provides the ActualWidth of the Element, which is provided by another Binding.

This is not a simple solution to the problem, but it is the best that I can think of for how to databind to ActualWidth.

If you explained your scenario a bit more, it may be possible to come up with a simpler solution. DataBinding may not be required at all; would it be possible to just set the property from code in a SizeChanged event handler?

无声无音无过去 2024-08-15 10:03:57

使用附加属性机制,可以定义表示ActualHeightActualWidth并由SizeChanged事件更新的属性。它的用法如下所示。

<Grid local:SizeChange.IsEnabled="True" x:Name="grid1">...</Grid>

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

技术详细信息可在以下位置找到:

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

与其他解决方案相比,此解决方案的优点在于解决方案中定义的附加属性(SizeChange.ActualHeight 和 SizeChange) .ActualWidth) 可用于任何 FrameworkElement,无需创建任何子类。 此解决方案可重复使用且侵入性较小。


如果链接变得过时,这里是链接上显示的 SizeChange 类:

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

Using the mechanism of attached properties, properties which represent ActualHeight and ActualWidth and are updated by SizeChanged event can be defined. Its usage will look like the following.

<Grid local:SizeChange.IsEnabled="True" x:Name="grid1">...</Grid>

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

Technical details can be found at the following:

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

The advantage of this solution compared to others is in that the attached properties defined in the solution (SizeChange.ActualHeight and SizeChange.ActualWidth) can be used for any FrameworkElement without creating any sub class. This solution is reusable and less invasive.


In the event that the link becomes stale, here is the SizeChange Class as shown on the link:

// 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);
     }
 }
醉生梦死 2024-08-15 10:03:57

我的解决方案是声明我自己的名为 RealWidth 的 DependencyProperty 并在 SizeChanged 事件上更新其值。然后,您可以绑定到 RealWidth,它将更新,这与 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;
}

My solution is to declare my own DependencyProperty called RealWidth and update its value on the SizeChanged event. You can then bind to RealWidth, which will update, unlike the ActualWidth property.

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;
}
过去的过去 2024-08-15 10:03:57

为什么不创建一个继承自 ContentPresenter 并且实际上可以提供当前大小的简单面板控件。

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

然后它应该用作实际内容的包装。

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

对我来说很有魅力而且看起来很干净。

Why not create a simple panel control that inherits from ContentPresenter and actually can provide the current size.

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

It should then be used as wrapper for the actual content.

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

Worked for me like a charm and looks clean.

沉默的熊 2024-08-15 10:03:57

基于@darutk的答案,这里有一个附加的基于属性的解决方案,它可以非常优雅地完成这项工作。

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

像这样使用它:

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

Based on @darutk's answer, here's an attached property-based solution which does the job very elegantly.

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

Use it like this:

    <Grid>
        <Border x:Name="Border" behaviors:SizeBindings.IsEnabled="True"/>
        <Border MinWidth="{Binding (behaviors:SizeBindings.ActualWidth), ElementName=Border}"/>
    </Grid>
a√萤火虫的光℡ 2024-08-15 10:03:57

我已经测试了您使用 TestConverter 发布的更新的 xaml,以查看传递给宽度的值,它对我有用(我正在使用 VS 2010 B2)。要使用 TestConverter,只需在 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;
        }

    }

传入的值为 150,矩形的宽度为 150。

您是否期望有所不同?

I've tested the updated xaml that you publishing using a TestConverter to see what value gets passed to the width and it is working for me (I am using VS 2010 B2). To use the TestConverter just set a breakpoint in the Convert method.

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

    }

A value of 150 was passed in and the Rectangle had a width of 150.

Were you expecting something different?

我要还你自由 2024-08-15 10:03:57

这是一个作为旁白的答案,可能会帮助某人绑定到ActualWidth

我的流程不需要更改事件,它需要当前状态值的最终结果。因此,我在自定义控件/进程上创建了一个名为 Target 的依赖属性作为 FrameworkElement,并且使用者 xaml 将绑定到相关的实际对象。

当需要计算时,代码可以提取实际对象并从中提取它的 ActualWidth


控件的依赖属性

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 显示与矩形的绑定

<local:ThicknessWrapper Target="{Binding ElementName=thePanel}"/>

<Rectangle x:Name="thePanel" HorizontalAlignment="Stretch" Height="20"  Fill="Blue"/>

获取代码

double width;

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

This is an as an aside answer which may help someone for binding to the ActualWidth.

My process didn't need a change event, it needed an end result of a value in its current state. So I created a dependency property called Target on my custom control/process as a FrameworkElement and the consumer xaml would bind to the actual object in question.

When it was time for the calculation the code could pulled the actual object and extracted it's ActualWidth from it.


Dependency Property on 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 on Consumer side showing a binding to a Rectangle

<local:ThicknessWrapper Target="{Binding ElementName=thePanel}"/>

<Rectangle x:Name="thePanel" HorizontalAlignment="Stretch" Height="20"  Fill="Blue"/>

Code to Acquire

double width;

if (Target != null)
   width = Target.ActualWidth;  // Gets the current value.
猫烠⑼条掵仅有一顆心 2024-08-15 10:03:57

根据 KeithMahoney 的回答,它在我的 UWP 应用程序上运行良好并解决了我的问题。但是,我在设计时看不到我的控件,因为设计时未提供 ActualWidthValueActualHeightValue 的初始值。虽然它在运行时工作得很好,但对于设计我的控件的布局来说很不方便。只要稍微修改一下,这个问题就可以解决。

  1. 在属性 ActualWidthValueActualHeightValue 的 C# 代码中,添加

    <块引用>

    设置{;}

    让我们可以从 XAML 代码提供虚拟值。虽然它对运行时没有用处,但可以用于设计时。

  2. 在其 XAML 代码的Resources声明中,为ActualWidthValueActualHeightValue提供c:ActualSizePropertyProxy合适的值> 比如

    <块引用>

    ActualHeightValue="800" ActualWidthValue="400"

    然后它会在设计时向您显示一个 400x800 的控件。

Based on KeithMahoney's answer, it works fine on my UWP App and solves my problem. However, I cannot see my control in Design time because both the initial values of ActualWidthValue and ActualHeightValue are not provided in Design time. Although it works fine in running time, it is inconvenient for designing the layout of my control. With a little modification, this problem can be solved.

  1. In his c# code for both properties ActualWidthValue and ActualHeightValue, add

    set {;}

    to let us can provide dummy values from XAML code. Although it is no use for running time, it can be used for design time.

  2. In the declaration of Resources of his XAML code, provide c:ActualSizePropertyProxy suitable values for ActualWidthValue and ActualHeightValue such as

    ActualHeightValue="800" ActualWidthValue="400"

    Then it will show you a 400x800 control in design time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文