C# wpf 实现任意控件(包括窗口)更多调整大小功能
WPF拖动改变大小系列
第一节 Grid内控件拖动调整大小
第二节 Canvas内控件拖动调整大小
第三节 窗口拖动调整大小
第四节 附加属性实现拖动调整大小
第五章 拓展更多调整大小功能(本章)
文章目录
- WPF拖动改变大小系列
- 前言
- 一、添加的功能
- 1、任意控件DragResize
- 2、边界限制
- 3、交叉拖动
- 4、拖动点模板
- 5、拖动点容器模板
- 6、整体模板
- 7、窗口平滑拖动
- 8、拖动事件
- 9、其他功能
- (1)适应MinWidth、MinHeight
- (2)适应MaxWidth、MaxHeight
- (3)适配任意dpi
- 二、完整代码
- 三、使用示例
- 0、基础功能
- (1)、引用命名空间
- (2)、使用附加属性
- (3)、窗口注意事项
- 1、DragResize
- 2、边界限制
- 3、交叉拖动
- 4、拖动点布局模板
- (1)自定义圆点
- (2)4个顶点
- (3)单独定制每个点
- 5、拖动点容器模板
- (1)无Margin
- (2)设置Margin
- 6、整体模板
- 7、窗口平滑拖动
- 8、拖动事件
- 9、其他功能
- (1)适应MinWidth、MinHeight
- (2)适应MaxWidth、MaxHeight
- 总结
- 附录
- 1、依赖System.Drawing
前言
上一章我们已经实现了任意控件统一的拖动调整功能,能够方便的给任意控件设置拖动调整大小。开发过程中发现还是有些功能可以继续拓展的,比如cs代码触发拖动、自定义模板、交叉拖动、限制拖动范围等功能。有功能实现起来不算太容易,却很有实用价值。
一、添加的功能
在第四章基础上添加了如下功能。
1、任意控件DragResize
我们知道wpf的Window有DragMove功能,在鼠标左键按下事件中调用此方法就能实现拖动功能很方便。对于调整大小也可以实现类似的DragResize功能, 实际效果和点击画板拖出一个形状差不多。
代码示例如下:
/// <summary>/// 手动触发拖动改变大小,与Window.DragMove类似,只能在鼠标左键按下时调用。/// 实际效果和点击画板拖出一个形状差不多。/// 此方法为拓展方法,FrameworkElement的子类控件(即有宽高属性的控件)都可以调用此方法。/// </summary>/// <param name="elememt"></param>/// <returns>返回Task,await等待拖动完成</returns>/// <exception cref="InvalidOperationException"></exception>public static async Task DragResize(this FrameworkElement elememt){if (Mouse.LeftButton != MouseButtonState.Pressed){throw new InvalidOperationException("Left button down to call this method");}if (elememt.Parent == null && elememt is not Window){throw new InvalidOperationException("Element should be on the visual tree");}//生成Resizeable对象,第四章完整代码中。//获取右下角Thumb//手动触发Thumb拖动事件//等待拖动完成}
2、边界限制
添加一个IsResizeInBounds附加属性,表示拖动范围是否在父控件内。
代码示例如下:
public static bool GetIsResizeInBounds(DependencyObject obj)
{return (bool)obj.GetValue(IsResizeInBoundsProperty);
}public static void SetIsResizeInBounds(DependencyObject obj, bool value)
{obj.SetValue(IsResizeInBoundsProperty, value);
}/// <summary>
/// 是否在父控件范围内拖动
/// </summary>
public static readonly DependencyProperty IsResizeInBoundsProperty =DependencyProperty.RegisterAttached("IsResizeInBounds", typeof(bool), typeof(Resize), new PropertyMetadata(false));
第四章的拖动逻辑中添加相应的限制功能,本质上就是判断如果超出边界则控件刚好依附在边界上,代码如下:
var dx = left - margin.Left;var dy = top - margin.Top;if (GetIsResizeInBounds(c)){var pos = c.GetPosition();var parent = _resizeTarget.Parent as FrameworkElement;Size size;if (parent == null){size.Width = SystemParameters.PrimaryScreenWidth;size.Height = SystemParameters.PrimaryScreenHeight;}else{size.Width = parent.ActualWidth;size.Height = parent.ActualHeight;}if (pos.X + dx < 0){left = -pos.X + margin.Left;width = pos.X + c.ActualWidth;}else if (pos.X + dx + width > size.Width){width = size.Width - pos.X;right = margin.Right + c.ActualWidth - width;}if (pos.Y + dy < 0){top = -pos.Y + margin.Top;height = pos.Y + c.ActualHeight;}else if (pos.Y + dy + height > size.Height){height = size.Height - pos.Y;bottom = margin.Bottom + c.ActualHeight - height;}}
3、交叉拖动
交叉拖动是曾经用gdi画图时会出现的一种情况,gdi绘制的宽高可以为负数,所以可以直接穿过起点反向拖动也能绘制出图形。在wpf中的控件是不支持宽高负数的,所以我们需要用其他方式实现。
下列步骤以横向为例:
(1)判断控件边界
if (width < 0)
(2)固定到控件边界
SetTargetMargin为前3章的集合,根据不同控件类型比如Window是设置Left、Top、Grid则设置Margin等。minWidth是控件的MinWidth属性。margin参考第四张完整代码。
if (thumb.HorizontalAlignment == HorizontalAlignment.Left)
//左拖动点
{SetTargetMargin(new Thickness(margin.Left + c.Width - minWidth, margin.Top, margin.Right - c.Width + minWidth, margin.Bottom));
}
else
//右拖动点
{SetTargetMargin(new Thickness(margin.Left - c.Width + minWidth, margin.Top, margin.Right + c.Width - minWidth, margin.Bottom));
}
(3)事件转移
//当前拖动点触发鼠标弹起事件
MouseButtonEventArgs upEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left)
{ RoutedEvent = UIElement.MouseLeftButtonUpEvent };
thumb.RaiseEvent(upEvent);
//反向拖动点触发鼠标按下事件
MouseButtonEventArgs downEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left)
{ RoutedEvent = UIElement.MouseLeftButtonDownEvent };
t.RaiseEvent(downEvent);
4、拖动点模板
添加附加属性ThumbsTemplate
public static ControlTemplate GetThumbsTemplate(DependencyObject obj)
{return (ControlTemplate)obj.GetValue(ThumbsTemplateProperty);
}public static void SetThumbsTemplate(DependencyObject obj, ControlTemplate value)
{obj.SetValue(ThumbsTemplateProperty, value);
}/// <summary>
/// 拖动点的模板
/// </summary>
public static readonly DependencyProperty ThumbsTemplateProperty =DependencyProperty.RegisterAttached("ThumbsTemplate", typeof(ControlTemplate), typeof(Resize), new PropertyMetadata(null));
生成拖动点时会应用模板
var thumbsTemplate = GetThumbsTemplate(_resizeTarget);
thumb.Template = thumbsTemplate;
5、拖动点容器模板
拖动点的容器模板,主要用于设置margin调整拖动点的整体位置,添加附加属性ThumbsPanel。
public static ItemsPanelTemplate GetThumbsPanel(DependencyObject obj){return (ItemsPanelTemplate)obj.GetValue(ThumbsPanelProperty);}public static void SetThumbsPanel(DependencyObject obj, ItemsPanelTemplate value){obj.SetValue(ThumbsPanelProperty, value);}/// <summary>/// 拖动点的容器,主要用于设置margin/// </summary>public static readonly DependencyProperty ThumbsPanelProperty =DependencyProperty.RegisterAttached("ThumbsPanel", typeof(ItemsPanelTemplate), typeof(Resize), new PropertyMetadata(null));
生成拖动点布局时会应用模板
var itemsPanel = GetThumbsPanel(_resizeTarget);
_defalutPanel.ItemsPanel = itemsPanel;
6、整体模板
拖动点模板和拖动点布局模板已经很大程度灵活了使用,如果需要更高的定制性,直接使用整体模板,整体模板赋值后拖动点模板和拖动点布局模板会失效。此功能与第四章的ResizeTemplate相同但名称改为Template。基本规则是第一级控件为容器、第二级控件为Thumb类型自动识别为拖动点,拖动方向由HorizontalAlignment和VerticalAlignment决定。
7、窗口平滑拖动
之所有要对窗口拖动平滑处理是因为,自定义的调整大小只能设置Window的Left、Top、Width、Height,当窗口进行左或上拖动时右或下会出现残影,这种情况通过SetWindowPos和MoveWindow也无法改善。在不使用窗口自带的拖动功能的情况下,目前笔者研究出的方法就是使用透明窗口全屏,控件模拟窗口进行拖动。当然这种实现的限制就是一定要透明窗口,AllowTransparency为true或者WindowChrome的GlassFrameThickness为-1。
因为这种实现还不是很完美对装饰器不兼容,所以提供IsWindowDragSmooth属性,可以打开和关闭功能。
public static bool GetIsWindowDragSmooth(DependencyObject obj)
{return (bool)obj.GetValue(IsWindowDragSmoothProperty);
}public static void SetIsWindowDragSmooth(DependencyObject obj, bool value)
{obj.SetValue(IsWindowDragSmoothProperty, value);
}/// <summary>
/// 拖拽窗口调整大小是否平滑处理,作用是避免拖拽窗口左上时右下闪烁。
/// 此属性只对窗口有效
/// 此属性为true时需要透明窗口才能生效,即AllowTransparency为true或者WindowChrome的GlassFrameThickness为-1。
/// 当前版本不兼容有装饰器的窗口,拖动中装饰器可能会显示在窗口外面。
/// </summary>
// Using a DependencyProperty as the backing store for IsWindowDragSmooth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsWindowDragSmoothProperty =DependencyProperty.RegisterAttached("IsWindowDragSmooth", typeof(bool), typeof(Resize), new PropertyMetadata(false));
8、拖动事件
提供3个拖动事件,拖动开始、拖动变化、拖动结束。
代码示例如下:
/// <summary>
/// 拖动开始事件
/// </summary>
public static readonly RoutedEvent DragResizeStartedEvent = EventManager.RegisterRoutedEvent("DragResizeStarted", RoutingStrategy.Direct, typeof(EventHandler<DragResizeStartedEventArgs>), typeof(Resize));
/// <summary>
/// 拖动变化事件
/// </summary>
public static readonly RoutedEvent DragResizeDeltaEvent = EventManager.RegisterRoutedEvent("DragResizeDelta", RoutingStrategy.Direct, typeof(EventHandler<DragResizeDeltaEventArgs>), typeof(Resize));
/// <summary>
/// 拖动结束事件
/// </summary>
public static readonly RoutedEvent DragResizeCompletedEvent = EventManager.RegisterRoutedEvent("DragResizeCompleted", RoutingStrategy.Direct, typeof(EventHandler<DragResizeCompletedEventArgs>), typeof(Resize));
9、其他功能
(1)适应MinWidth、MinHeight
在第四章完整带的基础上将边界判断修改为控件的MinWidth、MinHeight即可。
横向
if (width >= minWidth/*原本是0*/)
{//略
}
纵向与横向逻辑一致,修改对应变量即可,略
(2)适应MaxWidth、MaxHeight
超过了最大值需要进行修正示例如下
横向:
if (width > c.MaxWidth)
{if (thumb.HorizontalAlignment == HorizontalAlignment.Left){left += width - c.MaxWidth;right = margin.Right;}else{left = margin.Left;right += width - c.MaxWidth;}width = c.MaxWidth;
}
纵向与横向逻辑一致,修改对应变量即可,略。
(3)适配任意dpi
所有改变坐标以及大小的代码已经适配了任意dpi。
主要注意的就是PointToScreen得到的坐标需要dpi转换。
下列是获取dpi的方法。
static Point GetDpiFromVisual(Visual visual)
{var source = PresentationSource.FromVisual(visual);var dpiX = 96.0;var dpiY = 96.0;if (source?.CompositionTarget != null){dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;}return new Point(dpiX, dpiY);
}
二、完整代码
vs2022 wpf .net 6.0 项目,包含了第四章的功能,不需要重复下载。如果需要单独使用Resize.cs文件需要添加依赖,参考附录。
https://download.csdn.net/download/u013113678/88749991
三、使用示例
0、基础功能
这个是与第四章一致的基础功能。
(1)、引用命名空间
Window 的其他属性略
<Window xmlns:ac="clr-namespace:AC"/>
(2)、使用附加属性
需要某个控件可以拖动调整大小则
<Grid ac:Resize.IsDragResizeable="True" />
(3)、窗口注意事项
当Window需要使用此功能时
<Window ac:Resize.IsDragResizeable="True" />
Window的拖动点会放在子控件的装饰层,需要注意以几点:
①Window必须有子控件,子控件有装饰层(大部分控件都有装饰层比如Grid、Border、Button等)。
②子控件不可限定大小需要,跟随窗口大小变化。
③此功能与Window自带的调整大小区别是,此功能可以用于无边框窗口以及自定义拖动点。
1、DragResize
DragResize需要在鼠标左键按下事件中使用,对已存在的控件或者动态生成控件使用。此方法不需要ac:Resize.IsDragResizeable="True"也可以使用。
xaml
<Window x:Class="WpfResize.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:WpfResize"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"WindowStyle="None"ResizeMode="NoResize"><Grid x:Name="grid" Background="SeaGreen" MouseLeftButtonDown="Window_MouseLeftButtonDown"/>
</Window>
因为是拓展方法,所以获取到控件对象直接调用DragResize即可。
cs
using AC;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;namespace WpfResize
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private async void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e){//生成控件var border = new Border();border.Background = Brushes.Azure;border.Width = 0;border.Height = 0;//加入到容器grid.Children.Add(border);//拖出控件await border.DragResize();//如果宽高为0则移除if (border.Width == 0|| border.Height == 0){grid.Children.Remove(border);}}}
}
效果预览
注:qq录制鼠标出现了偏移
2、边界限制
设置ac:Resize.IsResizeInBounds="True"即可。边界限制的范围是父控件。
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"WindowStyle="None"ResizeMode="NoResize" ><Grid x:Name="grid" Background="SeaGreen"><Border BorderThickness="1" BorderBrush="White" Margin="40"><StackPanel><Border ac:Resize.IsDragResizeable="True" ac:Resize.IsResizeInBounds="False" Background="White" Height="100" Width="200" CornerRadius="10" ><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="不限制边界"></TextBlock></Border><Border ac:Resize.IsDragResizeable="True" ac:Resize.IsResizeInBounds="True" Margin="0,20,0,0" Background="White" Height="100" Width="200" CornerRadius="10" ><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="限制边界"></TextBlock></Border></StackPanel></Border></Grid>
</Window>
效果预览
注:qq录制鼠标出现了偏移
3、交叉拖动
通过附加属性ac:Resize.IsAllowsCrossover设置是否交叉拖动,默认为true。
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"WindowStyle="None"ResizeMode="NoResize" ><Grid x:Name="grid" Background="SeaGreen"><StackPanel><Border Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="False" Background="White" Height="100" Width="200" CornerRadius="10" ><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="不允许交叉拖动"></TextBlock></Border><Border ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="True" Margin="0,20,0,0" Background="White" Height="100" Width="200" CornerRadius="10" ><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="允许交叉拖动"></TextBlock></Border></StackPanel></Grid>
</Window>
效果预览
注:qq录制鼠标出现了偏移
4、拖动点布局模板
通过ac:Resize.ThumbsTemplate设置拖动点模板
(1)自定义圆点
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" ><Grid x:Name="grid" Background="White"><Grid Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="False" Background="SeaGreen " Height="100" Width="200" ><ac:Resize.ThumbsTemplate><ControlTemplate ><Border BorderBrush="Gray" BorderThickness="2" CornerRadius="8" Background="White" Width="16" Height="16"/></ControlTemplate></ac:Resize.ThumbsTemplate><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Text="自定义拖动点模板"></TextBlock></Grid></Grid>
</Window>
效果预览
(2)4个顶点
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" WindowStyle="None"ResizeMode="NoResize"><Grid x:Name="grid" Background="White"><Grid Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="False" Background="SeaGreen " Height="100" Width="200" ><ac:Resize.ThumbsTemplate><ControlTemplate ><Border x:Name="brd" BorderBrush="Gray" BorderThickness="2" CornerRadius="8" Background="White" Width="16" Height="16"/><!--通过触发器隐藏4条边上的点--><ControlTemplate.Triggers><!--左右两条边上的点--><Trigger Property="HorizontalAlignment" Value="Stretch"><Setter TargetName="brd" Property="Visibility" Value="Collapsed" ></Setter></Trigger><!--上下两条边上的点--><Trigger Property="VerticalAlignment" Value="Stretch"><Setter TargetName="brd" Property="Visibility" Value="Collapsed" ></Setter></Trigger></ControlTemplate.Triggers></ControlTemplate></ac:Resize.ThumbsTemplate><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Text="自定义拖动点模板"></TextBlock></Grid></Grid>
</Window>
效果预览
(3)单独定制每个点
通过MultiTrigger触发器来区分每个点。
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" WindowStyle="None"ResizeMode="NoResize"><Grid x:Name="grid" Background="White"><Grid Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="False" Background="SeaGreen" Height="100" Width="200" ><ac:Resize.ThumbsTemplate><ControlTemplate ><Border x:Name="brd" BorderBrush="Gray" BorderThickness="2" CornerRadius="8" Background="White" Width="16" Height="16"/><ControlTemplate.Triggers><!--左上--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Left" ></Condition><Condition Property="VerticalAlignment" Value="Top" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="Aqua"></Setter></MultiTrigger><!--右上--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Right" ></Condition><Condition Property="VerticalAlignment" Value="Top" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="DarkGoldenrod"></Setter></MultiTrigger><!--右下--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Right" ></Condition><Condition Property="VerticalAlignment" Value="Bottom" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="DeepPink"></Setter></MultiTrigger><!--左下--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Left" ></Condition><Condition Property="VerticalAlignment" Value="Bottom" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="Red"></Setter></MultiTrigger><!--上--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Stretch" ></Condition><Condition Property="VerticalAlignment" Value="Top" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="Gold"></Setter></MultiTrigger><!--下--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Stretch" ></Condition><Condition Property="VerticalAlignment" Value="Bottom" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="Indigo"></Setter></MultiTrigger><!--左--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Left" ></Condition><Condition Property="VerticalAlignment" Value="Stretch" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="Blue"></Setter></MultiTrigger><!--右--><MultiTrigger><MultiTrigger.Conditions><Condition Property="HorizontalAlignment" Value="Right" ></Condition><Condition Property="VerticalAlignment" Value="Stretch" ></Condition></MultiTrigger.Conditions><Setter TargetName="brd" Property="BorderBrush" Value="Green"></Setter></MultiTrigger></ControlTemplate.Triggers></ControlTemplate></ac:Resize.ThumbsTemplate><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Text="自定义拖动点模板"></TextBlock></Grid></Grid>
</Window>
效果预览
5、拖动点容器模板
通过ac:Resize.ThumbsPanel设置拖动点容器模板,主要作用是设置margin,方便调整拖动点的偏移。
默认的容器有Margin="-3"的偏移。
(1)无Margin
此示例是为了说明无Margin的情况。
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" WindowStyle="None"ResizeMode="NoResize"><Grid x:Name="grid" Background="White"><Grid Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="False" Background="SeaGreen " Height="100" Width="200" ><ac:Resize.ThumbsTemplate><ControlTemplate ><Border x:Name="brd" BorderBrush="Gray" BorderThickness="2" CornerRadius="8" Background="White" Width="16" Height="16"/></ControlTemplate></ac:Resize.ThumbsTemplate><ac:Resize.ThumbsPanel><ItemsPanelTemplate><Grid></Grid></ItemsPanelTemplate></ac:Resize.ThumbsPanel><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Text="自定义拖点容器模板"></TextBlock></Grid></Grid>
</Window>
效果预览
(2)设置Margin
Margin设置为拖动点的一半大小就刚好在边线中间。
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" WindowStyle="None"ResizeMode="NoResize"><Grid x:Name="grid" Background="White"><Grid Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" ac:Resize.IsAllowsCrossover="False" Background="SeaGreen " Height="100" Width="200" ><ac:Resize.ThumbsTemplate><ControlTemplate ><Border x:Name="brd" BorderBrush="Gray" BorderThickness="2" CornerRadius="8" Background="White" Width="16" Height="16"/></ControlTemplate></ac:Resize.ThumbsTemplate><ac:Resize.ThumbsPanel><ItemsPanelTemplate><Grid Margin="-8"></Grid></ItemsPanelTemplate></ac:Resize.ThumbsPanel><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Text="自定义拖点容器模板"></TextBlock></Grid></Grid>
</Window>
效果预览
6、整体模板
设置整体模板Template后会覆盖拖动点模板和拖动点布局模板。规则是第一级控件为容器、第二级控件为Thumb类型自动识别为拖动点,拖动方向由HorizontalAlignment和VerticalAlignment决定, 即可以有任意个拖动点Thumb,也可以放任意其他控件。
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" WindowStyle="None"ResizeMode="NoResize"><Grid x:Name="grid" Background="White"><Grid Margin="0,20,0,0" ac:Resize.IsDragResizeable="True" Background="SeaGreen " Height="100" Width="200" ><ac:Resize.Template><ControlTemplate ><Grid Margin="-4"><Grid.Resources><Style TargetType="Thumb"><Setter Property="Width" Value="8"></Setter><Setter Property="Height" Value="8"></Setter><Setter Property="Template"><Setter.Value><ControlTemplate><Border Background="Aqua"></Border></ControlTemplate></Setter.Value></Setter></Style></Grid.Resources><Border BorderBrush="Aqua" BorderThickness="2" Margin="4"></Border><!--左--><Thumb HorizontalAlignment="Left" Cursor="SizeWE"/><!--上--><Thumb VerticalAlignment="Top" Cursor="SizeNS"/><!--右--><Thumb HorizontalAlignment="Right" Cursor="SizeWE"/><!--下--><Thumb VerticalAlignment="Bottom" Cursor="SizeNS"/><!--左上--><Thumb HorizontalAlignment="Left" VerticalAlignment="Top" Cursor="SizeNWSE"/><!--右上--><Thumb HorizontalAlignment="Right" VerticalAlignment="Top" Cursor="SizeNESW"/><!--右下--><Thumb HorizontalAlignment="Right" VerticalAlignment="Bottom" Cursor="SizeNWSE"/><!--左下--><Thumb HorizontalAlignment="Left" VerticalAlignment="Bottom" Cursor="SizeNESW"/></Grid></ControlTemplate></ac:Resize.Template><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Text="自定义整体模板"></TextBlock></Grid></Grid>
</Window>
效果预览
7、窗口平滑拖动
窗口为透明窗口(AllowTransparency为true或者WindowChrome的GlassFrameThickness为-1),附加属性 ac:Resize.IsWindowDragSmooth设置为true即可以实现平滑拖动。
注:当前版本和装饰器不兼容,拖动时装饰器可能显示在窗口外面,谨慎使用此属性
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" WindowStyle="None"ResizeMode="NoResize"AllowsTransparency="True"ac:Resize.IsDragResizeable="True"ac:Resize.IsWindowDragSmooth="True"><Grid Background="SeaGreen "/>
</Window>
作为对比先展示非平滑拖动
注:qq录制鼠标出现了偏移
设置平滑拖动效果预览
注:qq录制鼠标出现了偏移
8、拖动事件
3个事件,拖动开始ac:Resize.DragResizeStarted、拖动变化ac:Resize.DragResizeDelta、拖动结束ac:Resize.DragResizeCompleted
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" ><Grid Background="SeaGreen "><Border Background="Aqua" Width="200" Height="200" ac:Resize.IsDragResizeable="True" ac:Resize.DragResizeStarted="Border_DragResizeStarted" ac:Resize.DragResizeCompleted="Border_DragResizeCompleted" ac:Resize.DragResizeDelta="Border_DragResizeDelta"></Border></Grid>
</Window>
cs
using AC;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;namespace WpfResize
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void Border_DragResizeStarted(object sender, DragResizeStartedEventArgs e){Console.WriteLine("开始拖动");}private void Border_DragResizeCompleted(object sender, DragResizeCompletedEventArgs e){Console.WriteLine("结束拖动");}private void Border_DragResizeDelta(object sender, DragResizeDeltaEventArgs e){Console.WriteLine("横向变化:"+e.HorizontalChange+ " 纵向变化:"+e.VerticalChange+ " 宽变化:" + e.WidthChange + " 高变化:" + e.HeightChange);}}
}
效果预览
注:qq录制鼠标出现了偏移
9、其他功能
(1)适应MinWidth、MinHeight
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" ><Grid Background="SeaGreen "><Border Background="Aqua" MinWidth="100" MinHeight="100" Width="200" Height="200" ac:Resize.IsDragResizeable="True" ></Border></Grid>
</Window>
效果预览
注:qq录制鼠标出现了偏移
(2)适应MaxWidth、MaxHeight
xaml
<Window x:Class="WpfResize.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:WpfResize"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800" ><Grid Background="SeaGreen "><Border Background="Aqua" MaxWidth="200" MaxHeight="200" Width="100" Height="100" ac:Resize.IsDragResizeable="True" ></Border></Grid>
</Window>
效果预览
总结
以上就是今天要讲的内容,拓展后的功能更加全面以及兼容性更强了,比如DragRezie就可以用于画板,边界限制也是比较实用的功能,拖动点模板简化了自定义的难度,拖动事件可以用于实现撤销重做功能,窗口平滑拖动优化了使用体验。但是还是有些功能不够完,需要后期继续优化。总的来说,本文实现的拖动调整大小模块已经变得更加方便实用,后期还会继续完善优化。
附录
1、依赖System.Drawing
完整代码依赖了System.Drawing,添加依赖的方法参考C# wpf 使用GDI+实现截屏的第一节。
相关文章:

