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 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 =开发者_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 TextBlock
s? If not, you'd better use a StackPanel
with Horizontal orientation, it will handle the layout math for you.
精彩评论