WPF 实现截屏控件之移动(仿微信)

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF 实现截屏控件之移动(仿微信)相关的知识,希望对你有一定的参考价值。

WPF开发者QQ群

此群已满340500857 ,请加新群458041663

       由于微信群人数太多入群请添加小编微信号

 yanjinhuawechatW_Feng_aiQ 邀请入群

 需备注WPF开发者 

 接着上一篇,兼容屏幕缩放问题。

01

代码如下

一、创建ScreenCut.xaml代码如下。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controls="clr-namespace:WPFDevelopers.Controls">
    
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Basic/ControlBasic.xaml"/>
        <ResourceDictionary Source="ButtonsStyles.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <Style x:Key="RectangleStyle" TargetType="x:Type Rectangle">
        <Setter Property="Fill" Value="DynamicResource BlackSolidColorBrush"/>
        <Setter Property="Opacity" Value=".5"/>
    </Style>
    
    <Style TargetType="x:Type controls:ScreenCut" BasedOn="StaticResource ControlBasicStyle">
        <Setter Property="WindowState" Value="Maximized"/>
        <Setter Property="WindowStyle" Value="None"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="x:Type controls:ScreenCut">
                    <Canvas x:Name="PART_Canvas"
            Width="Binding Source=x:Static SystemParameters.PrimaryScreenWidth"
            Height="Binding Source=x:Static SystemParameters.PrimaryScreenHeight">
                        <Rectangle x:Name="PART_RectangleLeft" Style="DynamicResource RectangleStyle"/>
                        <Rectangle x:Name="PART_RectangleTop" Style="DynamicResource RectangleStyle"/>
                        <Rectangle x:Name="PART_RectangleRight" Style="DynamicResource RectangleStyle"/>
                        <Rectangle x:Name="PART_RectangleBottom" Style="DynamicResource RectangleStyle"/>
                        <Border x:Name="PART_Border" BorderBrush="DynamicResource PrimaryNormalSolidColorBrush" 
                                BorderThickness="1" Background="Transparent" Cursor="SizeAll"/>
                        <WrapPanel x:Name="PART_WrapPanel" 
                                   Visibility="Hidden" Panel.ZIndex="99"
                                   Height="38" Background="DynamicResource WhiteSolidColorBrush"
                                   VerticalAlignment="Center">
                            <Button x:Name="PART_ButtonSave" Style="DynamicResource PathButton"
                                    ToolTip="保存" Margin="10,0,0,0">
                                <Button.Content>
                                    <Path Fill="DynamicResource InfoPressedSolidColorBrush" 
                                          Width="18" Height="18" Stretch="Fill" 
                                          Data="StaticResource PathSave"/>
                                </Button.Content>
                            </Button>
                            <Button x:Name="PART_ButtonCancel" Style="DynamicResource PathButton"
                                    ToolTip="取消">
                                <Button.Content>
                                    <Path Fill="DynamicResource DangerPressedSolidColorBrush" 
                                          Width="14" Height="14" Stretch="Fill" 
                                          Data="StaticResource PathCancel"/>
                                </Button.Content>
                            </Button>
                            <Button x:Name="PART_ButtonComplete"  Style="DynamicResource PathButton"
                                    ToolTip="完成" Margin="0,0,10,0">
                                <Button.Content>
                                    <Path Fill="DynamicResource SuccessPressedSolidColorBrush"  
                                          Width="20" Height="15" Stretch="Fill" 
                                          Data="StaticResource PathComplete"/>
                                </Button.Content>
                            </Button>
                        </WrapPanel>

                    </Canvas>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

二、创建ScreenCut.cs代码如下。

