wpf在图上画矩形,矩形可拖动、大小可调节,使用装饰器Adorner调整矩形大小,限制拖动和调节范围

效果

在这里插入图片描述

功能

使用wpf实现

  1. 在图片上画一个矩形框
  2. 该矩形框可以调节大小
  3. 该矩形框可以拖动调整位置

注:这里的鼠标事件是,双击在图上画一个固定大小的矩形框,右键按住拖动矩形框。有需要的可以自行调整对应的鼠标事件
参考资料:https://blog.csdn.net/u013113678/article/details/121466724

实现代码

实现自定义的装饰器(可以直接整个复制使用)

public class CanvasAdorner : Adorner
{//4条边Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;//4个角Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;Ellipse _centerPoint;private const double thumbSize = 6;private const double centerPointRadius = 3;Grid _grid;UIElement _adornedElement;UIElement _parentElement;public CanvasAdorner(UIElement adornedElement, UIElement adornedParentElement) : base(adornedElement){_adornedElement = adornedElement;_parentElement = adornedParentElement;// 中心点_centerPoint = new Ellipse{Width = centerPointRadius * 2,Height = centerPointRadius * 2,Fill = Brushes.Red,Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")),StrokeThickness = 2,HorizontalAlignment = HorizontalAlignment.Center,VerticalAlignment = VerticalAlignment.Center,};//初始化thumb缩放手柄_leftThumb = CreateThumb(HorizontalAlignment.Left, VerticalAlignment.Center, Cursors.SizeWE);_topThumb = CreateThumb(HorizontalAlignment.Center, VerticalAlignment.Top, Cursors.SizeNS);_rightThumb = CreateThumb(HorizontalAlignment.Right, VerticalAlignment.Center, Cursors.SizeWE);_bottomThumb = CreateThumb(HorizontalAlignment.Center, VerticalAlignment.Bottom, Cursors.SizeNS);_lefTopThumb = CreateThumb(HorizontalAlignment.Left, VerticalAlignment.Top, Cursors.SizeNWSE);_rightTopThumb = CreateThumb(HorizontalAlignment.Right, VerticalAlignment.Top, Cursors.SizeNESW);_rightBottomThumb = CreateThumb(HorizontalAlignment.Right, VerticalAlignment.Bottom, Cursors.SizeNWSE);_leftbottomThumb = CreateThumb(HorizontalAlignment.Left, VerticalAlignment.Bottom, Cursors.SizeNESW);_grid = new Grid();_grid.Children.Add(_leftThumb);_grid.Children.Add(_topThumb);_grid.Children.Add(_rightThumb);_grid.Children.Add(_bottomThumb);_grid.Children.Add(_lefTopThumb);_grid.Children.Add(_rightTopThumb);_grid.Children.Add(_rightBottomThumb);_grid.Children.Add(_leftbottomThumb);AddVisualChild(_grid);// 绘制中心点和x,y坐标轴_grid.Children.Add(_centerPoint);DrawAxisWithArrow(0,15,0,0,isXAxis: true);DrawAxisWithArrow(0, 0, 10, 25, isXAxis: false);}protected override Visual GetVisualChild(int index){return _grid;}protected override int VisualChildrenCount{get{return 1;}}protected override Size ArrangeOverride(Size finalSize){//直接给grid布局,grid内部的thumb会自动布局。_grid.Arrange(new Rect(new Point(-_leftThumb.Width / 2, -_leftThumb.Height / 2), new Size(finalSize.Width + _leftThumb.Width, finalSize.Height + _leftThumb.Height)));return finalSize;}// 创建缩放手柄private Thumb CreateThumb(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, Cursor cursor){var thumb = new Thumb {Width = thumbSize,Height = thumbSize,HorizontalAlignment = horizontalAlignment,VerticalAlignment = verticalAlignment,Background = Brushes.Green,Cursor = cursor,Template = new ControlTemplate(typeof(Thumb)){VisualTree = GetFactory(new SolidColorBrush(Colors.White))},};thumb.DragDelta += Thumb_DragDelta;return thumb;}// 缩放手柄resize逻辑private void Thumb_DragDelta(object sender, DragDeltaEventArgs e){var c = _adornedElement as FrameworkElement;var p = _parentElement as FrameworkElement;var thumb = sender as FrameworkElement;double left, top, width, height;if (thumb.HorizontalAlignment == HorizontalAlignment.Left){left = double.IsNaN(Canvas.GetLeft(c)) ? 0 : Canvas.GetLeft(c) + e.HorizontalChange;width = c.Width - e.HorizontalChange;// 确保不会超出 Canvas 左边界if (left < 0){width += left; // 减少宽度以适应左侧边界left = 0; // 不能再向左移动}}else{left = Canvas.GetLeft(c);width = c.Width + e.HorizontalChange;// 确保不会超出 Canvas 右边界if (left + width > p.Width){width = p.Width - left; // 减少宽度以适应右侧边界}}if (thumb.VerticalAlignment == VerticalAlignment.Top){top = double.IsNaN(Canvas.GetTop(c)) ? 0 : Canvas.GetTop(c) + e.VerticalChange;height = c.Height - e.VerticalChange;// 确保不会超出 Canvas 上边界if (top < 0){height += top; // 减少高度以适应上侧边界top = 0; // 不能再向上移动}}else{top = Canvas.GetTop(c);height = c.Height + e.VerticalChange;// 确保不会超出 Canvas 下边界if (top + height > p.Height){height = p.Height - top; // 减少高度以适应下侧边界}}if (thumb.HorizontalAlignment != HorizontalAlignment.Center){if (width >= 0){Canvas.SetLeft(c, left);c.Width = width;}}if (thumb.VerticalAlignment != VerticalAlignment.Center){if (height >= 0){Canvas.SetTop(c, top);c.Height = height;}}}private void DrawAxisWithArrow(int x1, int x2, int y1, int y2, bool isXAxis){// 绘制主轴线Line axisLine = new Line{X1 = x1,Y1 = y1,X2 = x2,Y2 = y2,Stroke = Brushes.GreenYellow,StrokeThickness = 1,HorizontalAlignment = HorizontalAlignment.Center,VerticalAlignment = VerticalAlignment.Center,Margin = new Thickness { Left = x2, Top = 0, Right = 0, Bottom = 0 }};_grid.Children.Add(axisLine);// 绘制箭头TextBlock textBlock = new TextBlock { Text=isXAxis?"> x": "∨y",Foreground = Brushes.GreenYellow,HorizontalAlignment= HorizontalAlignment.Center,VerticalAlignment= VerticalAlignment.Center,Margin= new Thickness { Left= isXAxis ? x2 * 2:5, Top=y2, Right=0, Bottom= isXAxis ? 2.5:0 }};_grid.Children.Add(textBlock);}//thumb的样式FrameworkElementFactory GetFactory(Brush back){var fef = new FrameworkElementFactory(typeof(Ellipse));fef.SetValue(Ellipse.FillProperty, back);fef.SetValue(Ellipse.StrokeProperty, new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")));fef.SetValue(Ellipse.StrokeThicknessProperty, (double)2);return fef;}
}

