WPF开发之ScreenCut截图改变大小

  • WPF开发之ScreenCut截图改变大小已关闭评论
  • 189 次浏览
  • A+
所属分类:.NET技术
摘要

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


前言

接着上周写的截图控件继续更新 缩放操作

1.WPF实现截屏「仿微信」
2.WPF 实现截屏控件之移动(二)「仿微信」

正文

WPF开发之ScreenCut截图改变大小

实现拉伸放大或缩小缩放操作需在矩形四个方向绘制8Thumb,这里有两种方式
1)可以自行在XAML中硬编写8Thumb
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[index];       }        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,      }     [TemplatePart(Name = CanvasTemplateName, Type = typeof(Canvas))]     [TemplatePart(Name = RectangleLeftTemplateName, Type = typeof(Rectangle))]     [TemplatePart(Name = RectangleTopTemplateName, Type = typeof(Rectangle))]     [TemplatePart(Name = RectangleRightTemplateName, Type = typeof(Rectangle))]     [TemplatePart(Name = RectangleBottomTemplateName, Type = typeof(Rectangle))]     [TemplatePart(Name = BorderTemplateName, Type = typeof(Border))]     [TemplatePart(Name = WrapPanelTemplateName, Type = typeof(WrapPanel))]     [TemplatePart(Name = ButtonSaveTemplateName, Type = typeof(Button))]     [TemplatePart(Name = ButtonCancelTemplateName, Type = typeof(Button))]     [TemplatePart(Name = ButtonCompleteTemplateName, Type = typeof(Button))]     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 <= 0)                     left = 0;                 if (top <= 0)                     top = 0;                 if (left + _border.Width >= _canvas.ActualWidth)                     left = _canvas.ActualWidth - _border.ActualWidth;                 if (top + _border.Height >= _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 <= 0 ? 0 : left >= _canvas.ActualWidth ? _canvas.ActualWidth : left;                   Canvas.SetLeft(_rectangleTop, _rectangleLeft.Width);                 _rectangleTop.Height = top <= 0 ? 0 : top >= _canvas.ActualHeight ? _canvas.ActualHeight : top;                  Canvas.SetLeft(_rectangleRight, left + _border.Width);                 var wRight = _canvas.ActualWidth - (_border.Width + _rectangleLeft.Width);                 _rectangleRight.Width = wRight <= 0 ? 0 : wRight;                 _rectangleRight.Height = _canvas.ActualHeight;                  Canvas.SetLeft(_rectangleBottom, _rectangleLeft.Width);                 Canvas.SetTop(_rectangleBottom, top + _border.Height);                 _rectangleBottom.Width = _border.Width;                 var hBottom = _canvas.ActualHeight - (top + _border.Height);                 _rectangleBottom.Height = hBottom <= 0 ? 0 : hBottom;             }         }          protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)         {             WrapPanelPosition();             isMouseUp = true;             if (screenCutMouseType != ScreenCutMouseType.Default)                 screenCutMouseType = ScreenCutMouseType.Default;         }         void WrapPanelPosition()         {             _wrapPanel.Visibility = Visibility.Visible;             Canvas.SetLeft(_wrapPanel, rect.X + rect.Width - _wrapPanel.ActualWidth);             var y = Canvas.GetTop(_border) + _border.ActualHeight + _wrapPanel.ActualHeight;             if (y > _canvas.ActualHeight)                 y = Canvas.GetTop(_border) - _wrapPanel.ActualHeight - 8;             else                 y = Canvas.GetTop(_border) + _border.ActualHeight + 8;             Canvas.SetTop(_wrapPanel, y);         }         void MoveAllRectangle(Point current)         {             pointEnd = current;             rect = new Rect(pointStart, pointEnd);             _rectangleLeft.Width = rect.X;             _rectangleLeft.Height = _canvas.Height;              Canvas.SetLeft(_rectangleTop, _rectangleLeft.Width);             _rectangleTop.Width = rect.Width;             double h = 0.0;             if (current.Y < pointStart.Y)                 h = current.Y;             else                 h = current.Y - rect.Height;              _rectangleTop.Height = h;              Canvas.SetLeft(_rectangleRight, _rectangleLeft.Width + rect.Width);             _rectangleRight.Width = _canvas.Width - (rect.Width + _rectangleLeft.Width);             _rectangleRight.Height = _canvas.Height;              Canvas.SetLeft(_rectangleBottom, _rectangleLeft.Width);             Canvas.SetTop(_rectangleBottom, rect.Height + _rectangleTop.Height);             _rectangleBottom.Width = rect.Width;             _rectangleBottom.Height = _canvas.Height - (rect.Height + _rectangleTop.Height);              _border.Height = rect.Height;             _border.Width = rect.Width;             Canvas.SetLeft(_border, rect.X);             Canvas.SetTop(_border, rect.Y);                      }                 BitmapSource Capture()         {              IntPtr hBitmap;             IntPtr hDC = Win32ApiHelper.GetDC(Win32ApiHelper.GetDesktopWindow());             IntPtr hMemDC = Win32ApiHelper.CreateCompatibleDC(hDC);             size.cx = Win32ApiHelper.GetSystemMetrics(0);             size.cy = Win32ApiHelper.GetSystemMetrics(1);             hBitmap = Win32ApiHelper.CreateCompatibleBitmap(hDC, size.cx, size.cy);             if (hBitmap != IntPtr.Zero)             {                 IntPtr hOld = (IntPtr)Win32ApiHelper.SelectObject(hMemDC, hBitmap);                 Win32ApiHelper.BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, Win32ApiHelper.TernaryRasterOperations.SRCCOPY);                 Win32ApiHelper.SelectObject(hMemDC, hOld);                 Win32ApiHelper.DeleteDC(hMemDC);                 Win32ApiHelper.ReleaseDC(Win32ApiHelper.GetDesktopWindow(), hDC);                 var bsource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());                 Win32ApiHelper.DeleteObject(hBitmap);                 GC.Collect();                 return bsource;             }             return null;         }      } }    

三、运行效果如下

WPF开发之ScreenCut截图改变大小