动态更改内容时,客户控件的内容实际大小总是 return 0
Customer control's content actual size always return 0 when dynamically change content
在我的控件中,canvas 面板内有内容展示器。我根据内容的实际大小安排了((以屏幕点为中心,左右对齐)canvas 中的内容。初始内容大小正确加载,当动态更改内容时,它的实际大小总是 return 0. 因此,无法在屏幕位置对齐内容。能否请您告诉我如何在动态情况下获取内容大小,如下例
<Page.Resources>
<!--control style-->
<Style TargetType="local:CustomLabel">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomLabel">
<Grid>
<Canvas>
<Ellipse Height="10" Width="10" Canvas.Left="300" Canvas.Top="300" Fill="Red" />
</Canvas>
<Canvas>
<ContentPresenter Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelMargin}" Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<local:CustomLabel x:Name="label">
<local:CustomLabel.Content>
<Grid>
<TextBlock Text="Initial Label" />
</Grid>
</local:CustomLabel.Content>
</local:CustomLabel>
<Button Content="Change text" VerticalAlignment="Bottom" Height="75" Click="Button_Click" />
</Grid>
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var grid = new Grid();
grid.Children.Add(new TextBlock() { Text = "Change" });
this.label.Content = grid;
}
// control implementation
public class CustomLabel : Control
{
bool loadTime = false;
public CustomLabel()
{
this.SizeChanged += CustomLabel_SizeChanged;
}
private void CustomLabel_SizeChanged(object sender, SizeChangedEventArgs e)
{
loadTime = true;
CalculateContentPosition();
}
private void CalculateContentPosition()
{
if (loadTime)
{
var content = Content as FrameworkElement;
if (content != null)
{
var left = content.ActualWidth / 2;
var top = content.ActualHeight / 2;
this.LabelMargin = new Thickness(300 - left, 300 - top, 0, 0);
}
}
}
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(CustomLabel), new PropertyMetadata(null,OnContentChanged));
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as CustomLabel).CalculateContentPosition();
}
public Thickness LabelMargin
{
get { return (Thickness)GetValue(LabelMarginProperty); }
set { SetValue(LabelMarginProperty, value); }
}
// Using a DependencyProperty as the backing store for LabelMargin. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LabelMarginProperty =
DependencyProperty.Register("LabelMargin", typeof(Thickness), typeof(CustomLabel), new PropertyMetadata(new Thickness(1)));
}
这个问题是因为你太早访问它的大小,它甚至还没有在 XAML 页面上呈现。您可能会问为什么 Grid 第一次在 XAML 页上有大小。这是因为父面板在布局子元素时做了这个操作(类似Measure(size)
)
因此,要解决您的问题,您可以注册 'CustomLabel' 的 LayoutUpdated
事件。然后,在它的事件处理程序中,您可以调用 CalculateContentPosition()
方法,而不是调用 OnContentChanged
方法。
public CustomLabel()
{
this.SizeChanged += CustomLabel_SizeChanged;
this.LayoutUpdated += CustomLabel_LayoutUpdated;
}
private void CustomLabel_LayoutUpdated(object sender, object e)
{
CalculateContentPosition();
}
在我的控件中,canvas 面板内有内容展示器。我根据内容的实际大小安排了((以屏幕点为中心,左右对齐)canvas 中的内容。初始内容大小正确加载,当动态更改内容时,它的实际大小总是 return 0. 因此,无法在屏幕位置对齐内容。能否请您告诉我如何在动态情况下获取内容大小,如下例
<Page.Resources>
<!--control style-->
<Style TargetType="local:CustomLabel">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomLabel">
<Grid>
<Canvas>
<Ellipse Height="10" Width="10" Canvas.Left="300" Canvas.Top="300" Fill="Red" />
</Canvas>
<Canvas>
<ContentPresenter Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelMargin}" Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<local:CustomLabel x:Name="label">
<local:CustomLabel.Content>
<Grid>
<TextBlock Text="Initial Label" />
</Grid>
</local:CustomLabel.Content>
</local:CustomLabel>
<Button Content="Change text" VerticalAlignment="Bottom" Height="75" Click="Button_Click" />
</Grid>
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var grid = new Grid();
grid.Children.Add(new TextBlock() { Text = "Change" });
this.label.Content = grid;
}
// control implementation
public class CustomLabel : Control
{
bool loadTime = false;
public CustomLabel()
{
this.SizeChanged += CustomLabel_SizeChanged;
}
private void CustomLabel_SizeChanged(object sender, SizeChangedEventArgs e)
{
loadTime = true;
CalculateContentPosition();
}
private void CalculateContentPosition()
{
if (loadTime)
{
var content = Content as FrameworkElement;
if (content != null)
{
var left = content.ActualWidth / 2;
var top = content.ActualHeight / 2;
this.LabelMargin = new Thickness(300 - left, 300 - top, 0, 0);
}
}
}
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(CustomLabel), new PropertyMetadata(null,OnContentChanged));
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as CustomLabel).CalculateContentPosition();
}
public Thickness LabelMargin
{
get { return (Thickness)GetValue(LabelMarginProperty); }
set { SetValue(LabelMarginProperty, value); }
}
// Using a DependencyProperty as the backing store for LabelMargin. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LabelMarginProperty =
DependencyProperty.Register("LabelMargin", typeof(Thickness), typeof(CustomLabel), new PropertyMetadata(new Thickness(1)));
}
这个问题是因为你太早访问它的大小,它甚至还没有在 XAML 页面上呈现。您可能会问为什么 Grid 第一次在 XAML 页上有大小。这是因为父面板在布局子元素时做了这个操作(类似Measure(size)
)
因此,要解决您的问题,您可以注册 'CustomLabel' 的 LayoutUpdated
事件。然后,在它的事件处理程序中,您可以调用 CalculateContentPosition()
方法,而不是调用 OnContentChanged
方法。
public CustomLabel()
{
this.SizeChanged += CustomLabel_SizeChanged;
this.LayoutUpdated += CustomLabel_LayoutUpdated;
}
private void CustomLabel_LayoutUpdated(object sender, object e)
{
CalculateContentPosition();
}