动态更改内容时,客户控件的内容实际大小总是 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();
}