络腮胡菲菲 发表于 2022-9-3 06:33:18

WPF开发之ScreenCut截图改变大小

前言

接着上周写的截图控件继续更新 缩放操作。
1.WPF实现截屏「仿微信」
2.WPF 实现截屏控件之移动(二)「仿微信」
正文

https://files.mdnice.com/user/24276/0faafa99-da58-44f7-87fb-27115a096ebf.png
实现拉伸放大或缩小缩放操作需在矩形四个方向绘制8个Thumb,这里有两种方式
1)可以自行在XAML中硬编写8个Thumb
2)使用装饰器Adorner
本章使用了第二种方式
一、首先新建个项目,然后创建个自定义控件,命名为ScreenCutAdorner,然后让它继承Adorner。
1.1

在装饰器中定义
8个Thumb,对应8个方位点:
      const double THUMB_SIZE = 15;
      const double MINIMAL_SIZE = 20;
      Thumb lc, tl, tc, tr, rc, br, bc, bl;
      VisualCollection visCollec;
      public ScreenCutAdorner(UIElement adorned): base(adorned)
      {
          visCollec = new VisualCollection(this);
          visCollec.Add(lc = GetResizeThumb(Cursors.SizeWE, HorizontalAlignment.Left, VerticalAlignment.Center));
          visCollec.Add(tl = GetResizeThumb(Cursors.SizeNWSE, HorizontalAlignment.Left, VerticalAlignment.Top));
          visCollec.Add(tc = GetResizeThumb(Cursors.SizeNS, HorizontalAlignment.Center, VerticalAlignment.Top));
          visCollec.Add(tr = GetResizeThumb(Cursors.SizeNESW, HorizontalAlignment.Right, VerticalAlignment.Top));
          visCollec.Add(rc = GetResizeThumb(Cursors.SizeWE, HorizontalAlignment.Right, VerticalAlignment.Center));
          visCollec.Add(br = GetResizeThumb(Cursors.SizeNWSE, HorizontalAlignment.Right, VerticalAlignment.Bottom));
          visCollec.Add(bc = GetResizeThumb(Cursors.SizeNS, HorizontalAlignment.Center, VerticalAlignment.Bottom));
          visCollec.Add(bl = GetResizeThumb(Cursors.SizeNESW, HorizontalAlignment.Left, VerticalAlignment.Bottom));
      }1.2

重写 ArrangeOverride
在派生类中重写时,为 FrameworkElement派生类定位子元素并确定大小:
protected override Visual GetVisualChild(int index);
protected override int VisualChildrenCount{get;}
protected override Size ArrangeOverride(Size finalSize)
      {
            double offset = THUMB_SIZE / 2;
            Size sz = new Size(THUMB_SIZE, THUMB_SIZE);
            lc.Arrange(new Rect(new Point(-offset, AdornedElement.RenderSize.Height / 2 - offset), sz));
            tl.Arrange(new Rect(new Point(-offset, -offset), sz));
            tc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - offset, -offset), sz));
            tr.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width - offset, -offset), sz));
            rc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width - offset, AdornedElement.RenderSize.Height / 2 - offset), sz));
            br.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width - offset, AdornedElement.RenderSize.Height - offset), sz));
            bc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - offset, AdornedElement.RenderSize.Height - offset), sz));
            bl.Arrange(new Rect(new Point(-offset, AdornedElement.RenderSize.Height - offset), sz));
            return finalSize;
      }   1.3