C# wpf 实现任意控件(包括窗口)更多调整大小功能
WPF拖动改变大小系列 第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小 第五章 拓展更多调整大小功能(本章) 文章目录 WPF拖动改变大小系列前言一、添加的功能1、任意控件Drag…...

Vue+OpenLayers7入门到实战:快速搭建Vue+OpenLayers7地图脚手架项目。从零开始构建Vue项目并整合OpenLayers7.5.2
返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 前言 本章针对Vue初学者,对Vue不熟悉,甚至还不会Vue的入门学生读者。 本章会详细讲解从NodeJS环境到npm环境的各个步骤,再到使用vue-cli脚手架快速生成项目,以及添加OpenLayers7地图库依赖,编写简单的xyz高德地图显示…...

mysql-线上常用运维sql
1.表备份 INSERT INTO table1 SELECT * FROM table2; 2.用一个表中的字段更新另一张表中的字段 UPDATE table2 JOIN table1 ON table2.id table1.id SET table2.column2 table1.column1; 3.在MySQL中,查询一个表的列字段值是否包含另一个表的字段,…...

Linux之进程间通信(system V 共享内存)
目录 一、共享内存 1、基本原理 2、共享内存的创建 3、共享内存的释放 4、共享内存的关联 5、共享内存的去关联 6、查看IPC资源 二、完整通信代码 三、共享内存的特点 四、信号量 1、相关概念 2、信号量概念 进程间通信的本质就是让不同的进程看到同一个资源。而前…...

