7

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 = 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;
        }
    }));
}
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Mark
  • 14,820
  • 17
  • 99
  • 159
  • 1
    TextBlock isn't visible and isn't loaded so it hasn't a property ActualWidth. I've solved a similar problem with Itemscontrol, but i'm not sure about Canvas. Try events like LayoutUpdated, SizeChanged and so on. – vortexwolf Dec 21 '10 at 11:09

4 Answers4

5

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
   ...
}
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
smichaud
  • 326
  • 2
  • 12
3

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.

Rune Andersen
  • 1,650
  • 12
  • 15
2

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.

decyclone
  • 30,394
  • 6
  • 63
  • 80
  • there are many many textblocks though, I would have to subscribe to the Loaded event of the last one added which seems like a bit of a hack dont you think? – Mark Dec 21 '10 at 10:58
  • I guess you are right! Unless you add these `TextBlock` controls in a `StackPanel` with `Horizontal` orientation and scroll that panel. In that case, you would have to deal with that `Panel`'s events only. – decyclone Dec 21 '10 at 11:02
  • A Control is rendered first before its Loaded event is called. – guan boshen Aug 06 '19 at 00:53
0

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.

xenry
  • 456
  • 3
  • 8
  • im doing a scrolling marquee effect which means that each item really needs to be individual and positioned absolutely on the canvas – Mark Dec 21 '10 at 12:09