Почему MeasureOverride и т. Д. Не вызывается
Ваши рекламодатели MeasureOverride
, ArrangeOverride
и OnRender
не вызывают, потому что ваш элемент управления SegmentLine никогда не меняет размер или позицию:
- Поскольку ваша SegmentLine не реализует
MeasureOverride
, он всегда имеет размер по умолчанию, назначенный механизмом компоновки.
- Поскольку ваша SegmentLine не реализует
ArrangeOverride
и не манипулирует какими-либо преобразованиями, ее позиция всегда точно соответствует левому верхнему углу контейнера.
WPF MeasureOverride
, ArrangeOverride
и OnRender
Adorner вызываются только WPF при следующих условиях:
-
AdornedElement
изменяет размер или положение (это наиболее распространенный случай), или
- Одно из свойств Adorner подвержено ошибкам, и это свойство помечено
AffectsMeasure
, AffectsArrange
, AffectsRender
или
- Вы звоните
InvalidateMeasure()
, InvalidateArrange()
или InvalidateVisuaul()
на адресата.
Поскольку ваша SegmentLine никогда не изменяет размер или положение, случай 1 не применяется. Поскольку у вас нет таких свойств в Adorner и вы не вызываете InvalidateMeasure()
, InvalidateArrange()
или InvalidateVisual()
, другие случаи также не применяются.
Точные правила пересмотра Adorner
Вот точные правила для случая, когда изменение украшенного элемента вызывает вызов Adorner.MeasureOverride
:
Украшенный элемент должен форсировать проход макета, аннулируя его Measure
или Arrange
в ответ на какое-либо событие. Это может быть вызвано автоматически изменением свойства DependencyProperty с AffectsMeasure
или AffectsArrange
или прямым вызовом InvalidateMeasure()
, InvalidateArrange()
или InvalidateVisual()
.
Методы Measure
и Arrange
украшенного элемента не должны вызываться напрямую из кода пользователя между недействительностью и передачей макета. Другими словами, вы должны подождать, пока менеджер раскладки выполнит эту работу.
Украшенный элемент должен вносить нетривиальное изменение либо в RenderSize
, либо в Transform
.
Комбинация всех преобразований между AdornerLayer
и украшенным элементом должна быть аффинной. Как правило, так будет, если вы не используете 3D.
Ваша SegmentLine просто рисует линию на новом месте, а не обновляет свои собственные измерения, тем самым опуская мое требование № 3 выше.
Рекомендация
Обычно я бы порекомендовал вашему рекламодателю привязать свойства AffectsRender DependencyProperties к свойствам SegmentLine, поэтому каждый раз, когда X1, Y1 и т. Д. Изменяются в SegmentLine, они также обновляются в Adorner, что приводит к повторной визуализации Adorner. Это обеспечивает очень чистый интерфейс, так как украшатель может использоваться на любом элементе управления, который имеет свойства X1, Y1 и т. Д., Но он менее эффективен, чем их тесная связь.
В вашем случае, рекламодатель явно тесно связан с вашей SegmentLine, поэтому я думаю, что имеет такой же смысл звонить InvalidateVisual()
на рекламодателя из OnRender()
из SegmentLine, например:
public class SegmentLine : Shape
{
TextAdorner adorner;
...
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if(adorner==null)
{
var layer = AdornerLayer.GetAdornerLayer(this); if(layer==null) return;
adorner = new TextAdorner(this);
... set other adorner properties and events ...
layer.Add(adorner);
}
adorner.InvalidateVisual();
}
}
Обратите внимание, что это не относится к ситуации, когда SegmentLine удаляется из визуального дерева, а затем снова добавляется позже. Ваш исходный код также не справляется с этим, поэтому я избежал сложности, связанной с этим делом. Если вам нужно, чтобы это работало, сделайте это вместо:
public class SegmentLine : Shape
{
AdornerLayer lastLayer;
TextAdorner adorner;
...
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var layer = AdornerLayer.GetAdornerLayer(this);
if(layer!=lastLayer)
{
if(adorner==null)
{
adorner = new TextAdorner(this);
... set other adorner properties and events ...
}
if(lastLayer!=null) lastLayer.Remove(adorner);
if(layer!=null) layer.Add(adorner);
lastLayer = layer;
}
adorner.InvalidateVisual();
}
}