如何设置ControlTemplate内元素的Canvas位置,实现连接线中点放置文本
问题根因
Canvas.Left和Canvas.Top属于附加属性,仅对Canvas的直接子元素生效。你代码中操作的TextBlock是ControlTemplate内部Grid的子元素,父容器是Grid而非外层的Canvas,因此设置的附加属性不会被外层Canvas识别,自然会一直停留在(0,0)位置。
另外你当前用起止点计算中点的逻辑仅对直线路径有效,针对折线、曲线等任意形状的A*路径,会出现标签偏离路径的问题。
可行解决方案
方案1:调整定位逻辑,用变换偏移文本(推荐,无需修改模板)
不用修改现有XAML模板,直接将计算出的坐标转换为控件内部相对坐标,通过TranslateTransform偏移TextBlock即可:
public void PlaceTextBlock() { // 针对任意形状路径,获取路径上50%位置的真实中点,比起止点计算更准确 PathGeometry flattenGeo = MyPath.Data.GetFlattenedPathGeometry(); flattenGeo.GetPointAtFractionLength(0.5, out Point canvasMidPoint, out _); // 将Canvas坐标系的中点转换为当前控件内部的相对坐标 Point relativeMid = this.TransformToVisual(this).Transform(canvasMidPoint); var textBlock = (TextBlock)this.Template.FindName("ConnectorNameTextBlock", this); // 偏移时减去文本自身宽高的一半,实现文本居中对齐中点 textBlock.RenderTransform = new TranslateTransform( relativeMid.X - textBlock.ActualWidth / 2, relativeMid.Y - textBlock.ActualHeight / 2 ); }
注意:该方法需要在控件
Loaded事件触发后调用,否则ActualWidth、ActualHeight未计算会导致偏移不准。如果路径会动态更新,可监听Path.Data的变更事件,自动触发重算。
方案2:修改模板结构,用内嵌Canvas承载文本
将模板内部的Grid替换为Canvas,此时Canvas附加属性对TextBlock生效,可直接设置坐标:
<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type vm:targettypehere}"> <Canvas Margin="0"> <Path Data="{Binding MyPath.Data, RelativeSource={RelativeSource TemplatedParent}}" Stroke="Black" StrokeThickness="2" /> <TextBlock x:Name="ConnectorNameTextBlock" Text="{Binding ConnectorName, RelativeSource={RelativeSource TemplatedParent}}"/> </Canvas> </ControlTemplate> </Setter.Value> </Setter>
对应修改后台代码,计算内部Canvas下的相对坐标后直接赋值即可:
public void PlaceTextBlock() { PathGeometry flattenGeo = MyPath.Data.GetFlattenedPathGeometry(); flattenGeo.GetPointAtFractionLength(0.5, out Point midPoint, out _); Point relativeMid = this.TransformToVisual(this).Transform(midPoint); var textBlock = (TextBlock)this.Template.FindName("ConnectorNameTextBlock", this); Canvas.SetLeft(textBlock, relativeMid.X - textBlock.ActualWidth/2); Canvas.SetTop(textBlock, relativeMid.Y - textBlock.ActualHeight/2); }
方案3:纯XAML实现(无后台代码)
如果需要符合MVVM规范避免后台逻辑,可以自定义IValueConverter,传入Path的Data参数,转换器内计算中点坐标后返回TranslateTransform,直接绑定到TextBlock的RenderTransform属性即可,无需手动调用定位方法。
内容的提问来源于stack exchange,提问作者ChrisOBrien
相关产品推荐
相关产品推荐

