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

如何让Adorner上的Rectangle随绑定Image窗口缩放同步适配?

解决WPF Adorner中Rectangle随Image同步缩放的问题

嘿,这个问题我刚入门WPF的时候也踩过坑!你现在的问题核心很明确:你用绝对像素值定义了Adorner里的Rectangle大小和位置,但Image在窗口resize时是通过Stretch="Uniform"自适应缩放的,Adorner根本不知道Image的缩放比例,自然没法跟着同步调整。你想到的「归一化坐标」完全是正确的思路,咱们直接把这个思路落地成代码:

核心思路

放弃用绝对像素存储ROI区域,改成存储相对于原始图片尺寸的比例值。每次Adorner渲染时,根据当前Image的实际显示尺寸,把比例值转换成对应的绝对Rect,这样不管Image怎么缩放,ROI都会精准覆盖图片上的同一区域。

修改后的完整代码

1. 重构RoiAdorner类

class RoiAdorner : Adorner
{
    // 存储相对比例:相对于原始图片宽高的百分比
    private readonly double _relativeX;
    private readonly double _relativeY;
    private readonly double _relativeWidth;
    private readonly double _relativeHeight;

    public RoiAdorner(UIElement adornedElement) : base(adornedElement)
    {
        var image = adornedElement as Image;
        if (image?.Source == null) return;

        // 获取原始图片的真实尺寸(不是显示尺寸)
        double originalWidth = 0;
        double originalHeight = 0;
        if (image.Source is BitmapImage bitmap)
        {
            originalWidth = bitmap.PixelWidth;
            originalHeight = bitmap.PixelHeight;
        }
        // 兼容其他图片源类型(比如BitmapFrame)
        else if (image.Source is BitmapFrame frame)
        {
            originalWidth = frame.PixelWidth;
            originalHeight = frame.PixelHeight;
        }

        // 这里替换成你最初想要的ROI绝对区域(相对于原始图片)
        double roiX = 0;
        double roiY = 0;
        double roiWidth = 100;
        double roiHeight = 30;

        // 计算相对比例
        _relativeX = roiX / originalWidth;
        _relativeY = roiY / originalHeight;
        _relativeWidth = roiWidth / originalWidth;
        _relativeHeight = roiHeight / originalHeight;

        IsHitTestVisible = false;
        // 监听Image尺寸变化,自动触发重绘
        adornedElement.SizeChanged += (_, __) => InvalidateVisual();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        var pen = new Pen(Brushes.Green, 5);
        
        // 获取Image当前的实际显示尺寸
        var currentRenderSize = AdornedElement.RenderSize;
        // 把相对比例转换成当前的绝对Rect
        var currentRoiRect = new Rect(
            _relativeX * currentRenderSize.Width,
            _relativeY * currentRenderSize.Height,
            _relativeWidth * currentRenderSize.Width,
            _relativeHeight * currentRenderSize.Height
        );

        drawingContext.DrawRectangle(null, pen, currentRoiRect);
    }
}

2. 调整MainWindow代码(确保图片加载完成再创建Adorner)

如果你的图片是异步加载的(比如本地文件或网络图片),直接在构造函数里创建Adorner可能拿不到原始图片尺寸,所以我们把Adorner的创建放到Image的Loaded事件里:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        // 监听Image加载完成事件
        Btn.Loaded += OnImageLoaded;
    }

    private void OnImageLoaded(object sender, RoutedEventArgs e)
    {
        var adornerLayer = AdornerLayer.GetAdornerLayer(Btn);
        // 避免重复添加
        if (adornerLayer.GetAdorners(Btn)?.Length == 0)
        {
            adornerLayer.Add(new RoiAdorner(Btn));
        }
    }
}

3. XAML代码无需修改

你的原有XAML代码保持不变即可:

<Window x:Class="TestingAdorners.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestingAdorners"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid ClipToBounds="True">
        <AdornerDecorator>
            <Image Name="Btn" Source="nyan.png" Stretch="Uniform"/>
        </AdornerDecorator>
    </Grid>
</Window>

关键细节说明

  • 为什么要监听SizeChanged?因为窗口resize时,Image的RenderSize会变化,我们需要触发InvalidateVisual()让Adorner重新渲染,计算新的Rect。
  • 原始图片尺寸的获取:要区分PixelWidth/PixelHeight(图片本身的真实尺寸)和RenderSize(控件显示的尺寸),我们的比例是基于真实尺寸计算的,这样才能保证ROI始终对应图片上的同一区域。
  • 如果需要支持用户拖拽调整ROI,只需要在调整后重新计算相对比例即可,逻辑是一样的。

这样修改后,不管你怎么缩放窗口,那个绿色矩形都会牢牢贴合在图片的目标区域上啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:00:28