C# wpf Canvas中实现控件拖动调整大小

2024-08-29 05:48

本文主要是介绍C# wpf Canvas中实现控件拖动调整大小,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

WPF拖动改变大小系列

第一节 Grid内控件拖动调整大小
第二节 Canvas内控件拖动调整大小(本章)
第三节 窗口拖动调整大小
第四节 附加属性实现拖动调整大小
第五章 拓展更多调整大小功能


文章目录

  • WPF拖动改变大小系列
  • 前言
  • 一、功能说明
  • 二、如何实现?
    • 1.继承Adorner
    • 2.使用Thumb
    • 3.实现拖动逻辑
  • 三、完整代码
  • 四、使用示例
  • 总结


前言

我们做图片编辑工具、视频编辑工具、或者画板有时需要实现控件缩放功能,比如图片或图形可以拉伸放大或缩小,实现这种功能通常需要8个点,对应4条边和4个角,在wpf中通常可以使用装饰器实现。


一、功能说明

8个点方放置在控件的8个方位上,通过拖动这些点对控件进行拉伸或缩小,示意图如下:
在这里插入图片描述


二、如何实现?

1.继承Adorner

通过装饰器的方式添加8个点在控件上,这样既可以不影响控件布局,又可以自由摆放8点控件。通过重写方法,给装饰添加控件。必要的重写的方法如下面示例所示:

public class CanvasAdorner : Adorner
{//获取装饰器的元素个数protected override Visual GetVisualChild(int index);//指定装饰器子元素个数protected override int VisualChildrenCount{get;}//布局,添加的子元素需要手动布局。protected override Size ArrangeOverride(Size finalSize);      
}

2.使用Thumb

因为Thumb实现拖动比较容易,有相关事件获取拖动距离。在装饰器中定义8个Thumb,对应8个方位点。
示例代码如下:

//4条边
Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;
//4个角
Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;

初始化

 public CanvasAdorner(UIElement adornedElement) : base(adornedElement){//初始化thumb_leftThumb = new Thumb();_leftThumb.HorizontalAlignment = HorizontalAlignment.Left;_leftThumb.VerticalAlignment = VerticalAlignment.Center;_leftThumb.Cursor = Cursors.SizeWE;//其他略...
}

3.实现拖动逻辑

在Thumb的DragDelta事件可以获取拖动距离,根据八个方位的不同计算并修改控件的大小。

private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
//1.右侧点HorizontalChange加宽
//2.左侧点HorizontalChange减宽,加左移
//3.下侧点VerticalChange加高
//4.上侧点VerticalChange减高,加上移
}

三、完整代码

