Ответ 1
WPF имеет встроенный механизм, который заставляет все Adorners
переоцениваться, переупорядочиваться и переписываться всякий раз, когда соответствующий AdornedElement
изменяет размер, положение или преобразование. Этот механизм требует, чтобы вы следовали определенным правилам при кодировании вашего adorner, не все из которых документированы так четко, как они должны быть.
Сначала я отвечу на ваш титульный вопрос , почему ваш adorner не будет повторно перерисовываться, а затем объяснит лучший способ его исправить.
Почему adorner не реинжинирует
Всякий раз, когда AdornerLayer получает уведомление LayoutChanged, он сканирует каждый из своих Adorners, чтобы увидеть, изменился ли AdornedElement
по размеру, положению или преобразованию. Если это так, он устанавливает флаги, чтобы заставить Adorner
измерять, упорядочивать и отображать снова - примерно эквивалентно InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();
.
Что обычно происходит в этой ситуации, так это то, что контроль сначала измеряется, затем упорядочивается, затем отображается. На самом деле WPF пытается сделать это наиболее распространенным случаем, потому что это наиболее эффективная последовательность. Однако есть много ситуаций, когда контроль может быть перегруппирован и/или удален, прежде чем он будет переоценен. Это законный порядок событий в WPF (для обеспечения гибких методов компоновки), но он не является обычным явлением, поэтому его часто не тестируют.
Правильно реализованный Adorner
или другой UIElement
будет осторожно вызывать InvalidateVisual()
в любое время, когда рендеринг может быть затронут, если только свойства зависимостей AffectsRender
не были изменены.
В вашем случае размер вашего adorner явно влияет на рендеринг. Свойства размера не являются AffectsRender
свойствами зависимостей, поэтому при их изменении необходимо вручную вызвать InvalidateVisual()
. Если вы этого не сделаете, WPF, возможно, никогда не узнает о том, чтобы повторно рекламировать вашего adorner.
Что происходит в вашей ситуации, возможно, это:
- Макет завершается, а событие
LayoutChanged
запускается -
AdornerLayer
обнаруживает изменение размера наAdornedElement
-
AdornerLayer
назначает вашего рекламодателя для повторной оценки, повторной компоновки и повторной рендеринга - Что-то вызывает вызов
Arrange()
, который вызывает повторную компоновку и повторную визуализацию до повторной оценки. Это заставляет WPF думать, что рекламодатель больше не нуждается в повторной компоновке или повторной обработке. - Механизм компоновки обнаруживает, что рекламодатель нуждается в измерении и вызывает
Measure
- Adorner
MeasureOverride
пересчитывает желаемый размер, но ничего не говорит WPF, что рекламодатель нуждается в повторной рендеринге - Механизм компоновки решает, что больше нечего делать, и поэтому adorner никогда не переделывает
Что вы можете сделать, чтобы исправить его
Разумеется, решение состоит в том, чтобы исправить ошибку в 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()
ничего не сделает, кроме тех случаев, когда это действительно необходимо.