WPF控制移动但其装配者 - 不是:“/

时间:2010-11-17 14:34:58

标签: wpf wpf-controls adorner

我在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 个答案:

答案 0 :(得分:7)

为什么不调用MeasureOverride等

您的广告系列MeasureOverrideArrangeOverrideOnRender未被调用,因为您的SegmentLine控件永远不会更改大小或位置:

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

Adorner的MeasureOverrideArrangeOverrideOnRender仅在以下条件下由WPF调用:

  1. AdornedElement更改大小或位置(这是最常见的情况)或
  2. 其中一个 Adorner 的属性chagnes,该属性标记为AffectsMeasureAffectsArrangeAffectsRender,或
  3. 您在装饰工具上致电InvalidateMeasure()InvalidateArrange()InvalidateVisuaul()
  4. 由于您的SegmentLine永远不会更改大小或位置,因此案例1不适用。由于您在Adorner上没有任何此类属性,并且未拨打InvalidateMeasure()InvalidateArrange()InvalidateVisual(),因此其他情况也不适用。

    Adorner重新衡量的精确规则

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

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

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

    3. 装饰元素必须对其RenderSize或其Transform进行非平凡的更改。

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

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

      <强>建议

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

      在您的情况下,装饰器显然与您的SegmentLine紧密相关,因此我认为从SegmentLine的InvalidateVisual()调用装配工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();
        } 
      }
      

答案 1 :(得分:0)

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

答案 2 :(得分:0)

您可能想使用segmentBounds来定义midPoint吗?否则它在那里做什么?您似乎相对于重新呈现的细分来定义midPoint

答案 3 :(得分: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();
        }
    }

现在,问题在于,当我点击一个装饰器时,控件本身并没有收到命中...