ser*_*hio 3 wpf adorner wpf-controls
我在WPF线元素上创建了一个装饰器,因为需要添加一些文本.
现在,当移动此行时,装饰器不会自动"跟随"该行.事实上,它没有刷新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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
为什么没有调用MeasureOverride等
你的装饰器的MeasureOverride,ArrangeOverride和OnRender不被调用,因为你的SegmentLine控制是永远不会改变大小或位置:
MeasureOverride,因此它始终具有布局引擎指定的默认大小.ArrangeOverride或操纵任何变换,因此其位置始终恰好位于容器的左上角.装饰器的MeasureOverride,ArrangeOverride并且OnRender仅通过WPF在这些条件下称为:
AdornedElement变化大小或位置(这是最常见的情况),或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的属性,因此在SegmentLine中任何时候X1,Y1等都会发生变化,他们也会在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();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这不涉及从可视树中删除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();
}
}
Run Code Online (Sandbox Code Playgroud)