ScreenCutAdorner
完整代码如下:
using System;using System.Windows;using System.Windows.Controls;using System.Windows.Controls.Primitives;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Shapes;namespace WPFDevelopers.Controls{public class ScreenCutAdorner : Adorner{      const double THUMB_SIZE = 15;
      const double MINIMAL_SIZE = 20;
      Thumb lc, tl, tc, tr, rc, br, bc, bl;
      VisualCollection visCollec;
      public ScreenCutAdorner(UIElement adorned): base(adorned)
      {
          visCollec = new VisualCollection(this);
          visCollec.Add(lc = GetResizeThumb(Cursors.SizeWE, HorizontalAlignment.Left, VerticalAlignment.Center));
          visCollec.Add(tl = GetResizeThumb(Cursors.SizeNWSE, HorizontalAlignment.Left, VerticalAlignment.Top));
          visCollec.Add(tc = GetResizeThumb(Cursors.SizeNS, HorizontalAlignment.Center, VerticalAlignment.Top));
          visCollec.Add(tr = GetResizeThumb(Cursors.SizeNESW, HorizontalAlignment.Right, VerticalAlignment.Top));
          visCollec.Add(rc = GetResizeThumb(Cursors.SizeWE, HorizontalAlignment.Right, VerticalAlignment.Center));
          visCollec.Add(br = GetResizeThumb(Cursors.SizeNWSE, HorizontalAlignment.Right, VerticalAlignment.Bottom));
          visCollec.Add(bc = GetResizeThumb(Cursors.SizeNS, HorizontalAlignment.Center, VerticalAlignment.Bottom));
          visCollec.Add(bl = GetResizeThumb(Cursors.SizeNESW, HorizontalAlignment.Left, VerticalAlignment.Bottom));
      }      protected override Size ArrangeOverride(Size finalSize)      {          double offset = THUMB_SIZE / 2;          Size sz = new Size(THUMB_SIZE, THUMB_SIZE);          lc.Arrange(new Rect(new Point(-offset, AdornedElement.RenderSize.Height / 2 - offset), sz));          tl.Arrange(new Rect(new Point(-offset, -offset), sz));          tc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - offset, -offset), sz));          tr.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width - offset, -offset), sz));          rc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width - offset, AdornedElement.RenderSize.Height / 2 - offset), sz));          br.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width - offset, AdornedElement.RenderSize.Height - offset), sz));          bc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - offset, AdornedElement.RenderSize.Height - offset), sz));          bl.Arrange(new Rect(new Point(-offset, AdornedElement.RenderSize.Height - offset), sz));          return finalSize;      }      void Resize(FrameworkElement ff)      {          if (Double.IsNaN(ff.Width))            ff.Width = ff.RenderSize.Width;          if (Double.IsNaN(ff.Height))            ff.Height = ff.RenderSize.Height;      }      Thumb GetResizeThumb(Cursor cur, HorizontalAlignment hor, VerticalAlignment ver)      {          var thumb = new Thumb()          {            Width = THUMB_SIZE,            Height = THUMB_SIZE,            HorizontalAlignment = hor,            VerticalAlignment = ver,            Cursor = cur,            Template = new ControlTemplate(typeof(Thumb))            {                  VisualTree = GetFactory(new SolidColorBrush(Colors.White))            }          };          thumb.DragDelta += (s, e) =>          {            var element = AdornedElement as FrameworkElement;            if (element == null)                  return;            Resize(element);            switch (thumb.VerticalAlignment)            {                  case VerticalAlignment.Bottom:                      if (element.Height + e.VerticalChange > MINIMAL_SIZE)                        element.Height += e.VerticalChange;                      break;                  case VerticalAlignment.Top:                      if (element.Height - e.VerticalChange > MINIMAL_SIZE)                      {                        element.Height -= e.VerticalChange;                        Canvas.SetTop(element, Canvas.GetTop(element) + e.VerticalChange);                      }                      break;            }            switch (thumb.HorizontalAlignment)            {                  case HorizontalAlignment.Left:                      if (element.Width - e.HorizontalChange > MINIMAL_SIZE)                      {                        element.Width -= e.HorizontalChange;                        Canvas.SetLeft(element, Canvas.GetLeft(element) + e.HorizontalChange);                      }                      break;                  case HorizontalAlignment.Right:                      if (element.Width + e.HorizontalChange > MINIMAL_SIZE)                        element.Width += e.HorizontalChange;                      break;            }            e.Handled = true;          };          return thumb;      }      FrameworkElementFactory GetFactory(Brush back)      {          var fef = new FrameworkElementFactory(typeof(Ellipse));          fef.SetValue(Ellipse.FillProperty, back);          fef.SetValue(Ellipse.StrokeProperty, DrawingContextHelper.Brush);          fef.SetValue(Ellipse.StrokeThicknessProperty, (double)2);          return fef;      }      protected override Visual GetVisualChild(int index)      {          return visCollec;      }      protected override int VisualChildrenCount      {          get          {            return visCollec.Count;          }      }}}二、找到之前的自定义控件ScreenCut修改为Border创建装饰器层
AdornerLayer.GetAdornerLayer(_border);
接着给装饰层添加装饰器 adornerLayer.Add(screenCutAdorner);


