В 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".