3

我在 WPF 行元素上创建了一个装饰器,因为需要添加一些文本。

现在,当这条线被移动时,装饰器不会自动“跟随”这条线。事实上,它并没有刷新它的ef:

替代文字 替代文字
这里黑色曲线是控制图,红色“120 m”是装饰图。

一些代码

    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

4 回答 4

9

为什么不调用 MeasureOverride 等

您的装饰器MeasureOverride,ArrangeOverrideOnRender没有被调用,因为您的 SegmentLine 控件永远不会改变大小或位置:

  • 由于您的 SegmentLine 没有实现MeasureOverride,因此它始终具有布局引擎分配的默认大小。
  • 由于您的 SegmentLine 没有实现ArrangeOverride或操作任何转换,因此它的位置始终恰好是容器的左上角。

Adorner 的MeasureOverride,ArrangeOverride并且OnRender仅在以下条件下由 WPF 调用:

  1. 更改大小或位置(这AdornedElement是最常见的情况),或
  2. Adorner的属性之一chagnes 并且该属性标记为AffectsMeasure, AffectsArrange, 或AffectsRender, 或
  3. 你打电话给InvalidateMeasure(), InvalidateArrange(), 或InvalidateVisuaul()在装饰器上。

因为您的 SegmentLine 永远不会改变大小或位置,所以案例 1 不适用。由于您在 Adorner 上没有任何此类属性并且不调用InvalidateMeasure(), InvalidateArrange()or InvalidateVisual(),因此其他情况也不适用。

Adorner 重新测量的精确规则

以下是装饰元素更改触发对 的调用时的精确规则Adorner.MeasureOverride

  1. Measure装饰元素必须通过使其无效或Arrange响应某些事件来强制布局通过。这可以通过使用 or 更改 DependencyPropertyAffectsMeasureAffectsArrange直接调用InvalidateMeasure(), InvalidateArrange()or来自动触发InvalidateVisual()

  2. 在失效和布局传递之间,不得直接从用户代码调用装饰元素Measure和方法。Arrange换句话说,您必须等待布局管理器完成这项工作。

  3. 装饰元素必须对其RenderSize或其Transform.

  4. 和装饰元素之间的所有变换的组合AdornerLayer必须是仿射的。只要您不使用 3D,通常就是这种情况。

您的 SegmentLine 只是在新的地方画线,而不是更新自己的尺寸,从而省略了我上面的要求 3。

推荐

通常,我会建议您的装饰器将 AffectsRender DependencyProperties 绑定到 SegmentLine 的属性,因此每当 X1、Y1 等在 SegmentLine 中发生更改时,它们也会在 Adorner 中更新,从而导致 Adorner 重新渲染。这提供了一个非常干净的界面,因为装饰器可以用于任何具有 X1、Y1 等属性的控件,但它的效率低于紧密耦合它们。

在您的情况下,装饰器显然与您的 SegmentLine 紧密绑定,因此我认为InvalidateVisual()从 SegmentLine调用装饰器同样有意义OnRender(),如下所示:

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();
  } 
}
于 2010-11-18T00:24:32.663 回答
0

白痴修复,但它的工作原理

    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();
        }
    }

现在,问题是当我点击装饰器时,控件本身不会收到点击...

于 2010-11-17T19:48:37.597 回答
0

线路是如何移动的?装饰器的 MeasureOverride 或 ArrangeOverride 是否在移动后被调用?OnRender 只会在视觉无效(例如 invalidatevisual)时被调用,所以我猜渲染没有被无效。

于 2010-11-17T15:04:20.687 回答
0

可能是你想用来segmentBounds定义midPoint?否则它在那里做什么?看起来您正在定义midPoint相对于未重新渲染的段。

于 2010-11-17T15:25:40.127 回答