我在WPF线元素上创建了一个装饰器,因为需要添加一些文本。
现在,当移动此行时,装饰器不会自动“跟随”该行。事实上,它并没有刷新它:
这里黑色曲线是控制图,红色“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);
}
}
}
答案 0 :(得分:7)
为什么不调用MeasureOverride等
您的广告系列MeasureOverride
,ArrangeOverride
和OnRender
未被调用,因为您的SegmentLine控件永远不会更改大小或位置:
MeasureOverride
,因此它始终具有布局引擎指定的默认大小。ArrangeOverride
或操纵任何转换,因此其位置始终恰好位于容器的左上角。 Adorner的MeasureOverride
,ArrangeOverride
和OnRender
仅在以下条件下由WPF调用:
AdornedElement
更改大小或位置(这是最常见的情况)或AffectsMeasure
,AffectsArrange
或AffectsRender
,或InvalidateMeasure()
,InvalidateArrange()
或InvalidateVisuaul()
。由于您的SegmentLine永远不会更改大小或位置,因此案例1不适用。由于您在Adorner上没有任何此类属性,并且未拨打InvalidateMeasure()
,InvalidateArrange()
或InvalidateVisual()
,因此其他情况也不适用。
Adorner重新衡量的精确规则
以下是装饰元素更改何时触发对Adorner.MeasureOverride
的调用的准确规则:
装饰元素必须通过使其Measure
或Arrange
无效来强制执行布局,以响应某些事件。这可以通过使用AffectsMeasure
或AffectsArrange
更改为DependencyProperty,或直接调用InvalidateMeasure()
,InvalidateArrange()
或InvalidateVisual()
来自动触发。
不得在失效和布局传递之间直接从用户代码调用已装饰元素的Measure
和Arrange
方法。换句话说,您必须等待布局管理器完成工作。
装饰元素必须对其RenderSize
或其Transform
进行非平凡的更改。
AdornerLayer
和装饰元素之间的所有变换的组合必须是仿射的。只要您不使用3D,通常就是这种情况。
你的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();
}
}
现在,问题在于,当我点击一个装饰器时,控件本身并没有收到命中...