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 TextBlock
s to the Canvas. Then I want to create a marquee scrolling effect which should work just fine, except after I add the TextBlock
s 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;
}
}));
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With