2.1 ScreenCut监听Border的大小变化修改四个矩形的大小与位置:
private void _border_SizeChanged(object sender, SizeChangedEventArgs e)
   {
         var left = Canvas.GetLeft(_border);
         var top = Canvas.GetTop(_border);
         var beignPoint = new Point(left, top);
         var endPoint = new Point(left + _border.ActualWidth, top + _border.ActualHeight);
         rect = new Rect(beignPoint, endPoint);
         pointStart = beignPoint;
         MoveAllRectangle(endPoint);
         WrapPanelPosition();
   }2.2 ScreenCut完整代码如下:
using Microsoft.Win32;using System;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Interop;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;using WPFDevelopers.Helpers;namespace WPFDevelopers.Controls{    public enum ScreenCutMouseType    {      Default,      DrawMouse,      MoveMouse,    }                                            public class ScreenCut : Window    {      private const string CanvasTemplateName = "PART_Canvas";      private const string RectangleLeftTemplateName = "PART_RectangleLeft";      private const string RectangleTopTemplateName = "PART_RectangleTop";      private const string RectangleRightTemplateName = "PART_RectangleRight";      private const string RectangleBottomTemplateName = "PART_RectangleBottom";      private const string BorderTemplateName = "PART_Border";      private const string WrapPanelTemplateName = "PART_WrapPanel";      private const string ButtonSaveTemplateName = "PART_ButtonSave";      private const string ButtonCancelTemplateName = "PART_ButtonCancel";      private const string ButtonCompleteTemplateName = "PART_ButtonComplete";      private Canvas _canvas;      private Rectangle _rectangleLeft, _rectangleTop, _rectangleRight, _rectangleBottom;      private Border _border;      private WrapPanel _wrapPanel;      private Button _buttonSave, _buttonCancel, _buttonComplete;      private Rect rect;      private Point pointStart, pointEnd;      private bool isMouseUp = false;      private Win32ApiHelper.DeskTopSize size;      private ScreenCutMouseType screenCutMouseType = ScreenCutMouseType.Default;      private AdornerLayer adornerLayer;      private ScreenCutAdorner screenCutAdorner;      static ScreenCut()      {            DefaultStyleKeyProperty.OverrideMetadata(typeof(ScreenCut), new FrameworkPropertyMetadata(typeof(ScreenCut)));      }      public override void OnApplyTemplate()      {            base.OnApplyTemplate();            _canvas = GetTemplateChild(CanvasTemplateName) as Canvas;            _rectangleLeft = GetTemplateChild(RectangleLeftTemplateName) as Rectangle;            _rectangleTop = GetTemplateChild(RectangleTopTemplateName) as Rectangle;            _rectangleRight = GetTemplateChild(RectangleRightTemplateName) as Rectangle;            _rectangleBottom = GetTemplateChild(RectangleBottomTemplateName) as Rectangle;            _border = GetTemplateChild(BorderTemplateName) as Border;            _border.MouseLeftButtonDown += _border_MouseLeftButtonDown;                     _wrapPanel = GetTemplateChild(WrapPanelTemplateName) as WrapPanel;            _buttonSave = GetTemplateChild(ButtonSaveTemplateName) as Button;            if (_buttonSave != null)                _buttonSave.Click += _buttonSave_Click;            _buttonCancel = GetTemplateChild(ButtonCancelTemplateName) as Button;            if (_buttonCancel != null)                _buttonCancel.Click += _buttonCancel_Click;            _buttonComplete = GetTemplateChild(ButtonCompleteTemplateName) as Button;            if (_buttonComplete != null)                _buttonComplete.Click += _buttonComplete_Click;            _canvas.Background = new ImageBrush(Capture());            _rectangleLeft.Width = _canvas.Width;            _rectangleLeft.Height = _canvas.Height;                  }      public ScreenCut()      {            Loaded += (s,e)=>             {                adornerLayer = AdornerLayer.GetAdornerLayer(_border);                screenCutAdorner = new ScreenCutAdorner(_border);                adornerLayer.Add(screenCutAdorner);                _border.SizeChanged += _border_SizeChanged;            };      }      private void _border_SizeChanged(object sender, SizeChangedEventArgs e)      {            var left = Canvas.GetLeft(_border);            var top = Canvas.GetTop(_border);            var beignPoint = new Point(left, top);            var endPoint = new Point(left + _border.ActualWidth, top + _border.ActualHeight);            rect = new Rect(beignPoint, endPoint);            pointStart = beignPoint;            MoveAllRectangle(endPoint);            WrapPanelPosition();      }      private void _border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)      {            if(screenCutMouseType == ScreenCutMouseType.Default)                screenCutMouseType = ScreenCutMouseType.MoveMouse;                        }      private void _buttonSave_Click(object sender, RoutedEventArgs e)      {            SaveFileDialog dlg = new SaveFileDialog();            dlg.FileName = $"WPFDevelopers{DateTime.Now.ToString("yyyyMMddHHmmss")}.jpg";            dlg.DefaultExt = ".jpg";            dlg.Filter = "image file|*.jpg";            if (dlg.ShowDialog() == true)            {                BitmapEncoder pngEncoder = new PngBitmapEncoder();                pngEncoder.Frames.Add(BitmapFrame.Create(CutBitmap()));                using (var fs = System.IO.File.OpenWrite(dlg.FileName))                {                  pngEncoder.Save(fs);                  fs.Dispose();                  fs.Close();                }            }            Close();      }      private void _buttonComplete_Click(object sender, RoutedEventArgs e)      {            Clipboard.SetImage(CutBitmap());            Close();      }      CroppedBitmap CutBitmap()      {            _border.Visibility = Visibility.Collapsed;            _rectangleLeft.Visibility = Visibility.Collapsed;            _rectangleTop.Visibility = Visibility.Collapsed;            _rectangleRight.Visibility = Visibility.Collapsed;            _rectangleBottom.Visibility = Visibility.Collapsed;            var renderTargetBitmap = new RenderTargetBitmap((int)_canvas.Width,(int)_canvas.Height, 96d, 96d, System.Windows.Media.PixelFormats.Default);            renderTargetBitmap.Render(_canvas);            return new CroppedBitmap(renderTargetBitmap, new Int32Rect((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height));      }      private void _buttonCancel_Click(object sender, RoutedEventArgs e)      {            Close();      }      protected override void OnPreviewKeyDown(KeyEventArgs e)      {            if (e.Key == Key.Escape)                Close();      }      protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)      {            pointStart = e.GetPosition(_canvas);            if (!isMouseUp)            {                screenCutMouseType = ScreenCutMouseType.DrawMouse;                _wrapPanel.Visibility = Visibility.Hidden;                pointEnd = pointStart;                rect = new Rect(pointStart, pointEnd);            }      }      protected override void OnPreviewMouseMove(MouseEventArgs e)      {            if (e.LeftButton == MouseButtonState.Pressed)            {                var current = e.GetPosition(_canvas);                switch (screenCutMouseType)                {                  case ScreenCutMouseType.DrawMouse:                        MoveAllRectangle(current);                        break;                                    case ScreenCutMouseType.MoveMouse:                        MoveRect(current);                        break;                  default:                        break;                }            }      }      void MoveRect(Point current)      {            if (current != pointStart)            {                Console.WriteLine($"current:{current}");                Console.WriteLine($"pointStart:{pointStart}");                var vector = Point.Subtract(current, pointStart);                var left = Canvas.GetLeft(_border) + vector.X;                var top = Canvas.GetTop(_border) + vector.Y;                Console.WriteLine($"left:{left}");                if (left = _canvas.ActualHeight)                  top = _canvas.ActualHeight - _border.ActualHeight;                pointStart = current;                Canvas.SetLeft(_border, left);                Canvas.SetTop(_border, top);                rect = new Rect(new Point(left, top), new Point(left + _border.Width, top + _border.Height));                _rectangleLeft.Height = _canvas.ActualHeight;                _rectangleLeft.Width = left = _canvas.ActualWidth ? _canvas.ActualWidth : left;                Canvas.SetLeft(_rectangleTop, _rectangleLeft.Width);                _rectangleTop.Height = top = _canvas.ActualHeight ? _canvas.ActualHeight : top;                Canvas.SetLeft(_rectangleRight, left + _border.Width);                var wRight = _canvas.ActualWidth - (_border.Width + _rectangleLeft.Width);                _rectangleRight.Width = wRight
页: [1]
查看完整版本: WPF开发之ScreenCut截图改变大小