xaml前台代码(根据自己实际情况调整图片的Source和Canvas的Width、Height,这里我的图片是绑定viewmodel的值,Canvas的宽高是始终跟图片大小一致)

<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"><Image x:Name="imageView" Stretch="Uniform" Source="{Binding LoadTemplate}"MouseLeftButtonDown="Image_MouseLeftButtonDown"/><Canvas x:Name="overlayCanvas"Width="{Binding ActualWidth, ElementName=imageView}" Height="{Binding ActualHeight, ElementName=imageView}"/>
</Grid>

xaml后台代码(截取相关代码)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Documents;
using System.Windows.Input;private Rectangle _currentRectangle;
private bool _isDragging = false;
private Point _startPoint;
private Point _originalRectanglePosition;
private DateTime _lastClickTime = DateTime.MinValue;// 图片点击事件
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{// 双击DateTime currentClickTime = DateTime.Now;TimeSpan timeSinceLastClick = currentClickTime - _lastClickTime;_lastClickTime = currentClickTime;if (timeSinceLastClick.TotalMilliseconds >= 300) return;if (_currentRectangle != null){ResetCanvas();}Point clickPosition = e.GetPosition(overlayCanvas);double rectWidth = 100;double rectHeight = 100;double rectLeft = clickPosition.X - rectWidth / 2;double rectTop = clickPosition.Y - rectHeight / 2;// 确保矩形框不会超出 Canvas 的左边界if (rectLeft < 0){rectLeft = 0;}// 确保矩形框不会超出 Canvas 的右边界if (rectLeft + rectWidth > overlayCanvas.Width){rectLeft = overlayCanvas.Width - rectWidth;}// 确保矩形框不会超出 Canvas 的上边界if (rectTop < 0){rectTop = 0;}// 确保矩形框不会超出 Canvas 的下边界if (rectTop + rectHeight > overlayCanvas.Height){rectTop = overlayCanvas.Height - rectHeight;}_currentRectangle = new Rectangle{Width = rectWidth,Height = rectHeight,Stroke = Brushes.Red,Fill = Brushes.Transparent,StrokeThickness = 1};Canvas.SetLeft(_currentRectangle, rectLeft);Canvas.SetTop(_currentRectangle, rectTop);overlayCanvas.Children.Add(_currentRectangle);// 为矩形添加resize装饰器var layer = AdornerLayer.GetAdornerLayer(_currentRectangle);layer.Add(new CanvasAdorner(_currentRectangle, overlayCanvas));// 为矩形添加拖动事件_currentRectangle.MouseRightButtonDown += Rectangle_MouseLeftButtonDown;_currentRectangle.MouseMove += Rectangle_MouseMove;_currentRectangle.MouseRightButtonUp += Rectangle_MouseLeftButtonUp;_currentRectangle.MouseEnter += Rectangle_MouseEnter;
}private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{if (e.RightButton == MouseButtonState.Pressed){_isDragging = true;_startPoint = e.GetPosition(overlayCanvas);_originalRectanglePosition = new Point(Canvas.GetLeft(_currentRectangle), Canvas.GetTop(_currentRectangle));_currentRectangle.CaptureMouse();}
}
private void Rectangle_MouseMove(object sender, MouseEventArgs e)
{if (_isDragging){Point currentPosition = e.GetPosition(overlayCanvas);// 计算矩形的新位置double newLeft = _originalRectanglePosition.X+ (currentPosition.X - _startPoint.X);double newTop = _originalRectanglePosition.Y + (currentPosition.Y - _startPoint.Y);// 限制矩形不超出 Canvas 边界newLeft = Math.Max(0, Math.Min(newLeft, overlayCanvas.Width - _currentRectangle.Width));newTop = Math.Max(0, Math.Min(newTop, overlayCanvas.Height - _currentRectangle.Height));// 更新矩形的位置Canvas.SetLeft(_currentRectangle, newLeft);Canvas.SetTop(_currentRectangle, newTop);}
}
private void Rectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{_isDragging = false;_currentRectangle.ReleaseMouseCapture();
}
private void Rectangle_MouseEnter(object sender, MouseEventArgs e)
{_currentRectangle.Cursor = Cursors.SizeAll;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/431668.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vant van-pull-refresh + van-list实现list列表支持搜索和下拉刷新

1 介绍 在使用 van-pull-refresh van-list实现list列表下拉刷新时遇到几个问题在这里进行一个总结。 2 出现的问题 问题一&#xff1a;当van-pull-refresh van-list组合使用时&#xff0c;下拉刷新会调用两个加载图标。 解答&#xff1a;去除van-pull-refresh加载图标&…

刷题小记3----每日一题精进Java技能(详细思路解析✅)

文章目录 一、两种排序方法二、最小公倍数三、另类加法四、倒置字符串五、统计回文 一、两种排序方法 题目链接&#xff1a;两种排序方法 题目描述&#xff1a; 考拉有n个字符串字符串&#xff0c;任意两个字符串长度都是不同的。考拉最近学习到有两种字符串的排序方法&#x…

Web端云剪辑解决方案,提供前端产品源码

美摄科技作为业界领先的视频技术服务商&#xff0c;匠心打造Web端云剪辑解决方案&#xff0c;以前沿技术赋能企业用户&#xff0c;开启视频创作与编辑的新纪元。 【云端赋能&#xff0c;重塑剪辑体验】 美摄科技的Web端云剪辑解决方案&#xff0c;颠覆了传统视频编辑的局限&a…

zabbix“专家坐诊”第257期问答

问题一 Q&#xff1a;zabbix5.0监控项里的键值&#xff0c;怎么设置变量值&#xff1f;{#ABC} {$ABC} 都识别不到变量。 A&#xff1a;可以参考一下这个。 问题二 Q&#xff1a;我想问一下用odbc创建监控项&#xff0c;生成了json格式&#xff0c;如何创建一个触发器去判断里面…

人工智能武器化与国家网络威慑机制选择

文章目录 前言一、人工智能武器化与国家网络威慑机制选择1、人工智能时代国家推动网络威慑的逻辑二、迈向攻防平衡期的网络威慑机制选择三、攻防平衡状态下的网络威慑机制选择前言 威慑理论是国家应对战争威胁的重要思想,同时也是一种严格的信号传递机制。自21世纪初期“网络…

方法部分 学习

方法是程序中最小的执行单元 方法的定义调用 public static void 方法名&#xff08;&#xff09;{ 方法体 } 写在main方法外面&#xff0c;在main函数里面直接调用带参数&#xff1a;public static void 方法名&#xff08;int num1 &#xff0c; int num2&am…

成都睿明智科技有限公司电商服务引领品牌跃升

在当今这个数字化浪潮汹涌的时代&#xff0c;抖音电商以其独特的魅力迅速崛起&#xff0c;成为众多品牌商家竞相追逐的新战场。在这片充满机遇与挑战的领域中&#xff0c;成都睿明智科技有限公司以其专业的抖音电商服务&#xff0c;成为了众多商家信赖的伙伴。今天&#xff0c;…

在虚幻引擎中创建毛发/头发

在虚幻引擎中创建毛发/头发 , 首先开启两个插件 Groom 和 Alembic Groom Importer 打开蒙皮缓存 导出人物模型 将人物导入Blender , 选择需要种植头发的点 指定并选择 点击毛发 这里变成爆炸头了 , 把数量和长度调一下 切换到梳子模式 调整发型 导出为abc , 文件路径不…

针对 Linux SSH 服务器的新攻击:Supershell 恶意软件危害易受攻击的系统

ASEC 研究人员发现了针对保护不善的 Linux SSH 服务器的新攻击。 在其中&#xff0c;黑客使用了用Go编写的 Supershell恶意软件。 该后门使攻击者能够远程控制受感染的系统。 初次感染后&#xff0c;黑客启动扫描仪来寻找其他易受攻击的目标。 据信这些攻击是使用从已受感…

kubernetes K8S 挂载分布式存储 ceph

目录 一、Ceph简介 二、Ceph核心组件介绍 三、安装Ceph集群 1初始化实验环境 1.1、配置静态IP&#xff1a; 1.2、配置主机名&#xff1a; 1.3、配置hosts文件&#xff1a; 1.4、配置互信 1.5、关闭防火墙 1.6、关闭selinux 1.7、配置Ceph安装源 1.8、配置时间同步 …

【自学笔记】支持向量机(4)——支持向量回归SVR

引入 SVM解决了分类问题&#xff0c;而用类似方法解决回归问题的模型称为支持向量回归。目标是得到一个模型&#xff0c;使输出的 f ( x ⃗ ) f(\vec{x}) f(x )与 y y y尽可能接近。 传统的回归模型直接计算 f ( x ⃗ ) f(\vec{x}) f(x )与 y y y的差距作为损失&#xff0c;当两…

Linux驱动开发(速记版)--驱动基础

第一章 初识内核源码 Linux系统源码提供了操作系统的核心功能&#xff0c;如进程管理、内存管理、文件系统等。 BusyBox这类的文件系统构建工具&#xff0c;则提供了在这些核心功能之上运行的一系列实用工具和命令&#xff0c;使得用户能够执行常见的文件操作、文本处理、网络配…

爬虫逆向学习(八):Canvas画图滑块验证码解决思路与绕过骚操作

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 逆向站点 aHR0cHM6Ly93d3cuYm9odWF5aWNhaS5jbi8/VTU4Iy9jaGVtaWNhbC9sb2dpbj9yZWRpcmVjdD0lMkZjaGVtaWNhbA 滑块验证码样式 滑块验证码研究 一般的滑块验证码都是会直接提供滑块和…

Diffusion Model Stable Diffusion(笔记)

参考资料&#xff1a; 文章目录 DDPM架构模型如何拥有产生逼真图片的能力Denoise模型功能Denoise模型如何训练考虑进文字 文生图流程(Stable Diffusion) DDPM架构 模型如何拥有产生逼真图片的能力 Denoise模型功能 通过Denoise将一个噪音图一步步生成为目标图像 Denoise实际…

【开源免费】基于SpringBoot+Vue.JS墙绘产品展示交易平台(JAVA毕业设计)

本文项目编号 T 049 &#xff0c;文末自助获取源码 \color{red}{T049&#xff0c;文末自助获取源码} T049&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

echarts根据容器宽度动态截取展示横坐标名称

效果如下&#xff1a; 初始状态&#xff1a; 缩放页面后&#xff1a; 代码地址&#xff1a;代码地址-面包多

Oracle 19c 使用EMCC 监控当前所有数据库

一.EMCC简介 EMCC&#xff0c;全称Oracle Enterprise Manager Cloud Control&#xff0c;是Oracle提供的一套集中化监控工具&#xff0c;可以对数据库、操作系统、中间件等进行监控&#xff0c;通过OMS&#xff08;Oracle Management Service&#xff09;收集监控数据并将监控信…

赛氪作媒体支持单位受邀参加首届科普翻译与跨学科专业学术研讨会

2024年9月22日&#xff0c;正值全国科普日之际&#xff0c;首届科普翻译与跨学科专业学术研讨会在上海健康与营养研究所信息中心励志厅成功举行并圆满结束。此次研讨会汇聚了来自全国各地的近60名专家学者、学界及企业界代表&#xff0c;共同探讨科普翻译与跨学科专业的发展。作…

OpenAI首款芯片曝光 专为文生视频Sora打造

近日&#xff0c;OpenAI首款芯片的消息终于曝光。据了解OpenAI已经预定了台积电的A16工艺制程&#xff0c;用于制造AI芯片&#xff0c;专为Sora视频应用打造&#xff0c;旨在提升Sore的视频生成能力。 年初曾有报道称&#xff0c;OpenAI CEO奥特曼打算募集7万亿美元和台积电合…

新160个crackme - 064-CR-Game0.7

运行分析 需破解Name、Company、Serial PE分析 32位程序&#xff0c;EP Section为.text&#xff0c;猜测无壳 静态分析&动态调试 ida搜索关键字符串&#xff0c;双击进入函数 level 0 动调sub_401403函数&#xff0c;注释如上Serial输入JPL-168-39&#xff0c;level 0通过 …