В WPF, как я могу получить визуализированный размер элемента управления до того, как он будет отображаться?

Я делаю пользовательский рендеринг в подклассе Decorator. Наш рендеринг требует создания сложных геометрий, которые необходимо создавать заново только при изменении фактического размера рендеринга. Таким образом, я переместил создание геометрии в его собственную функцию под названием UpdateGeometry который создает, а затем замораживает геометрию для использования в OnRender, Эту новую функцию нужно вызывать только в ответ на изменение ActualWidth или же ActualHeight,

Более того, похоже, мы должны просто переопределить OnRenderSizeChanged, который согласно документации гласит...

"При переопределении в производном классе участвует в операциях рендеринга, которые направляются системой макетов. Этот метод вызывается после обновления макета и перед рендерингом, если RenderSize элемента изменился в результате обновления макета".

Однако независимо от того, использую ли я переопределение или прослушиваю уведомления об изменении свойства ActualWidth а также ActualHeightМое логирование последовательно показывает OnRender как происходит в первую очередь! Гм... Что?

Чтобы быть уверенным, что это не то, чем я занимался в своем коде, я создал простой подкласс тестового декоратора и добавил туда логирование как при входе, так и при выходе из переопределений. Вот весь класс...

using System;
using System.Windows.Controls;

public class TestControl : Decorator
{

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        Console.WriteLine("OnRender Entered");

        base.OnRender(drawingContext);

        Console.WriteLine("OnRender Exited");
    }

    protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
    {
        Console.WriteLine("OnRenderSizeChanged Entered");

        base.OnRenderSizeChanged(sizeInfo);

        Console.WriteLine("OnRenderSizeChanged Exited");
    }

}

И, как я боялся... вот выход...

OnRender Entered
OnRender Exited
OnRenderSizeChanged Entered
OnRenderSizeChanged Exited

Так чего мне здесь не хватает?

Что еще более важно, как я могу получить ActualWidth а также ActualHeight значения после того, как подсистема верстки выполнила свою работу, но до визуализации элемента управления, поэтому я могу создать геометрию до того, как она понадобится в OnRender переопределить?

Моя последняя реализация переопределяет ArrangeOverride в качестве значения, которое передается, есть размер, содержащий то, что ActualWidth а также ActualHeight значения должны быть после того, как система основного макета принимает во внимание HorizontalAlignment а также VerticalAlignment со значениями 'Stretch', минимумов и максимумов и т. д., но на самом деле они зависят от значения, которое возвращается из этого переопределения, поэтому оно немного сложнее, чем это.

В любом случае, мне все еще интересно, почему OnRenderSizeChanged звонок не происходит, когда он должен. Мысли?

отметка

1 ответ

Решение

В общем, вы должны иметь возможность получить правильный размер из ArrangeOverride. Это не включает в себя такие вещи, как маржа, но это, вероятно, не следует принимать во внимание. Вы можете либо использовать размер, передаваемый в качестве параметра, в качестве размера "рендеринга", либо использовать возвращаемое значение вызова base.ArrangeOverride.

РЕДАКТИРОВАТЬ:

Метод OnRender вызывается из метода Arrange после окончательного вызова OnArrangeOverride. OnRenderSizeChanged, с другой стороны, вызывается из UpdateLayout, который эффективно отправляется для одновременного выполнения для данного раздела визуального дерева. Вот почему OnRenderSizeChanged вызывается после OnRender.

Документация может ссылаться на "рендеринг" в том виде, как он отображается на экране, а не при вызове OnRender. WPF может кэшировать инструкции рендеринга для данного элемента и выполнять их при необходимости. Таким образом, тот факт, что OnRender вызывается раньше OnRenderSizeChanged, не означает, что в это время на экран выводятся действительные инструкции рендеринга.

Вы можете изменить свой OnRenderSizeChanged, чтобы принудительно вызывать OnRender, используя:

protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
{
    Console.WriteLine("OnRenderSizeChanged Entered");

    base.OnRenderSizeChanged(sizeInfo);
    this.InvalidateVisual();

    Console.WriteLine("OnRenderSizeChanged Exited");
}

Вы также можете пропустить свой код OnRender, если RenderSize равен "0,0".

Другие вопросы по тегам