开发者

WPF ActualWidth is zero

I have a user control which has a Canvas of height 100 and width 1920.

At the loading of the control, I go to an external source, download a text file and add TextBlocks to the Canvas. Then I want to create a marquee scrolling effect which should work just fine, except after I add the TextBlocks to the Canvas, I need to get their width for calculation purposes but the ActualWidth property is always zero.

Here is some code:

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 =开发者_JS百科 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;
        }
    }));
}


You could always attach a delgate to the PropertyMetatdata/OnValueChanged and when ActualHeight/ActualWidth changes from 0 to something, adjust your scrolling, ActualWidth/ActualHeight will have a value once its rendered at least once:

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


If you want to stick with your dispatcher call - set the priority to loaded then it will be called same time as the loaded event and you should have a value. There is an overload on BeginInvoke that takes a priority also.


Any Control's ActualHeight or ActualWidth will always be zero before they are Loaded > Measured > Arranged > Rendered.

In your case, I recommend using Loaded or SizeChanged event of that TextBlock to your advantage.


Is there any particular reason for using Canvas for the layout of the TextBlocks? If not, you'd better use a StackPanel with Horizontal orientation, it will handle the layout math for you.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