2009-10-21 77 views
23

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

我做了find this bug logged on 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> 

回答

31

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

可以做的一件事就是设置可视化树,使得您不需要真正设置矩形的宽度,只需让它伸展到适当的大小即可。因此,在上面的示例中,如果您移除画布(或将画布更改为某个其他面板)并将RectangleHorizontalAlignment设置为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。

如果您更详细地解释了您的方案,可以想出一个更简单的解决方案。 DataBinding可能根本不需要;是否可以在SizeChanged事件处理程序中设置代码的属性?

+0

我采用了你使用SizeChanged事件处理程序所建议的方法,并得到了所需的效果。为了更多地解释场景,我需要绑定到ActualWidth属性,因为我有一些奇怪的UI设计,需要一些元素出现在控件边界之外。 – 2009-10-22 12:40:44

+2

超过5年,仍然在WinRT下工作;-) - 有一个小的变化:新的SizeEventHandler(Element_SizeChanged)必须直接由Element_SizeChanged替换。 – TheEye 2015-03-12 16:18:21

1

我测试过,你发布使用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

的宽度是你期待着什么不同?

+0

对于它从0开始是正确的,但是由于ActualWidth是一个依赖项属性,所以在它发生更改时应该有通知。 我无法绑定到宽度,因为它从来没有设置为控件,因此返回double.NaN – 2009-10-21 18:28:58

+0

你是正确的,它是一个依赖属性,所以它应该改变。但是,您需要在上面的Xaml中设置宽度。 – Bryant 2009-10-21 18:51:28

+0

我的歉意,这是sl on在我的最后,因为我刚刚从我正在努力解决我遇到的问题的沙箱中复制片段。 我修复了错误并更新了示例以更好地描述我的情况。 – 2009-10-21 19:27:35

8

我太迟了我知道,但只是摔跤这个问题。我的解决方案是声明自己的DependencyProperty,称为RealWidth并更新其在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; 
} 
20

使用的附加属性,其代表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); 
    } 
} 
+1

这适用于运行时,但在Visual Studio设计师 – 2015-11-01 09:57:16

5

为什么不创建一个从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> 

工作对我来说就像一个魅力,看起来干净。

+0

中给我一个“值不在预期范围内”的错误,使用StackPanel作为基础解决了我的问题。当您使用ContentPresenter时,您无法通过名称访问元素并设置其值。 – 2012-02-12 16:08:31

+0

非常好和干净的解决方案,在我的情况下使用网格作为父项 – deafsheep 2013-04-22 20:00:21

2

基于@ darutk的answer,这是一个附属的基于属性的解决方案,它非常优雅地完成工作。

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

这是一个顺便说一句答案可以帮助别人结合的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. 
0

基于KeithMahoney的答案,它工作正常,在我UWP应用,解决了我的问题。但是,由于在设计时间内未提供初始值ActualWidthValueActualHeightValue,所以我无法在设计时看到我的控件。虽然它在运行时间内工作正常,但设计我的控件布局不方便。稍作修改,即可解决此问题。

  1. 在他的C#代码,这两个属性ActualWidthValueActualHeightValue,加

    集合{;}

    让我们可以从XAML代码提供虚拟值。尽管对于运行时间没有用处,但它可用于设计时间。为ActualWidthValueActualSizePropertyProxy合适的值ActualHeightValue如

    ActualHeightValue = “800” ActualWidthValue =“400:

  2. 资源他XAML代码声明,提供Ç “

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