现在,当移动此行时,装饰器不会自动“跟随”该行.事实上,它没有刷新itsef:
这里黑色曲线是控制图,红色“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);
}
}
}
为什么没有调用MeasureOverride等
您的装饰者的MeasureOverride,ArrangeOverride和OnRender未被调用,因为您的SegmentLine控件永远不会更改大小或位置:
>由于SegmentLine未实现MeasureOverride,因此它始终具有布局引擎指定的默认大小.
>由于SegmentLine未实现ArrangeOverride或操纵任何变换,因此其位置始终恰好位于容器的左上角.
Adorner的MeasureOverride,ArrangeOverride和OnRender仅在以下条件下由WPF调用:
> AdornedElement会更改大小或位置(这是最常见的情况),或
>其中一个Adorner的属性chagnes和该属性标记为AffectsMeasure,AffectsArrange或AffectsRender,或者
>您在装饰器上调用InvalidateMeasure(),InvalidateArrange()或InvalidateVisuaul().
由于您的SegmentLine永远不会更改大小或位置,因此案例1不适用.由于您在Adorner上没有任何此类属性,并且未调用InvalidateMeasure(),InvalidateArrange()或InvalidateVisual(),因此其他情况也不适用.
Adorner重新测量的精确规则
以下是装饰元素更改何时触发对Adorner.MeasureOverride调用的准确规则:
>装饰元素必须通过使其测量或排列无效来强制布局传递以响应某些事件.这可以通过使用AffectsMeasure或AffectsArrange更改DependencyProperty,或直接调用InvalidateMeasure(),InvalidateArrange()或InvalidateVisual()来自动触发.
>不得在失效和布局过程之间直接从用户代码调用装饰元素的测量和排列方法.换句话说,您必须等待布局管理器完成工作.
>装饰元素必须对其RenderSize或其Transform进行非平凡的更改.
> AdornerLayer和装饰元素之间的所有变换的组合必须是仿射的.只要您不使用3D,通常就是这种情况.
您的SegmentLine只是在新的地方绘制线而不是更新自己的尺寸,从而省略了我上面的要求#3.
建议
通常我会建议您的装饰者将AffectsRender DependencyProperties绑定到SegmentLine的属性,因此在SegmentLine中任何时候X1,Y1等都会发生变化,他们也会在Adorner中更新,这会导致Adorner重新渲染.这提供了一个非常干净的界面,因为装饰器可用于任何具有属性X1,Y1等的控件,但它比紧耦合它们的效率低.
在你的情况下,装饰者显然与你的SegmentLine紧密相关,所以我认为从SegmentLine的OnRender()调用装配件上的InvalidateVisual()同样有意义,如下所示:
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();
}
}
