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

Я делаю пользовательский рендеринг в подклассе 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 не происходит, когда он должен. Мысли?

Mark

1 Ответ

4 голосов
/ 13 апреля 2011

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

EDIT:

Метод 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».

...