数据库 sql select *from account where name=‘张三‘ 执行过程
select *from account where name张三分析上面语句的执行过程 用到了索引 由于是根据 1.name字段进行查询,所以先根据name张三’到name字段的二级索引中进行匹配查 找。但是在二级索引中只能查找到 Arm 对应的主键值 10。 2.由于查询返回的数据是*,…...

力扣日记1.27-【回溯算法篇】131. 分割回文串
力扣日记:【回溯算法篇】131. 分割回文串 日期:2023.1.27 参考:代码随想录、力扣 131. 分割回文串 题目描述 难度:中等 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可…...

如何用web界面打开华为防火墙
目录 1.创建一个虚拟网卡 2.cloud操作 3.防火墙上操作 4. 登录 1.创建一个虚拟网卡 2.cloud操作 3.防火墙上操作 4. 登录...

力扣20、有效的括号(简单)
1 题目描述 图1 题目描述 2 题目解读 给定的字符串只包含括号,判断这个字符串中的括号是否按照正确顺序出现,即这个字符串是否有效。 3 解法一:栈 C的STL中的stack,在解题时非常好用。 3.1 解题思路 使用栈stk,并枚举…...

Android 系统启动过程
当按下电源时,引导芯片代码会从预定义的地方(固化在ROM) 开始执行,加载引导程序BootLoader到RAM,然后执行。 启动内核的第一个进程idle(pid0),idle进程是Linux系统第一个进程,是init进程和kthreadd进程的父进程。 idle的主要作用 初始化进程以及内存管…...