代码如下:

   public class CanvasAdorner : Adorner{//4条边Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;//4个角Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;//布局容器,如果不使用布局容器,则需要给上述8个控件布局,实现和Grid布局定位是一样的,会比较繁琐且意义不大。Grid _grid;UIElement _adornedElement;public CanvasAdorner(UIElement adornedElement) : base(adornedElement){_adornedElement = adornedElement;//初始化thumb_leftThumb = new Thumb();_leftThumb.HorizontalAlignment = HorizontalAlignment.Left;_leftThumb.VerticalAlignment = VerticalAlignment.Center;_leftThumb.Cursor = Cursors.SizeWE;_topThumb = new Thumb();_topThumb.HorizontalAlignment = HorizontalAlignment.Center;_topThumb.VerticalAlignment = VerticalAlignment.Top;_topThumb.Cursor = Cursors.SizeNS;_rightThumb = new Thumb();_rightThumb.HorizontalAlignment = HorizontalAlignment.Right;_rightThumb.VerticalAlignment = VerticalAlignment.Center;_rightThumb.Cursor = Cursors.SizeWE;_bottomThumb = new Thumb();_bottomThumb.HorizontalAlignment = HorizontalAlignment.Center;_bottomThumb.VerticalAlignment = VerticalAlignment.Bottom;_bottomThumb.Cursor = Cursors.SizeNS;_lefTopThumb = new Thumb();_lefTopThumb.HorizontalAlignment = HorizontalAlignment.Left;_lefTopThumb.VerticalAlignment = VerticalAlignment.Top;_lefTopThumb.Cursor = Cursors.SizeNWSE;_rightTopThumb = new Thumb();_rightTopThumb.HorizontalAlignment = HorizontalAlignment.Right;_rightTopThumb.VerticalAlignment = VerticalAlignment.Top;_rightTopThumb.Cursor = Cursors.SizeNESW;_rightBottomThumb = new Thumb();_rightBottomThumb.HorizontalAlignment = HorizontalAlignment.Right;_rightBottomThumb.VerticalAlignment = VerticalAlignment.Bottom;_rightBottomThumb.Cursor = Cursors.SizeNWSE;_leftbottomThumb = new Thumb();_leftbottomThumb.HorizontalAlignment = HorizontalAlignment.Left;_leftbottomThumb.VerticalAlignment = VerticalAlignment.Bottom;_leftbottomThumb.Cursor = 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);foreach (Thumb thumb in _grid.Children){thumb.Width = 16;thumb.Height = 16;thumb.Background = Brushes.Green;thumb.Template = new ControlTemplate(typeof(Thumb)){VisualTree = GetFactory(new SolidColorBrush(Colors.White))};thumb.DragDelta += Thumb_DragDelta;}}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 void Thumb_DragDelta(object sender, DragDeltaEventArgs e){var c = _adornedElement 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;}else{left = Canvas.GetLeft(c);width = c.Width + e.HorizontalChange;}if (thumb.VerticalAlignment == VerticalAlignment.Top){top = double.IsNaN(Canvas.GetTop(c)) ? 0 : Canvas.GetTop(c) + e.VerticalChange;height = c.Height - e.VerticalChange;}else{top = Canvas.GetTop(c);height = c.Height + e.VerticalChange;}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;}}}//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

<Canvas Margin="20"><Border  x:Name="border"  Width="200" Height="200"  Background="Gray"  ></Border>
</Canvas>

在窗口或控件的Loaded事件中添加装饰器:
cs

private void window_Loaded(object sender, RoutedEventArgs e)
{var layer = AdornerLayer.GetAdornerLayer(border);layer.Add(new CanvasAdorner(border));
}

效果预览:
在这里插入图片描述


总结

以上就是今天要讲的内容,本文讲述了控件缩放的方法与实现,都相对较简单,尤其是实现也是特意精简了代码,比如布局直接使用Grid控件,拖动的逻辑也是较为简化,最终呈现的效果还是比较不错的。

这篇关于C# wpf Canvas中实现控件拖动调整大小的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Python实现PDF按页分割的技术指南

《Python实现PDF按页分割的技术指南》PDF文件处理是日常工作中的常见需求,特别是当我们需要将大型PDF文档拆分为多个部分时,下面我们就来看看如何使用Python创建一个灵活的PDF分割工具吧... 目录需求分析技术方案工具选择安装依赖完整代码实现使用说明基本用法示例命令输出示例技术亮点实际应用场景扩

C#监听txt文档获取新数据方式

《C#监听txt文档获取新数据方式》文章介绍通过监听txt文件获取最新数据,并实现开机自启动、禁用窗口关闭按钮、阻止Ctrl+C中断及防止程序退出等功能,代码整合于主函数中,供参考学习... 目录前言一、监听txt文档增加数据二、其他功能1. 设置开机自启动2. 禁止控制台窗口关闭按钮3. 阻止Ctrl +

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

如何在Java Spring实现异步执行(详细篇)

《如何在JavaSpring实现异步执行(详细篇)》Spring框架通过@Async、Executor等实现异步执行,提升系统性能与响应速度,支持自定义线程池管理并发,本文给大家介绍如何在Sprin... 目录前言1. 使用 @Async 实现异步执行1.1 启用异步执行支持1.2 创建异步方法1.3 调用

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分