using System.Windows.Input; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System; using System.Windows.Shapes; namespace WPFProject.Utils { public class Resize { private static readonly double MinWidth = 60; private static readonly double MinHeight = 60; public static double GetReMaxWidth(DependencyObject obj) { return (double)obj.GetValue(ReMaxWithProperty); } public static void SetReMaxWidth(DependencyObject obj, double value) { obj.SetValue(ReMaxWithProperty, value); } public static readonly DependencyProperty ReMaxWithProperty = DependencyProperty.RegisterAttached("ReMaxWith", typeof(double), typeof(Resize), new PropertyMetadata(0.0)); // 拖拽模板 public static ControlTemplate GetResizeTemplate(DependencyObject obj) { return (ControlTemplate)obj.GetValue(ResizeTemplateProperty); } public static void SetResizeTemplate(DependencyObject obj, ControlTemplate value) { obj.SetValue(ResizeTemplateProperty, value); } public static readonly DependencyProperty ResizeTemplateProperty = DependencyProperty.RegisterAttached("ResizeTemplate", typeof(ControlTemplate), typeof(Resize), new PropertyMetadata(null, (d, e) => { FrameworkElement element = d as FrameworkElement; if (element == null) return; var resize = GetResizeable(element); if (resize == null) return; resize.Detach(); resize.Attach(); })); // 是否可以拖拽改变大小 public static bool GetIsResizeable(DependencyObject obj) { return (bool)obj.GetValue(IsResizeableProperty); } public static void SetIsResizeable(DependencyObject obj, bool value) { obj.SetValue(IsResizeableProperty, value); } public static readonly DependencyProperty IsResizeableProperty = DependencyProperty.RegisterAttached("IsResizeable", typeof(bool), typeof(Resize), new PropertyMetadata(false, (d, e) => { FrameworkElement element = d as FrameworkElement; if (element == null) return; // 先取消之前的拖拽装饰 Resizeable resize = GetResizeable(element); if (resize != null) resize.Detach(); // 实现逻辑 // 判断属性设置的是不是true if (!(bool)e.NewValue) { SetResizeable(element, null); return; } if (!element.IsLoaded) { RoutedEventHandler Loaded_Resize = null; Loaded_Resize = (sender, e1) => { Resizeable resize = new Resizeable(element); resize.Attach(); SetResizeable(element, resize); element.Loaded -= Loaded_Resize; }; element.Loaded += Loaded_Resize; } else { resize = new Resizeable(element); resize.Attach(); SetResizeable(element, resize); } })); public static Resizeable GetResizeable(DependencyObject obj) { return (Resizeable)obj.GetValue(ResizeableProperty); } public static void SetResizeable(DependencyObject obj, Resizeable value) { obj.SetValue(ResizeableProperty, value); } // Using a DependencyProperty as the backing store for Resizeable. This enables animation, styling, binding, etc... static readonly DependencyProperty ResizeableProperty = DependencyProperty.RegisterAttached("Resizeable", typeof(Resizeable), typeof(Resize), new PropertyMetadata(null)); public class Resizeable { ResizeType _resizeType; enum ResizeType { None, Grid, Canvas, Window, Others, } FrameworkElement _resizeTarget; FrameworkElement _thumbsParent = null; public Resizeable(FrameworkElement resizeTarget) { _resizeTarget = resizeTarget; } public void Attach() { FrameworkElement thumbsRoot = null; ControlTemplate template = GetResizeTemplate(_resizeTarget); // 看不懂了,大概是拖拽模板 if (template != null) { Control control = new Control(); control.Template = template; control.ApplyTemplate(); thumbsRoot = control; _thumbsParent = VisualTreeHelper.GetChild(thumbsRoot, 0) as FrameworkElement; } if (thumbsRoot != null) { UIElement target = _resizeTarget is Window _windowTarget ? _windowTarget.Content as UIElement : _resizeTarget; if (target != null) { AdornerLayer layer = AdornerLayer.GetAdornerLayer(target); if (layer == null) throw new Exception("获取控件装饰层失败,控件可能没有装饰层!"); layer.Add(new ResizeAdorner(target, thumbsRoot)); } } // 使用缺省样式 if (thumbsRoot == null) { Thumb leftThumb = null, topThumb = null, rightThumb = null, bottomThumb = null, lefTopThumb = null, rightTopThumb = null, rightBottomThumb = null, leftbottomThumb = null; int thumnSize = 5; // 初始化thumb leftThumb = new Thumb(); leftThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; leftThumb.HorizontalAlignment = HorizontalAlignment.Left; leftThumb.VerticalAlignment = VerticalAlignment.Stretch; leftThumb.Cursor = Cursors.SizeWE; leftThumb.Width = thumnSize; leftThumb.Height = double.NaN; leftThumb.Margin = new Thickness(0, thumnSize, 0, thumnSize); topThumb = new Thumb(); topThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; topThumb.HorizontalAlignment = HorizontalAlignment.Stretch; topThumb.VerticalAlignment = VerticalAlignment.Top; topThumb.Cursor = Cursors.SizeNS; topThumb.Width = double.NaN; topThumb.Height = thumnSize; topThumb.Margin = new Thickness(thumnSize, 0, thumnSize, 0); rightThumb = new Thumb(); rightThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; rightThumb.HorizontalAlignment = HorizontalAlignment.Right; rightThumb.VerticalAlignment = VerticalAlignment.Stretch; rightThumb.Cursor = Cursors.SizeWE; rightThumb.Width = thumnSize; rightThumb.Height = double.NaN; rightThumb.Margin = new Thickness(0, thumnSize, 0, thumnSize); bottomThumb = new Thumb(); bottomThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; bottomThumb.HorizontalAlignment = HorizontalAlignment.Stretch; bottomThumb.VerticalAlignment = VerticalAlignment.Bottom; bottomThumb.Cursor = Cursors.SizeNS; bottomThumb.Width = double.NaN; bottomThumb.Height = thumnSize; bottomThumb.Margin = new Thickness(thumnSize, 0, thumnSize, 0); lefTopThumb = new Thumb(); lefTopThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; lefTopThumb.HorizontalAlignment = HorizontalAlignment.Left; lefTopThumb.VerticalAlignment = VerticalAlignment.Top; lefTopThumb.Cursor = Cursors.SizeNWSE; lefTopThumb.Width = thumnSize; lefTopThumb.Height = thumnSize; rightTopThumb = new Thumb(); rightTopThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; rightTopThumb.HorizontalAlignment = HorizontalAlignment.Right; rightTopThumb.VerticalAlignment = VerticalAlignment.Top; rightTopThumb.Cursor = Cursors.SizeNESW; rightTopThumb.Width = thumnSize; rightTopThumb.Height = thumnSize; rightBottomThumb = new Thumb(); rightBottomThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; rightBottomThumb.HorizontalAlignment = HorizontalAlignment.Right; rightBottomThumb.VerticalAlignment = VerticalAlignment.Bottom; rightBottomThumb.Cursor = Cursors.SizeNWSE; rightBottomThumb.Width = thumnSize; rightBottomThumb.Height = thumnSize; leftbottomThumb = new Thumb(); leftbottomThumb.Template = new ControlTemplate(typeof(Thumb)) { VisualTree = GetFactory(new SolidColorBrush(Colors.Transparent)) }; leftbottomThumb.HorizontalAlignment = HorizontalAlignment.Left; leftbottomThumb.VerticalAlignment = VerticalAlignment.Bottom; leftbottomThumb.Cursor = Cursors.SizeNESW; leftbottomThumb.Width = thumnSize; leftbottomThumb.Height = thumnSize; Grid 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); UIElement element; if (_resizeTarget is Window _resizeTargetWindow) { element = _resizeTargetWindow.Content as UIElement; } else { element = _resizeTarget; } if (element != null) { var layer = AdornerLayer.GetAdornerLayer(element); if (layer == null) throw new Exception("获取控件装饰层失败,控件可能没有装饰层!"); layer.Add(new ResizeAdorner(element, grid)); } thumbsRoot = grid; _thumbsParent = thumbsRoot; } //注册事件 for (int i = 0; i < VisualTreeHelper.GetChildrenCount(_thumbsParent); i++) { Thumb thumb = VisualTreeHelper.GetChild(_thumbsParent, i) as Thumb; if (thumb != null) { thumb.DragStarted += Thumb_DragStarted; thumb.DragDelta += Thumb_DragDelta; } } } public void Detach() { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(_thumbsParent); i++) { Thumb thumb = VisualTreeHelper.GetChild(_thumbsParent, i) as Thumb; if (thumb != null) { thumb.DragStarted -= Thumb_DragStarted; thumb.DragDelta -= Thumb_DragDelta; } } _thumbsParent = null; } private void Thumb_DragStarted(object sender, DragStartedEventArgs e) { _resizeType = GetResizeType(); if (_resizeType == ResizeType.Canvas) { Canvas parent = GetCanvas(_resizeTarget); CanvasHelper.SetTopZIndex(_resizeTarget, parent, _resizeTarget.GetType()); } } private void Thumb_DragDelta(object sender, DragDeltaEventArgs e) { switch (_resizeType) { case ResizeType.None: break; case ResizeType.Grid: { FrameworkElement element = _resizeTarget; FrameworkElement thumb = sender as FrameworkElement; double left, top, right, bottom, width, height; if (thumb.HorizontalAlignment == HorizontalAlignment.Left) { right = element.Margin.Right; left = element.Margin.Left + e.HorizontalChange; width = (double.IsNaN(element.Width) ? element.ActualWidth : element.Width) - e.HorizontalChange; } else { left = element.Margin.Left; right = element.Margin.Right - e.HorizontalChange; width = (double.IsNaN(element.Width) ? element.ActualWidth : element.Width) + e.HorizontalChange; } if (thumb.VerticalAlignment == VerticalAlignment.Top) { bottom = element.Margin.Bottom; top = element.Margin.Top + e.VerticalChange; height = (double.IsNaN(element.Height) ? element.ActualHeight : element.Height) - e.VerticalChange; } else { top = element.Margin.Top; bottom = element.Margin.Bottom - e.VerticalChange; height = (double.IsNaN(element.Height) ? element.ActualHeight : element.Height) + e.VerticalChange; } if (thumb.HorizontalAlignment != HorizontalAlignment.Center && thumb.HorizontalAlignment != HorizontalAlignment.Stretch) { if (width >= 0) { element.Margin = new Thickness(left, element.Margin.Top, right, element.Margin.Bottom); element.Width = width; } } if (thumb.VerticalAlignment != VerticalAlignment.Center && thumb.VerticalAlignment != VerticalAlignment.Stretch) { if (height >= 0) { element.Margin = new Thickness(element.Margin.Left, top, element.Margin.Right, bottom); element.Height = height; } } } break; case ResizeType.Canvas: { FrameworkElement target = _resizeTarget; Canvas parent = GetCanvas(target); FrameworkElement thumb = sender as FrameworkElement; if (parent == null) { // 一般不会出现这种情况,保险判断一下 return; } double left, top, width, height; if (thumb.HorizontalAlignment == HorizontalAlignment.Left) { left = double.IsNaN(Canvas.GetLeft(target)) ? 0 : Canvas.GetLeft(target) + e.HorizontalChange; width = (double.IsNaN(target.ActualWidth) ? 0 : target.ActualWidth) - e.HorizontalChange; } else { left = Canvas.GetLeft(target); width = (double.IsNaN(target.ActualWidth) ? 0 : target.ActualWidth) + e.HorizontalChange; } if (thumb.VerticalAlignment == VerticalAlignment.Top) { top = double.IsNaN(Canvas.GetTop(target)) ? 0 : Canvas.GetTop(target) + e.VerticalChange; height = (double.IsNaN(target.ActualHeight) ? 0 : target.ActualHeight) - e.VerticalChange; } else { top = Canvas.GetTop(target); height = (double.IsNaN(target.ActualHeight) ? 0 : target.ActualHeight) + e.VerticalChange; } if (thumb.HorizontalAlignment != HorizontalAlignment.Center && thumb.HorizontalAlignment != HorizontalAlignment.Stretch) { if (e.HorizontalChange > 0 || (width >= MinWidth && left >= 0)) { Canvas.SetLeft(target, left); target.Width = width; } } if (thumb.VerticalAlignment != VerticalAlignment.Center && thumb.VerticalAlignment != VerticalAlignment.Stretch) { if (e.VerticalChange > 0 || (height >= MinHeight && top >= 0)) { Canvas.SetTop(target, top); target.Height = height; } } } break; case ResizeType.Window: { var c = _resizeTarget as Window; var thumb = sender as FrameworkElement; double left, top, width, height; if (thumb.HorizontalAlignment == HorizontalAlignment.Left) { left = c.Left + e.HorizontalChange; width = c.Width - e.HorizontalChange; } else { left = c.Left; width = c.Width + e.HorizontalChange; } if (thumb.VerticalAlignment == VerticalAlignment.Top) { top = c.Top + e.VerticalChange; height = c.Height - e.VerticalChange; } else { top = c.Top; height = c.Height + e.VerticalChange; } if (thumb.HorizontalAlignment != HorizontalAlignment.Center && thumb.HorizontalAlignment != HorizontalAlignment.Stretch) { if (width > 63) { c.Width = width; c.Left = left; } } if (thumb.VerticalAlignment != VerticalAlignment.Center && thumb.VerticalAlignment != VerticalAlignment.Stretch) { if (height > 63) { c.Top = top; c.Height = height; } } } break; case ResizeType.Others: break; } } ResizeType GetResizeType() { ResizeType rt; DependencyObject parent = VisualTreeHelper.GetParent(_resizeTarget); if (parent is Grid) { rt = ResizeType.Grid; } else if (parent is Canvas) { rt = ResizeType.Canvas; } else if (_resizeTarget is Window) { rt = ResizeType.Window; } else { rt = ResizeType.Grid; } return rt; } // 方形工厂 FrameworkElementFactory GetFactory(Brush back) { FrameworkElementFactory fac = new FrameworkElementFactory(typeof(Rectangle)); fac.SetValue(Rectangle.FillProperty, back); return fac; } // 装饰器 class ResizeAdorner : Adorner { // 布局容器,如果不使用布局容器,则需要给上述8个控件布局,实现和Grid布局定位是一样的,会比较繁琐且意义不大。 UIElement _child; public ResizeAdorner(UIElement adornedElement, UIElement child) : base(adornedElement) { _child = child; AddVisualChild(child); } protected override Visual GetVisualChild(int index) { return _child; } protected override int VisualChildrenCount { get; } = 1; protected override Size ArrangeOverride(Size finalSize) { // 直接给grid布局,grid内部的thumb会自动布局。 _child.Arrange(new Rect(new Point(0, 0), finalSize)); return finalSize; } } } static Canvas GetCanvas(DependencyObject element) { DependencyObject e = element; while (e is not null and not Canvas) { e = VisualTreeHelper.GetParent(e); } return e == null ? null : e as Canvas; } } }