WPF имеет встроенный механизм, который позволяет переоценивать, переупорядочивать и перерисовывать все Adorners
всякий раз, когда соответствующий AdornedElement
изменяет размер, положение или преобразование. Этот механизм требует от вас соблюдения определенных правил при кодировании вашего рекламодателя, не все из которых документированы так четко, как следовало бы.
Сначала я отвечу на ваш заглавный вопрос , почему ваш рекламодатель не выполняет повторную визуализацию, а затем объясню, как это исправить.
Почему рекламодатель не рендерит заново
Всякий раз, когда AdornerLayer получает уведомление LayoutChanged, он сканирует каждого из своих Adorners, чтобы увидеть, изменился ли AdornedElement
в размере, положении или преобразовании. Если это так, он устанавливает флаги, чтобы заставить Adorner
измерить, упорядочить и снова отобразить - примерно эквивалентно InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();
.
Что обычно происходит в этой ситуации, так это то, что управление сначала измеряется, затем организуется, а затем воспроизводится. Фактически, WPF пытается сделать это наиболее распространенным случаем, потому что это наиболее эффективная последовательность. Однако существует много ситуаций, когда элемент управления может в конечном итоге переставляться и / или перерисовываться перед его повторным измерением. Это законный порядок событий в WPF (для обеспечения гибких методов компоновки), но он не распространен, поэтому его часто не тестируют.
Правильно реализованный Adorner
или другой UIElement
будет осторожно вызывать InvalidateVisual()
каждый раз, когда на рендеринг может повлиять , если только не были изменены AffectsRender
свойства зависимости.
В вашем случае размер вашего рекламодателя явно влияет на рендеринг. Свойства размера не являются AffectsRender
свойствами зависимостей, поэтому необходимо вручную вызвать InvalidateVisual()
, когда они изменятся. Если вы этого не сделаете, WPF, возможно, никогда не узнает, как сделать ваш рекламодатель заново.
То, что происходит в вашей ситуации, вероятно, таково:
- Макет завершен, и событие
LayoutChanged
запускается
AdornerLayer
обнаруживает изменение размера вашего AdornedElement
AdornerLayer
планирует вашего рекламодателя для повторного измерения, изменения макета и повторной визуализации
- Что-то вызывает вызов
Arrange()
, что приводит к изменению макета и повторному рендерингу перед повторным измерением. Это заставляет WPF думать, что рекламодателю больше не нужно переделывать или перерисовывать.
- Механизм макета обнаруживает, что рекламодатель нуждается в измерении, и звонит
Measure
- Авторский
MeasureOverride
пересчитывает желаемый размер , но ничего не делает для того, чтобы сообщить WPF, что рекламодатель должен повторно визуализировать
- Механизм верстки решает, что больше ничего не нужно делать, и поэтому рекламодатель никогда не перерисовывает
Что вы можете сделать, чтобы исправить это
Решение, конечно, состоит в том, чтобы исправить ошибку в Adorner
, вызывая InvalidateVisual()
всякий раз, когда элемент управления повторно измеряется, например:
protected override Size MeasureOverride(Size constraint)
{
var result = base.MeasureOverride(constraint);
// ... add custom measure code here if desired ...
InvalidateVisual();
return result;
}
Выполнение этого приведет к тому, что ваш Adorner будет постоянно соблюдать все правила WPF, поэтому он будет работать, как и ожидалось, во всех ситуациях. Это также наиболее эффективное решение, поскольку InvalidateVisual()
вообще ничего не сделает, кроме тех случаев, когда это действительно необходимо.