C# 图片盖章功能实现,支持拖拽-旋转-放缩-保存
实现图片盖章功能,在图片上点击,增加“图章”小图片,可以拖拽“图章”到任意位置,也可以点击图章右下角园框,令图片跟着鼠标旋转和放缩。
操作方法:1.点击增加“图章”2.选中移动图标3.点中右下角放缩旋转图章。
实现代码如下:
1. 窗口Xaml代码
<Window x:Class="Lenovo.YogaPaster.ImageEditWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="ImageEditWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="20*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Canvas x:Name="canvas" Background="Yellow" /> <Button Content="保存成图片" Click="Button_Click_1" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </Window>
2. 窗口后台代码:
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using Lenovo.YP.Utils; using Microsoft.Win32; using Point = System.Windows.Point; namespace Lenovo.YogaPaster { /// <summary> /// ImageEditWindow.xaml 的交互逻辑 /// </summary> public partial class ImageEditWindow : Window { public ImageEditWindow() { InitializeComponent(); this.Loaded += (sender, e) => { canvas.MouseLeftButtonDown += canvas_MouseLeftButtonDown; }; } private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { PhotoEditHelper.CommomMaxZIndex++; var image = new MoveImage { ContainerCanvas = canvas }; Point point = e.GetPosition(canvas); image.CreateImage(point); } /// <summary> /// 保存方法 /// </summary> public void SavePicture() { //TODO: openFile对话框 SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.FileName = "图片"; // Default file name saveFileDialog.DefaultExt = ".bmp"; // Default file extension saveFileDialog.Filter = "图片文件 (.bmp)|*.bmp"; // Filter files by extension // Show save file dialog box Nullable<bool> result = saveFileDialog.ShowDialog(); // Process save file dialog box results if (result == true) { // Save document string filename = saveFileDialog.FileName; if (File.Exists(filename)) { File.Delete(filename); } BitmapSource bitmapSource = PictureMergeHelper.CreateNotRanderElementScreenshot(canvas, 1800, 1080); BitmapEncoder bitmapEncoder = new BmpBitmapEncoder(); bitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapSource)); FileStream fileStream = new FileStream(filename, FileMode.Create); bitmapEncoder.Save(fileStream); fileStream.Dispose(); } } private void Button_Click_1(object sender, RoutedEventArgs e) { SavePicture(); } } }
3. MoveImage类文件如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; namespace Lenovo.YP.Utils { /// <summary> /// 添加一个image控件,并实现拖动效果 /// </summary> public class MoveImage { #region 字段 private int m_ZIndex; /// <summary> /// 当前图章的位置 /// </summary> /// /// <summary> /// 旋转图标的起始位置 /// </summary> private Point m_ImageRoundStartOffset; /// <summary> /// 旋转图标的当前位置 /// </summary> private Point m_ImageRoundOffset; /// <summary> /// 标识图章是否被拖拽 /// </summary> private bool m_IsDragging; /// <summary> /// 标识旋转图标是否被是否被拖拽 /// </summary> private bool m_IsimageRoundDragging; /// <summary> /// 图章的父容器 /// </summary> public Canvas ContainerCanvas { get; set; } /// <summary> /// 图章图片 /// </summary> private Image m_FlogImage; /// <summary> /// 旋转按钮图片 /// </summary> private Image m_RotateImage; /// <summary> /// 图章和旋转图片的容器 /// </summary> private Grid m_MoveImageGrid; /// <summary> /// 选转 /// </summary> private RotateTransform m_ImageRotate; /// <summary> /// 缩放 /// </summary> private ScaleTransform m_ImageScale; /// <summary> /// 上次位置 /// </summary> private Point m_OldPos; #endregion public MoveImage() { } /// <summary> /// 创建一个图标 /// </summary> /// <param name="position">图标的位置</param> public void CreateImage(Point position) { #region 设置布局 m_FlogImage = new Image { //Width = 100, //Height = 100, Source = new BitmapImage(new Uri("/Images/shuzi.jpg", UriKind.RelativeOrAbsolute)) }; m_RotateImage = new Image { Width = 20, Height = 20, Source = new BitmapImage(new Uri("/Images/round.jpg", UriKind.RelativeOrAbsolute)) }; m_RotateImage.HorizontalAlignment = HorizontalAlignment.Right; m_RotateImage.VerticalAlignment = VerticalAlignment.Bottom; m_RotateImage.Visibility = Visibility.Hidden; m_MoveImageGrid = new Grid(); m_MoveImageGrid.Width = 110; m_MoveImageGrid.Height = 110; m_MoveImageGrid.Children.Add(m_FlogImage); m_MoveImageGrid.Children.Add(m_RotateImage); m_MoveImageGrid.MouseEnter += RootGridMouseEnter; m_MoveImageGrid.MouseLeave += RootGridMouseLeave; m_RotateImage.MouseLeftButtonDown += imageRound_MouseLeftButtonDown; m_RotateImage.MouseMove += imageRound_MouseMove; m_RotateImage.MouseLeftButtonUp += imageRound_MouseLeftButtonUp; // Image in den Vordergrund bringen Canvas.SetZIndex(m_MoveImageGrid, m_ZIndex++); #endregion // Event Handler für das Image installieren m_MoveImageGrid.MouseLeftButtonDown += RootGridMouseLeftButtonDown; m_MoveImageGrid.MouseLeftButtonUp += RootGridMouseLeftButtonUp; m_MoveImageGrid.MouseMove += RootGridMouseMove; // Image leicht transparent machen m_MoveImageGrid.Opacity = 0.8; m_ImageRotate = new RotateTransform(0, 0, 0); m_ImageScale = new ScaleTransform(); //m_ImageScale.ScaleX = 1; //m_ImageScale.ScaleY = 1; TransformGroup transformGroup = new TransformGroup(); transformGroup.Children.Add(m_ImageRotate); transformGroup.Children.Add(m_ImageScale); m_MoveImageGrid.RenderTransform = transformGroup; //m_ImageScale111 = new ScaleTransform(); //m_ImageScale111.ScaleX = 0.5; //m_ImageScale111.ScaleY = 0.5; //imageRound.RenderTransform = m_ImageScale111; m_MoveImageGrid.RenderTransformOrigin = new Point(0.5, 0.5); // Image positionieren var pos = position; Canvas.SetLeft(m_MoveImageGrid, pos.X - (m_MoveImageGrid.Width / 2)); Canvas.SetTop(m_MoveImageGrid, pos.Y - (m_MoveImageGrid.Height / 2)); // Image zum Canvas hinzufügen ContainerCanvas.Children.Add(m_MoveImageGrid); } /// <summary> /// 控制旋转图标隐藏 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void RootGridMouseLeave(object sender, MouseEventArgs e) { m_RotateImage.Visibility = Visibility.Hidden; } /// <summary> /// 控制选择图标显示 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void RootGridMouseEnter(object sender, MouseEventArgs e) { m_RotateImage.Visibility = Visibility.Visible; } #region 控制盖章的移动 private void RootGridMouseMove(object sender, MouseEventArgs e) { if (m_IsDragging) { var pos = e.GetPosition(ContainerCanvas); Canvas.SetLeft(m_MoveImageGrid, Canvas.GetLeft(m_MoveImageGrid) + pos.X - m_OldPos.X); Canvas.SetTop(m_MoveImageGrid, Canvas.GetTop(m_MoveImageGrid) + pos.Y - m_OldPos.Y); m_OldPos = pos; } } private void RootGridMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var grid = sender as Grid; // Canvas.SetZIndex(image, m_ZIndex++); // Bild wieder leicht transparent machen grid.Opacity = 0.8; grid.ReleaseMouseCapture(); m_IsDragging = false; } private void RootGridMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var grid = sender as Grid; // auf "nicht"-transparent setzen grid.Opacity = 1; // Position des Pointers relativ zum Bild speichern m_OldPos = e.GetPosition(ContainerCanvas); // isDragging auf true setzen für MouseMove m_IsDragging = true; // Image in den Vordergrund bringen Canvas.SetZIndex(grid, PhotoEditHelper.CommomMaxZIndex++); // Den Pointer einfangen. Bei schnellen Bewegungen kann der // Pointer aus dem Image bewegt werden. Damit die Pointer- // Events weiterhin stattfinden, wird der Pointer eingefangen. grid.CaptureMouse(); // als behandelt markieren, damit nicht noch der // PointerPressed-Event Handler des Canvas aufgerufen wird. e.Handled = true; } #endregion #region 控制图章的旋转 private void imageRound_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var image = sender as Image; m_MoveImageGrid.Opacity = 1; image.ReleaseMouseCapture(); m_IsimageRoundDragging = false; } private void imageRound_MouseMove(object sender, MouseEventArgs e) { if (m_IsimageRoundDragging) { //鼠标当前点的坐标 m_ImageRoundOffset = e.GetPosition(ContainerCanvas); m_ImageScale.ScaleX = m_ImageScale.ScaleY = GetLength(currCenter, m_ImageRoundStartOffset, m_ImageRoundOffset); m_ImageRotate.Angle = GetAngle(currCenter, m_ImageRoundStartOffset, m_ImageRoundOffset) - 45; } } private Point currCenter; private void imageRound_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var image = sender as Image; m_MoveImageGrid.Opacity = 1; //中心点坐标 currCenter = new Point(Canvas.GetLeft(m_MoveImageGrid) + m_MoveImageGrid.Width / 2, Canvas.GetTop(m_MoveImageGrid) + m_MoveImageGrid.Height / 2); m_ImageRoundStartOffset = new Point(currCenter.X + m_MoveImageGrid.Width / 2, currCenter.Y); m_IsimageRoundDragging = true; Canvas.SetZIndex(m_MoveImageGrid, PhotoEditHelper.CommomMaxZIndex++); image.CaptureMouse(); e.Handled = true; } /// 根据余弦定理求两个线段夹角 /// </summary> /// <param name="origin">原点</param> /// <param name="start">start点</param> /// <param name="end">end点</param> /// <returns></returns> private double GetAngle(Point origin, Point start, Point end) { double cosfi = 0, fi = 0, norm = 0; double dsx = start.X - origin.X; double dsy = start.Y - origin.Y; double dex = end.X - origin.X; double dey = end.Y - origin.Y; cosfi = dsx * dex + dsy * dey; norm = (dsx * dsx + dsy * dsy) * (dex * dex + dey * dey); if (norm == 0) return 0; // origin和end坐标一样 cosfi /= Math.Sqrt(norm); if (cosfi >= 1.0) return 0; if (cosfi <= -1.0) return 180; fi = Math.Acos(cosfi); double angle = 180 * fi / Math.PI; if (dey > 0) { return angle; } return -angle; } private double GetLength(Point origin, Point start, Point end) { double dex = end.X - origin.X; double dey = end.Y - origin.Y; double dsx = start.X - origin.X; double dsy = start.Y - origin.Y; double startLength = Math.Sqrt(Math.Pow(dsx, 2) + Math.Pow(dsy, 2)); double endLength = Math.Sqrt(Math.Pow(dex, 2) + Math.Pow(dey, 2)); return endLength / startLength; } #endregion }
效果图:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构