基于STM32的智能手环设计与实现
需要原理图工程,源码,PCB工程的朋友收藏,这篇文章关注我,私我吧!!! 基于STM32的智能手环设计与实现 摘要一、研究背景及意义二、实现功能三、系统方案设计系统方案设计框图3.1 单片机芯片选择3…...

[BJDCTF2020]The mystery of ip
hint 猜测ip和XFF有关 加一个XFF 下面这一步是看了wp出来的:存在ssti 这里尝试用jinja的注入方法,页面回显了是php的smarty框架 查了一下smarty的注入方法,发现可以直接执行php命令 在根目录找到flag...

RUST笔记:candle使用基础
candle介绍 candle是huggingface开源的Rust的极简 ML 框架。 candle-矩阵乘法示例 cargo new myapp cd myapp cargo add --git https://github.com/huggingface/candle.git candle-core cargo build # 测试,或执行 cargo ckeckmain.rs use candle_core::{Device…...

Python算法题集_接雨水
本文为Python算法题集之一的代码示例 题目42:接雨水 说明:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1]…...

FIND_IN_SET的使用:mysql表数据多角色、多用户查询
MySQL 函数 FIND_IN_SET 是用于在逗号分隔的字符串中查找特定值的函数。它的语法如下: FIND_IN_SET(search_value, comma_separated_string)search_value 是要查找的值。 comma_separated_string 是逗号分隔的字符串,在这个字符串中查找指定的值。FIND_…...

