0

我有两个 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() 中?)

4

1 回答 1

2

要回答您的问题:

Dispatcher.BeginInvoke()将调度程序的“待处理作业”队列中的操作排队。这允许它能够处理添加的第一个 UI 元素,并在继续执行代码之前运行Layout和传递。Render

因此,当你的代码运行时,第一个 TextBlock 的大小已经计算出来了,你可以得到它。

同样,我不知道您要做什么,但在代码中创建 UI 元素通常是设计不佳的标志。WPF 不是 winforms,WPF 方式与在 winforms 中做任何事情所需的可怕黑客完全不同。

编辑:

这是我使用 aWrapPanel和 some的方法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");
        }
    }
}

属性更改基础:

    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));
        }
    }

您可以单击并拖动单词以移动它们。

请注意,拖动的值将存储在OffsetXOffsetY属性中。这种方法的唯一问题是您在某种程度上失去了分辨率独立性,因为偏移值实际上会将单词从其默认位置移动(由 确定WrapPanel,因此它们可能会根据WrapPanel自身的大小而变化) .

于 2013-04-02T20:55:48.843 回答