绑定到ActualWidth不起作用

在Silverlight 3.0应用程序中,我试图在canvas中创build一个矩形,并将其拉伸到canvas的整个宽度。 我试图通过绑定到一个父容器的ActualWidth属性(似乎下面的示例),但是,虽然我没有看到任何绑定错误的值没有被绑定。 由于宽度为零,矩形不可见。 另外尝试绑定到包含我的矩形的canvas的ActualWidth ,但这没有什么区别。

我确实发现了这个在Microsoft Connect上logging的错误,但是没有列出任何解决方法。

有没有人能够解决这个问题,或者他们可以指出解决scheme?

编辑:原始代码示例是不是我想要实现的准确,更新更清晰。

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

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

可以做的一件事就是设置可视化树,使得您不需要真正设置矩形的宽度,只需将其拉伸到适当的大小即可。 所以在上面的例子中,如果你删除了Canvas(或者将Canvas改为其他Panel),并且将RectangleHorizontalAlignment设置为Stretch ,它将占用所有可用的宽度(实际上是网格的宽度)。

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

 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。 代理对象依次提供由另一个Binding提供的元素的ActualWidth。

这不是一个简单的问题的解决scheme,但它是最好的,我可以想到如何绑定到ActualWidth。

如果你更多的解释了你的情况,有可能想出一个更简单的解决scheme。 DataBinding可能根本不需要; 是否有可能从一个SizeChanged事件处理程序的代码中设置属性?

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

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

技术细节可以在以下find:

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

与其他解决scheme相比,这个解决scheme的优势在于解决scheme中定义的附加属性(SizeChange.ActualHeight和SizeChange.ActualWidth)可以用于任何FrameworkElement而不创build任何子类。 该解决scheme可重复使用,侵入性较小。


如果链接变旧,这里是链接上显示的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); } } 

我知道的方式太晚了,但只是摔跤这个问题。 我的解决scheme是声明我自己的名为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; } 

为什么不创build从ContentPresenterinheritance的实际可以提供当前大小的简单面板控件?

 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> 

为我工作像一个魅力,看起来干净。

基于@ darutk的回答 ,这是一个附属的基于属性的解决scheme,非常优雅地完成工作。

 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> 

我已经testing了使用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。

你期待有什么不同吗?

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

我的过程不需要更改事件,它需要在当前状态下的值的最终结果 。 所以我创build了一个名为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. 

基于KeithMahoney的答案,它适用于我的UWP应用程序,并解决了我的问题。 但是,在Design time中看不到我的控件,因为ActualWidthValueActualHeightValue的初始值在devise时不提供。 虽然它在运行时间中工作正常,但是devise我的控件的布局是不方便的。 稍加修改,就可以解决这个问题。

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

    设置{;}

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

  2. 在他的XAML代码的资源声明中,为ActualWidthValueActualHeightValue提供c:ActualSizePropertyProxy适当的值,例如

    ActualHeightValue =“800”ActualWidthValue =“400”

    然后它会在devise时显示一个400×800的控件。

Interesting Posts