using Microsoft.Win32;
using System;
using System.Windows;
using System.Windows.Controls;
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

    [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;

        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;
          
            _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;
        


        private void _buttonSave_Click(object sender, RoutedEventArgs e)
        
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.FileName = $"WPFDevelopersDateTime.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)
            
                _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);
                if (!isMouseUp)
                
                    MoveAllRectangle(current);
                

                else
                
                    if (current != pointStart)
                    
                        var vector = Point.Subtract(current, pointStart);
                        var left = Canvas.GetLeft(_border) + vector.X;
                        var top = Canvas.GetTop(_border) + vector.Y;
                        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.Width = left <= 0 ? 0 : left >= _canvas.ActualWidth ? _canvas.ActualWidth : left;
                        _rectangleLeft.Height = _canvas.ActualHeight;

                        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)
        
            _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 - 4;
            else
                y = Canvas.GetTop(_border) + _border.ActualHeight + 4;
            Canvas.SetTop(_wrapPanel, y);
            isMouseUp = true;
        

        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;
        

    

三、新建WINAPI DLL Imports.cs代码如下。

#region WINAPI DLL Imports

        [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

        [DllImport("gdi32.dll", SetLastError = true)]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateBitmap(int nWidth, int nHeight, uint cPlanes, uint cBitsPerPel, IntPtr lpvBits);

        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);


        [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
        public static extern IntPtr DeleteDC(IntPtr hDc);


        public const int SM_CXSCREEN = 0;

        public const int SM_CYSCREEN = 1;

        [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
        public static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
        public static extern int GetSystemMetrics(int abc);

        [DllImport("user32.dll", EntryPoint = "GetWindowDC")]
        public static extern IntPtr GetWindowDC(Int32 ptr);

        public struct DeskTopSize
        
            public int cx;
            public int cy;
        

        public enum TernaryRasterOperations : uint
        
            /// <summary>dest = source</summary>
            SRCCOPY = 0x00CC0020,
            /// <summary>dest = source OR dest</summary>
            SRCPAINT = 0x00EE0086,
            /// <summary>dest = source AND dest</summary>
            SRCAND = 0x008800C6,
            /// <summary>dest = source XOR dest</summary>
            SRCINVERT = 0x00660046,
            /// <summary>dest = source AND (NOT dest)</summary>
            SRCERASE = 0x00440328,
            /// <summary>dest = (NOT source)</summary>
            NOTSRCCOPY = 0x00330008,
            /// <summary>dest = (NOT src) AND (NOT dest)</summary>
            NOTSRCERASE = 0x001100A6,
            /// <summary>dest = (source AND pattern)</summary>
            MERGECOPY = 0x00C000CA,
            /// <summary>dest = (NOT source) OR dest</summary>
            MERGEPAINT = 0x00BB0226,
            /// <summary>dest = pattern</summary>
            PATCOPY = 0x00F00021,
            /// <summary>dest = DPSnoo</summary>
            PATPAINT = 0x00FB0A09,
            /// <summary>dest = pattern XOR dest</summary>
            PATINVERT = 0x005A0049,
            /// <summary>dest = (NOT dest)</summary>
            DSTINVERT = 0x00550009,
            /// <summary>dest = BLACK</summary>
            BLACKNESS = 0x00000042,
            /// <summary>dest = WHITE</summary>
            WHITENESS = 0x00FF0062
        

        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

        #endregion
    

四、新建ScreenCutExample.xaml.cs代码如下。

var screenCut = new ScreenCut();
            screenCut.ShowDialog();
            //screenCut.Show();

02


效果预览

鸣谢素材提供者 - 吴锋

源码地址如下

Github:https://github.com/WPFDevelopersOrg

Gitee:https://gitee.com/WPFDevelopersOrg

WPF开发者QQ群: 340500857 | 458041663

Github:https://github.com/WPFDevelopersOrg

出处:https://www.cnblogs.com/yanjinhua

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载请著名作者 出处 https://github.com/WPFDevelopersOrg

扫一扫关注我们,

更多知识早知道!

点击阅读原文可跳转至源代码

以上是关于WPF 实现截屏控件之移动(仿微信)的主要内容,如果未能解决你的问题,请参考以下文章

WPF实现截屏(仿微信)

Android 高仿微信头像截取 打造不一样的自己定义控件

android 仿微信demo————微信消息界面实现(移动端)

android 仿微信demo————微信消息界面实现(移动端)

android 仿微信demo————注册功能实现(移动端)

android 仿微信demo————登录功能实现(移动端)