WPF 控件移动,但其装饰器 - 不是:“/
我在 WPF 行元素上创建了一个装饰器,因为需要添加一些文本。
现在,当移动该线时,装饰器不会自动“跟随”该线。事实上,它不会刷新它自己:
这里黑色曲线是控制图,红色“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);
}
}
}
I created an adorner on a WPF line element, because there was neet to add some text.
Now, when this line is moved, the adorner does not "follow" the line automatically. In fact, it does not refresh itsef:
here black curves is the Control drawing, and the red "120 m" is the adorner one.
Some code
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);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
为什么 MeasureOverride 等没有被调用
你的装饰器的
MeasureOverride
、ArrangeOverride
和OnRender
没有被调用因为您的 SegmentLine 控件永远不会更改大小或位置: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 的属性,因此只要 X1、Y1 等在 SegmentLine 中发生更改,它们也会在装饰器中更新,从而导致装饰器重新渲染。这提供了一个非常干净的界面,因为装饰器可以在具有属性 X1、Y1 等的任何控件上使用,但它的效率低于紧密耦合它们。
在您的情况下,装饰器显然与您的 SegmentLine 紧密绑定,因此我认为从 SegmentLine 的 OnRender() 调用装饰器上的 InvalidateVisual() 也同样有意义,如下所示:
请注意,这不处理 SegmentLine 从可视化树中删除然后稍后再次添加的情况。您的原始代码也不处理这个问题,所以我避免了处理这种情况的复杂性。如果您需要它工作,请执行以下操作:
Why MeasureOverride, etc aren't being called
Your adorner's
MeasureOverride
,ArrangeOverride
, andOnRender
aren't being called because your SegmentLine control is never changing size or position:MeasureOverride
, it always has the default size assigned by the layout engine.ArrangeOverride
or manipulate any transforms, its position is always exactly the upper-left corner of the container.The Adorner's
MeasureOverride
,ArrangeOverride
andOnRender
are only called by WPF under these conditions:AdornedElement
changes size or position (this the most common case), orAffectsMeasure
,AffectsArrange
, orAffectsRender
, orInvalidateMeasure()
,InvalidateArrange()
, orInvalidateVisuaul()
on the adorner.Because your SegmentLine never changes size or position, case 1 doesn't apply. Since you don't have any such properties on the Adorner and don't call
InvalidateMeasure()
,InvalidateArrange()
orInvalidateVisual()
, the other cases don't apply either.Precise rules for Adorner re-measure
Here are the precise rules for when an adorned element change triggers a call to
Adorner.MeasureOverride
:The adorned element must force a layout pass by invalidating its
Measure
orArrange
in response to some event. This could be triggered automatically by a change to a DependencyProperty withAffectsMeasure
orAffectsArrange
, or by a direct call toInvalidateMeasure()
,InvalidateArrange()
orInvalidateVisual()
.The adorned element's
Measure
andArrange
methods must not be called directly from user code between the invalidation and the layout pass. In other words, you must wait for the layout manager to do the job.The adorned element must make a non-trivial change to either its
RenderSize
or itsTransform
.The combination of all transforms between the
AdornerLayer
and the adorned element must be affine. This will generally be the case as long as you are not using 3D.Your SegmentLine is just drawing the line in a new place rather than updating its own dimensions, thereby omitting my requirement #3 above.
Recommendation
Normally I would recommend your adorner have AffectsRender DependencyProperties bound to the SegmentLine's properties, so any time X1, Y1, etc change in the SegmentLine they are also updated in the Adorner which causes the Adorner to re-render. This provides a very clean interface, since the adorner can be used on any control that has properties X1, Y1, etc, but it is less efficient than tightly coupling them.
In your case the adorner is clearly tightly bound to your SegmentLine, so I think it makes just as much sense to call
InvalidateVisual()
on the adorner from the SegmentLine'sOnRender()
, like this:Note that this doesn't deal with the situation where the SegmentLine is removed from the visual tree and then added again later. Your original code doesn't deal with this either, so I avoided the complexity of dealing with that case. If you need that to work, do this instead:
线路如何移动?移动后是否会调用装饰器的 MeasureOverride 或 ArrangeOverride?仅当视觉效果无效(例如invalidatevisual)时才会调用OnRender,因此我猜测渲染并未无效。
How is the line being moved? Does the MeasureOverride or ArrangeOverride of the adorner get invoked after the move? OnRender will only get invoked if the visual is invalidated (e.g. invalidatevisual) so I'm guessing that the render isn't being invalidated.
也许您想使用
segmentBounds
来定义midPoint
?否则它在那里做什么?看起来您正在定义相对于未重新渲染的段的midPoint
。May be you wanted to use
segmentBounds
to definemidPoint
? Otherwise what is it doing there? Looks like you are definingmidPoint
relative to not rerendered segment.白痴修复,但它有效
现在,问题是当我单击装饰器时,控件本身没有收到点击...
idiot fix, but it works
Now, the problem is that when I click on a the adorner the control itself does not recieve the hit...