我有两个TextBlock,我在Canvas上连续定位。第一种情况正常:
TextBlock text1 = new TextBlock();
text1.Text = "Not ";
text1.FontSize = 18;
Canvas.SetTop(text1, 20);
Canvas.SetLeft(text1, 20);
canvas.Children.Add(text1);
TextBlock text2 = new TextBlock();
text2.Text = "bad!";
text2.FontSize = 18;
Canvas.SetTop(text2, 20);
canvas.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(delegate(Object state)
{
Canvas.SetLeft(text2, 20 + text1.ActualWidth);
return null;
}
), null);
canvas.Children.Add(text2);
结果:
但是,第二种不使用BeginInvoke()的情况失败了:
TextBlock text1 = new TextBlock();
text1.Text = "Not ";
text1.FontSize = 18;
Canvas.SetTop(text1, 20);
Canvas.SetLeft(text1, 20);
canvas.Children.Add(text1);
TextBlock text2 = new TextBlock();
text2.Text = "bad!";
text2.FontSize = 18;
Canvas.SetTop(text2, 20);
Canvas.SetLeft(text2, 20 + text1.ActualWidth); // ActualWidth is zero.
canvas.Children.Add(text2);
结果:
现在,我知道在第二种情况下,WPF渲染尚未发生。我的问题很简单:在这种情况下使用的首选模式是什么?我需要知道UI控件的实际坐标值,这些坐标值仅在渲染完成后才可用?
(例如,使用BeginInvoke()的方法是一个很好的解决方案吗?整个代码是否应该包含在一个巨大的BeginInvoke()中?)
答案 0 :(得分:2)
回答你的问题:
Dispatcher.BeginInvoke()
将对Dispatcher的“待处理作业”队列中的操作进行排队。这使得它能够处理第一个UI元素的添加,并在继续执行代码之前运行Layout
和Render
遍。
因此,当您运行代码时,已经计算了第一个TextBlock的大小,您可以获得它。
同样,我不知道你要做什么,但在代码中创建UI元素通常是设计不佳的标志。 WPF不是winforms,WPF的方式与在winforms中做任何事情所需的可怕黑客完全不同。
编辑:
这是我使用WrapPanel
和一些RenderTransform
:
<Window x:Class="MiscSamples.MovingWords"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MovingWords" Height="300" Width="300">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Thumb DragDelta="Thumb_DragDelta" Margin="2">
<Thumb.Template>
<ControlTemplate>
<TextBlock Text="{Binding Text}"
FontSize="{Binding FontSize}"
Foreground="{Binding Color}"/>
</ControlTemplate>
</Thumb.Template>
<Thumb.RenderTransform>
<TranslateTransform X="{Binding OffsetX}" Y="{Binding OffsetY}"/>
</Thumb.RenderTransform>
</Thumb>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
代码背后:
public partial class MovingWords : Window
{
public ObservableCollection<MovingWordModel> Words { get; set; }
public MovingWords()
{
InitializeComponent();
Words = new ObservableCollection<MovingWordModel>
{
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "Hello!!"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "This"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "is"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "the"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "Power"},
new MovingWordModel() {Color = "Black", FontSize = 18, Text = "of"},
new MovingWordModel() {Color = "Blue", FontSize = 18, Text = "WPF"},
};
DataContext = Words;
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
if (thumb == null)
return;
var word = thumb.DataContext as MovingWordModel;
if (word == null)
return;
word.OffsetX += e.HorizontalChange;
word.OffsetY += e.VerticalChange;
}
}
数据模型:
public class MovingWordModel:PropertyChangedBase
{
public string Text { get; set; }
public int FontSize { get; set; }
public string Color { get; set; }
private double _offsetX;
public Double OffsetX
{
get { return _offsetX; }
set
{
_offsetX = value;
OnPropertyChanged("OffsetX");
}
}
private double _offsetY;
public double OffsetY
{
get { return _offsetY; }
set
{
_offsetY = value;
OnPropertyChanged("OffsetY");
}
}
}
PropertyChangedBase:
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
您可以点击并拖动单词来移动它们。
请注意,拖动的值将存储在OffsetX
和OffsetY
属性中。这种方法的唯一问题是你有点失去分辨率独立性,因为偏移值实际上会将单词从默认位置移动(由WrapPanel
确定,因此它们可能会发生变化,具体取决于WrapPanel
本身的大小。