WPF Control перемещается, но его Adorner - нет: "/ - PullRequest
3 голосов
/ 17 ноября 2010

Я создал рекламный элемент для строчного элемента WPF, потому что не было возможности добавить текст.

Теперь, когда эта строка перемещается, рекламодатель не «следует» за ней автоматически.Фактически, это не обновляет само себя:

alt text alt text
здесь черные кривые - это контрольный чертеж, а красный "120 м" - дополнительный.Какой-то код

    void SegmentLine_Loaded(object sender, RoutedEventArgs e)
    {
        AdornerLayer aLayer = AdornerLayer.GetAdornerLayer(this);
        if (aLayer != null)
        {
            aLayer.Add(new TextAdorner(this));
        }
    }

    class TextAdorner : Adorner
    {
        public TextAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            SegmentLine segment = (this.AdornedElement as SegmentLine);

            if (segment != null)
            {
                Rect segmentBounds = new Rect(segment.DesiredSize);
                var midPoint = new Point(
                    (segment.X1 + segment.X2) / 2.0, 
                    (segment.Y1 + segment.Y2) / 2.0);
                var lineFont = // get line font as Font

                FormattedText ft = new FormattedText(
                    string.Format("{0} m", segment.Distance),
                    Thread.CurrentThread.CurrentCulture,
                    System.Windows.FlowDirection.LeftToRight,
                    new Typeface(lineFont.FontFamily.ToString()),
                    ligneFont.Size, Brushes.Red);

                drawingContext.DrawText(ft, midPoint);
            }

        }
    }

Ответы [ 4 ]

7 голосов
/ 18 ноября 2010

Почему MeasureOverride и т. Д. Не вызывается

Ваши рекламодатели MeasureOverride, ArrangeOverride и OnRender не вызывают, потому что ваш элемент управления SegmentLine никогда не меняет размер или позицию:

  • Поскольку ваша SegmentLine не реализует MeasureOverride, он всегда имеет размер по умолчанию, назначенный механизмом компоновки.
  • Поскольку ваша SegmentLine не реализует ArrangeOverride и не манипулирует какими-либо преобразованиями, ее позиция всегда точно соответствует левому верхнему углу контейнера.

WPF MeasureOverride, ArrangeOverride и OnRender Adorner вызываются только WPF при следующих условиях:

  1. AdornedElement изменяет размер или положение (это наиболее распространенный случай), или
  2. Одно из свойств Adorner подвержено ошибкам, и это свойство помечено AffectsMeasure, AffectsArrange, AffectsRender или
  3. Вы звоните InvalidateMeasure(), InvalidateArrange() или InvalidateVisuaul() на адресата.

Поскольку ваша SegmentLine никогда не изменяет размер или положение, случай 1 не применяется. Поскольку у вас нет таких свойств в Adorner и вы не вызываете InvalidateMeasure(), InvalidateArrange() или InvalidateVisual(), другие случаи также не применяются.

Точные правила пересмотра Adorner

Вот точные правила для случая, когда изменение украшенного элемента вызывает вызов Adorner.MeasureOverride:

  1. Украшенный элемент должен форсировать проход макета, аннулируя его Measure или Arrange в ответ на какое-либо событие. Это может быть вызвано автоматически изменением свойства DependencyProperty с AffectsMeasure или AffectsArrange или прямым вызовом InvalidateMeasure(), InvalidateArrange() или InvalidateVisual().

  2. Методы Measure и Arrange украшенного элемента не должны вызываться напрямую из кода пользователя между недействительностью и передачей макета. Другими словами, вы должны подождать, пока менеджер раскладки выполнит эту работу.

  3. Украшенный элемент должен вносить нетривиальное изменение либо в RenderSize, либо в Transform.

  4. Комбинация всех преобразований между 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();
  } 
}
0 голосов
/ 17 ноября 2010

Может быть, вы хотите использовать segmentBounds для определения midPoint?Иначе что он там делает?Похоже, вы определяете midPoint относительно не перерисованного сегмента.

0 голосов
/ 17 ноября 2010

исправление идиота, но оно работает

    AdornerLayer aLayer;
    void SegmentLine_Loaded(object sender, RoutedEventArgs e)
    {
        aLayer = AdornerLayer.GetAdornerLayer(this);
        if (aLayer != null)
        {
            aLayer.Add(new TextAdorner(this));
        }
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        if (aLayer != null)
        {
            aLayer.Update();
        }
    }

Теперь проблема в том, что когда я нажимаю на ярлык, сам элемент управления не получает попадание ...

0 голосов
/ 17 ноября 2010

Как движется линия?Будет ли MeasureOverride или ArrangeOverride рекламодателя вызываться после перемещения?OnRender будет вызываться только в том случае, если визуальный элемент признан недействительным (например, invalidatevisual), поэтому я предполагаю, что рендер не был признан недействительным.

...