JVM篇----第十一篇
系列文章目录 文章目录 系列文章目录前言一、在新生代-复制算法二、在老年代-标记整理算法三、分区收集算法四、GC 垃圾收集器前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你…...

鸿蒙HarmonyOS获取GPS精确位置信息
参考官方文档 #1.初始化时获取经纬度信息 aboutToAppear() {this.getLocation() } async getLocation () {try {const result await geoLocationManager.getCurrentLocation()AlertDialog.show({message: JSON.stringify(result)})}catch (error) {AlertDialog.show({message…...

java正则校验,手机号,邮箱,日期格式,时间格式,数字金额两位小数
java正则校验,手机号,邮箱,日期格式,时间格式,数字金额两位小数 3.58是否为金额:true 3.582是否为金额:false 1284789qq.com是否为email:true 1284789qq.com是否为email࿱…...

php下curl发送cookie
目录 一:使用 CURLOPT_COOKIE 选项 二:CURLOPT_COOKIEFILE 三:CURLOPT_HTTPHEADER php curl发送cookie的几种方式,下面来介绍下 一:使用 CURLOPT_COOKIE 选项 通过设置 CURLOPT_COOKIE 选项,你可以将 cookie 字符…...

stable diffusion学习笔记——文生图(一)
模型设置 基本模型 基本模型也就是常说的checkpoint(大模型),基本模型决定了生成图片的主体风格。 如上图所示,基本模型的后缀为.safetensors。需要存放在特定的文件夹下。 如果用的是启动器,可以在启动器内直接下载。…...

