C# WPF实际宽度为零
我有一个用户控件,它的画布高度为100,宽度为1920 加载控件时,我转到一个外部源,下载一个文本文件并将C# WPF实际宽度为零,c#,.net,wpf,layout,actualwidth,C#,.net,Wpf,Layout,Actualwidth,我有一个用户控件,它的画布高度为100,宽度为1920 加载控件时,我转到一个外部源,下载一个文本文件并将TextBlocks添加到画布。然后我想创建一个字幕滚动效果,它应该可以正常工作,除了在我将文本块添加到画布之后,我需要获取它们的宽度以便于计算,但是实际宽度属性始终为零 下面是一些代码: private readonly LinkedList<TextBlock> textBlocks = new LinkedList<TextBlock>(); public L
TextBlock
s添加到画布。然后我想创建一个字幕滚动效果,它应该可以正常工作,除了在我将文本块
添加到画布
之后,我需要获取它们的宽度以便于计算,但是实际宽度
属性始终为零
下面是一些代码:
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;
}
}));
}
private readonly LinkedList textBlocks=new LinkedList();
公共LocalNewsControl()
{
Loaded+=LocalNewsControlLoaded;
}
private void LocalNewsControlLoaded(对象发送方,RoutedEventArgs e)
{
LoadDataContext();
}
私有void LoadDataContext()
{
DataContext=newNewsItemsViewModel((异常)=>LoadNewsItems());
}
私有void LoadNewsItems()
{
var viewModel=(NewsItemsViewModel)DataContext;
newscavas.Children.Clear();
textBlocks.Clear();
foreach(viewModel.newitems中的var newsViewModel)
{
var tb=新文本块
{
Text=newsViewModel.Headline,
FontSize=28,
FontWeight=FontWeights。正常,
前景=画笔。黑色
};
newscavas.Children.Add(tb);
Canvas.SetTop(tb,20);
Canvas.SetLeft(tb,-999);
textBlocks.AddLast(tb);
}
Dispatcher.BeginInvoke(新操作(()=>
{
var节点=textBlocks.First;
while(节点!=null)
{
if(node.Previous!=null)
{
//这就是实际宽度始终为零的位置
var left=Canvas.GetLeft(node.Previous.Value)+node.Previous.Value.ActualWidth+Gap;
Canvas.SetLeft(node.Value,左);
}
其他的
SetLeft(node.Value,newscavas.Width+Gap);
node=node.Next;
}
}));
}
任何控件的实际高度
或实际宽度
在加载之前总是为零
测量排列渲染
在您的情况下,我建议使用Loaded
或SizeChanged
事件TextBlock
对您有利。使用Canvas
布局TextBlock
有什么特别的原因吗?如果没有,最好使用水平方向的StackPanel
,它将为您处理布局数学 如果要继续执行dispatcher调用,请将优先级设置为loaded,然后它将与loaded事件同时被调用,并且您应该有一个值。
BeginInvoke上的重载也具有优先权。您可以始终将delgate附加到PropertyStatData/OnValueChanged
上,并且当实际高度/实际宽度
从0更改为某个值时,调整您的滚动,实际宽度/实际高度
至少呈现一次后将有一个值:
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
...
}
虽然有很多文本块,但我必须订阅上一个添加的加载事件,这看起来有点像黑客,你不觉得吗?我想你是对的!除非您将这些TextBlock
控件添加到具有水平方向的StackPanel
中并滚动该面板。在这种情况下,您只能处理面板的事件。控件在调用其加载的事件之前首先呈现。TextBlock不可见且未加载,因此它没有属性ActualWidth。我用Itemscontrol解决了一个类似的问题,但我对Canvas没有把握。尝试LayoutUpdated、SizeChanged等事件。我正在做一个滚动字幕效果,这意味着每个项目都必须是独立的,并且绝对定位在画布上