You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让WPF Adorner仅在Canvas指定区域内绘制?

解决WPF中Adorner超出Canvas区域显示的问题

要让Adorner仅在Canvas范围内显示,需要从容器裁剪设置和Adorner自身绘制约束两方面入手,以下是具体实现方案:


1. 开启容器层面的裁剪

AdornerLayer默认不会继承Canvas的裁剪规则,即使将Canvas包裹在AdornerDecorator中,也需要显式开启裁剪属性:

修改你的XAML代码,给AdornerDecorator和Canvas都添加ClipToBounds="True":

<AdornerDecorator x:Name="DrawingAdorner" ClipToBounds="True">
    <Canvas x:Name="DrawingCanvas" Background="Transparent" ClipToBounds="True">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseLeftButtonDown">
                <prism:InvokeCommandAction Command="{Binding CanvasLButtonDownCmd}" />
            </i:EventTrigger>
            <i:EventTrigger EventName="MouseLeftButtonUp">
                <prism:InvokeCommandAction Command="{Binding CanvasLButtonUpCmd}" />
            </i:EventTrigger>
            <i:EventTrigger EventName="MouseMove">
                <prism:InvokeCommandAction Command="{Binding CanvasMouseMoveCmd}" />
            </i:EventTrigger>
            <i:EventTrigger EventName="SizeChanged">
                <prism:InvokeCommandAction Command="{Binding CanvasSizeChangedCmd}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Canvas>
</AdornerDecorator>

2. 自定义Adorner并约束绘制范围

仅靠容器裁剪可能无法完全满足需求(比如Adorner的逻辑位置仍可能超出Canvas),需要在自定义Adorner中主动限制绘制区域:

方案A:在绘制时裁剪内容

重写Adorner的OnRender方法,使用Canvas的边界作为裁剪范围:

public class CanvasRestrictedAdorner : Adorner
{
    public CanvasRestrictedAdorner(UIElement adornedElement) : base(adornedElement) { }

    protected override void OnRender(DrawingContext drawingContext)
    {
        var targetCanvas = AdornedElement as Canvas;
        if (targetCanvas == null)
        {
            base.OnRender(drawingContext);
            return;
        }

        // 创建Canvas边界的裁剪区域
        var canvasClip = new RectangleGeometry(new Rect(0, 0, targetCanvas.ActualWidth, targetCanvas.ActualHeight));
        drawingContext.PushClip(canvasClip);

        // 在这里绘制你的Adorner内容(示例:红色边框)
        var borderPen = new Pen(Brushes.Red, 2);
        drawingContext.DrawRectangle(null, borderPen, new Rect(AdornedElement.RenderSize));

        drawingContext.Pop();
    }
}

方案B:限制Adorner的变换位置

重写GetDesiredTransform方法,强制Adorner的位置不超出Canvas边界:

public class CanvasRestrictedAdorner : Adorner
{
    public CanvasRestrictedAdorner(UIElement adornedElement) : base(adornedElement) { }

    public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
    {
        var baseTransform = base.GetDesiredTransform(transform);
        var targetCanvas = AdornedElement as Canvas;
        if (targetCanvas == null)
            return baseTransform;

        // 计算Adorner相对于Canvas的边界
        var adornedBounds = AdornedElement.TransformToAncestor(targetCanvas)
                                          .TransformBounds(new Rect(AdornedElement.RenderSize));

        // 计算偏移量,确保Adorner不超出Canvas
        double offsetX = Math.Max(0, targetCanvas.ActualWidth - adornedBounds.Right);
        offsetX = Math.Min(offsetX, -adornedBounds.Left);
        double offsetY = Math.Max(0, targetCanvas.ActualHeight - adornedBounds.Bottom);
        offsetY = Math.Min(offsetY, -adornedBounds.Top);

        // 应用调整后的变换
        var transformGroup = new TransformGroup();
        transformGroup.Children.Add(baseTransform);
        transformGroup.Children.Add(new TranslateTransform(offsetX, offsetY));

        return transformGroup;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        // 正常绘制Adorner内容
        var borderPen = new Pen(Brushes.Red, 2);
        drawingContext.DrawRectangle(null, borderPen, new Rect(AdornedElement.RenderSize));
    }
}

3. 替换原有Adorner使用逻辑

在你的C#代码中,使用自定义的CanvasRestrictedAdorner替代原有Adorner:

if(newAdorner != null)
{
    // 注意:这里的newAdorner需要替换为CanvasRestrictedAdorner实例
    AdornerLayer.GetAdornerLayer(_drawingCanvas).Add(newAdorner);
    _adorners.Add(shapeItem.Uid, newAdorner);
}

内容的提问来源于stack exchange,提问作者mbh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:03:23