Linux下安装openresty
Linux下安装openresty 十一、Linux下安装openresty11.1.概述11.2.下载OpenResty并安装相关依赖:11.3.使用wget下载:11.4.解压缩:11.5.进入OpenResty目录:11.6.编译和安装11.7.进入OpenResty的目录,找到nginx:11.8.在conf目录下的nginx.conf添…...

【IM】如何保证消息可用性(一)
目录 1. 基本概念1.1 长连接 和 短连接1.2 PUSH模式和PULL模式 2. 背景介绍2.1 理解端到端的思想 3. 方案选型3.1 技术挑战3.2 技术目标 1. 基本概念 在讲解消息可用性之前,需要理解几个通信领域的基本概念。 1.1 长连接 和 短连接 什么是长连接,短连接…...

js直接下载附件和通过blob数据类型下载文件
js下载文件方式有使用a标签的,也有直接用window.open的,还有用form表单的;这里采用的是a标签的下载方式,一种是url直接下载,另一种是文件的blob数据类型进行下载。 文件blob数据类型的获取一般是后端返回文件的二进制流…...

第2章-神经网络的数学基础——python深度学习
第2章 神经网络的数学基础 2.1 初识神经网络 我们来看一个具体的神经网络示例,使用 Python 的 Keras 库 来学习手写数字分类。 我们这里要解决的问题是, 将手写数字的灰度图像(28 像素28 像素)划分到 10 个类别 中(0…...

【Docker】Docker学习⑧ - Docker仓库之分布式Harbor
【Docker】Docker学习⑧ - Docker仓库之分布式Harbor 一、Docker简介二、Docker安装及基础命令介绍三、Docker镜像管理四、Docker镜像与制作五、Docker数据管理六、网络部分七、Docker仓库之单机Dokcer Registry八、 Docker仓库之分布式Harbor1 Harbor功能官方介绍2 安装Harbor…...

一行命令在 wsl-ubuntu 中使用 Docker 启动 Windows
在 wsl-ubuntu 中使用 Docker 启动 Windows 0. 背景1. 验证我的系统是否支持 KVM?2. 使用 Docker 启动 Windows3. 访问 Docker 启动的 Windows4. Docker Hub 地址5. Github 地址 0. 背景 我们可以在 Windows 系统使用安装 wsl-ubuntu,今天玩玩在 wsl-ub…...

Datawhale 组队学习之大模型理论基础 Task7 分布式训练
第8章 分布式训练 8.1 为什么分布式训练越来越流行 近年来,模型规模越来越大,对硬件(算力、内存)的发展提出要求。因为内存墙的存在,单一设持续提高芯片的集成越来越困难,难以跟上模型扩大的需求。 为了…...

05-使用结构体构建相关数据
上一篇: 04-了解所有权 结构体(struct)是一种自定义数据类型,可以将多个相关值打包命名,组成一个有意义的组。如果你熟悉面向对象的语言,那么结构体就像是对象的数据属性。在本章中,我们将对元组…...

【Android】Android中的系统镜像由什么组成?
文章目录 总览Boot Loader 的加锁与解锁Boot 镜像内核RAM diskARM 中的设备树 (Device Tree) /System 和/Data 分区镜像参考 总览 各种Android设备都只能刷专门为相应型号的设备定制的镜像。 厂商会提供一套系统镜像把它作为“出厂默认”的 Android 系统刷在设备上。 一个完…...

仿真机器人-深度学习CV和激光雷达感知(项目2)day7【ROS关键组件】
文章目录 前言Launch 文件了解 XML 文件Launch 文件作用Launch 文件常用标签实例--作业1的 Launch 文件TF Tree介绍发布坐标变换--海龟例程获取坐标变换--海龟自动跟随例程rqt_工作箱前言 💫你好,我是辰chen,本文旨在准备考研复试或就业 💫本文内容是我为复试准备的第二个…...

解锁一些SQL注入的姿势
昨天课堂上布置了要去看一些sql注入的案例,以下是我的心得: 1.新方法 打了sqli的前十关,我发现一般都是联合查询,但是有没有不是联合查询的方法呢…...