0

キャンバスに連続して配置している 2 つの TextBlock があります。最初のケースは問題なく動作します:

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() を使用しない 2 番目のケースは失敗します。

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

結果:

ここに画像の説明を入力

これで、2 番目のケースでは、WPF レンダリングがまだ行われていないことがわかりました。私の質問は次のとおりです。レンダリングが行われた後にのみ使用できる UI コントロールの実際の座標値を知る必要がある場合に使用するのに適したパターンは何ですか?

(たとえば、BeginInvoke() を使用するアプローチは適切な解決策ですか? コード全体を巨大な BeginInvoke() で囲む必要がありますか?)

4

1 に答える 1

2

あなたの質問に答えるには:

Dispatcher.BeginInvoke()Dispatcher の「保留中のジョブ」キューに操作をキューに入れます。これにより、最初の UI 要素の追加を処理し、コードの実行を続行する前にLayoutおよびパスを実行できます。Render

したがって、コードを実行すると、最初の TextBlock のサイズは既に計算されており、取得できます。

繰り返しますが、あなたが何をしようとしているのかはわかりませんが、コードで UI 要素を作成することは、通常、設計が不十分であることを示しています。WPF は winform ではありません。WPF の方法は、winform で何かを行うために必要な恐ろしいハックとはまったく異なります。

編集:

WrapPanelこれは aと 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));
        }
    }

単語をクリックしてドラッグすると、単語を移動できます。

ドラッグの値はOffsetXおよびOffsetYプロパティに保存されることに注意してください。このアプローチの唯一の問題は、解像度の独立性がいくらか失われることです。これは、オフセット値が実際に単語をデフォルトの位置から移動するためです (これは によって決定されるため、自体WrapPanelのサイズに応じて変更される可能性があります)。 WrapPanel.

于 2013-04-02T20:55:48.843 に答える