WPF的ActualWidth为零

7

我有一个用户控件,其中包含高度为100,宽度为1920的Canvas

在控件加载时,我会去外部源下载一个文本文件,并将TextBlock添加到Canvas中。然后,我想创建一个跑马灯滚动效果,这应该没有问题,但是在我将TextBlock添加到Canvas之后,我需要获取它们的宽度进行计算,但是ActualWidth属性总是为零。

以下是一些代码:

private readonly LinkedList<TextBlock> textBlocks = new LinkedList<TextBlock>();

public LocalNewsControl()
{
    Loaded += LocalNewsControlLoaded;
}

private void LocalNewsControlLoaded(object sender, RoutedEventArgs e)
{
    LoadDataContext();
}

private void LoadDataContext()
{
    DataContext = new NewsItemsViewModel((exception) => LoadNewsItems());
}

private void LoadNewsItems()
{
    var viewModel = (NewsItemsViewModel)DataContext;

    NewsCanvas.Children.Clear();
    textBlocks.Clear();

    foreach (var newsViewModel in viewModel.NewsItems)
    {
        var tb = new TextBlock
        {
            Text = newsViewModel.Headline,
            FontSize = 28,
            FontWeight = FontWeights.Normal,
            Foreground = Brushes.Black
        };

        NewsCanvas.Children.Add(tb);

        Canvas.SetTop(tb, 20);
        Canvas.SetLeft(tb, -999);

        textBlocks.AddLast(tb);
    }

    Dispatcher.BeginInvoke(new Action(() =>
    {
        var node = textBlocks.First;

        while (node != null)
        {
            if (node.Previous != null)
            {
                //THIS IS WHERE ActualWidth is always ZERO
                var left = Canvas.GetLeft(node.Previous.Value) + node.Previous.Value.ActualWidth + Gap;
                Canvas.SetLeft(node.Value, left);
            }
            else
                Canvas.SetLeft(node.Value, NewsCanvas.Width + Gap);

            node = node.Next;
        }
    }));
}

1
TextBlock不可见也未加载,因此它没有属性ActualWidth。我已经通过Itemscontrol解决了类似的问题,但对于Canvas我不确定。尝试使用LayoutUpdated、SizeChanged等事件。 - vortexwolf
4个回答

5

你可以始终将委托附加到PropertyMetatdata/OnValueChanged,当ActualHeight/ActualWidth从0变为其他值时,调整滚动条,只有在至少渲染一次后,ActualWidth/ActualHeight才会有值:

LocalNewsControl()
{
    var descriptor = DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(TextBlock));
    if (descriptor != null)
        descriptor.AddValueChanged(myTextBlock, ActualWidth_ValueChanged);
}

private void ActualWidth_ValueChanged(object a_sender, EventArgs a_e)
{
   //Modify you scroll things here
   ...
}

3
如果你想继续使用调度程序调用 - 将优先级设置为loaded,然后它将与loaded事件同时调用,并且你应该有一个值。 还有一种重载的BeginInvoke可以带有优先级参数。

2
任何控件的实际高度或宽度在加载、测量、排列和渲染之前都将始终为零。在您的情况下,我建议利用文本块的“Loaded”或“SizeChanged”事件。

有很多文本块,我必须订阅最后添加的文本块的Loaded事件,这似乎有点不太正规,你觉得呢? - Mark
我猜你是正确的!除非你在一个具有水平方向的StackPanel中添加这些TextBlock控件并滚动该面板。在这种情况下,您只需要处理该Panel的事件。 - decyclone
一个控件在其Loaded事件被触发之前先进行渲染。 - guan boshen

0

使用Canvas布局TextBlock有什么特殊原因吗?如果没有,最好使用水平方向的StackPanel,它会为您处理布局计算。


我正在做一个滚动字幕效果,这意味着每个项目都需要是独立的,并在画布上绝对定位。 - Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接