专栏首页DotNet程序园采用WPF开发截图程序,so easy!

采用WPF开发截图程序,so easy!

前言 QQ、微信截图功能已很强大了,似乎没必要在开发一个截图程序了。但是有时QQ热键就是被占用,不能快速的开启截屏;有时,天天挂着QQ,领导也不乐意。既然是程序员,就要自己开发截屏工具,功能随心所欲,岂不快哉。

再强调一点:工具就是生产力!没有掌握WPF之前,我是不会开发这么一个程序的,如果采用MFC、winform框架,工作量是相当的大,开发出来的效果肯定也比较low。本人用WPF,花了一天多的功夫,开发了这个小程序。程序的定位就功能简单,平时工作不碍事,用着的时候,一键截图!

为了不影响视觉, 程序主界面非常小。程序会在所有界面最前端展示。

有两个按钮1)“快捷截图”:截图后,立即将截图复制到剪切板。2)“截图+编辑”:截图后,可以在图上标注箭头和文字。

正在截图时,效果:

截图后,可编辑:

新增保留历史记录功能,选中历史记录,复制到剪切板。

看似简单,对开发技巧要求很高。内行看门道!

开发思路

常言道:看到的不一定是真实的。开发也要这样。程序叫截屏,你不要一股劲想着怎么截取别的窗口图案,肯定很费劲!思虑就是掩人耳目:先将整个屏幕复制,放到自己程序窗体中,窗体最大化,覆盖整个屏幕!用户看到还是整个屏幕,但是整个屏幕已被偷梁换柱!此后,你所有的操作都是在自己窗体上处理,当然可以随心所欲了!

截取整个屏幕

       public Bitmap GetScreenSnapshot()
        {
            System.Drawing.Rectangle rc = SystemInformation.VirtualScreen;
            var bitmap = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            using (Graphics memoryGrahics = Graphics.FromImage(bitmap))
            {
                memoryGrahics.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);
            }

            return bitmap;
        }

创建全屏窗体

注意窗体属性,这样才能全面覆盖整个屏幕。

图层布局

这个很有技巧!为了实现非截图区域阴影效果,费了一番心机!即使这样,感觉也比winform用起来得心应手!

注:我不是一直贬低winform,但是要承认,这两个东西不是一个时代产物。wpf设计思路比winform先进很多。只是wpf新概念多,用的人少,开发起来常常蒙圈!经过一段迷茫期,前途就会光明了!

窗口的布局,不多说了!直接上代码。我对代码做了注释!

<Window.Resources>
        <ControlTemplate x:Key="templatePushButton" TargetType="RadioButton">
            <Border x:Name="Part_Border" BorderThickness="1" BorderBrush="Gray" 
                                        Background="{TemplateBinding Background}"
                                        Margin="{TemplateBinding Margin}"
                                        Padding="{TemplateBinding Padding}">
                <ContentPresenter></ContentPresenter>
            </Border>

            <ControlTemplate.Triggers >
                <Trigger Property="IsChecked" Value="True">
                    <Setter  TargetName="Part_Border"  Property="BorderBrush" Value="Blue"></Setter>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter  TargetName="Part_Border"  Property="Background" Value="#FFb2dff9"></Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

        <Style x:Key="stylePushButton" TargetType="RadioButton">
            <Setter Property="VerticalAlignment" Value="Center"></Setter>
            <Setter Property="Padding" Value="8,5,8,5"></Setter>
            <Setter Property="Template" Value="{StaticResource templatePushButton}"></Setter>
</Style>

    </Window.Resources>
    <Grid Background="Green" >
        <!-- 整个屏幕图像 -->
        <Image x:Name="imgScreen"
               MouseDown="ImgScreen_MouseDown"
               MouseUp="ImgScreen_MouseUp"
               Stretch="None"           
               MouseMove="ImgScreen_MouseMove">
        </Image>

        <!-- 覆盖一层黑色,半透明状 -->
        <Grid x:Name="gridCover" Visibility="Collapsed" Background="Black" Opacity="0.5">
        </Grid>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
            </Grid.RowDefinitions>
            <!-- 前面覆盖了一层黑色,但是截取的图像不能覆盖,只能在这里再显示截取图像 -->
            <Grid x:Name="gridCutImg"  
                  MouseDown="ImgCut_MouseDown" 
                  MouseMove="ImgCut_MouseMove"
                   MouseUp="ImgCut_MouseUp">
                <Image x:Name="imgCut" Grid.RowSpan="3" Stretch="None"                                                                    
                   HorizontalAlignment="Left" VerticalAlignment="Top"></Image>
                <!--用来画箭头和文字-->
                <Canvas x:Name="canvasEdit"  HorizontalAlignment='Left'
                        VerticalAlignment="Top"  Background="Transparent">
                    
                </Canvas>
            </Grid>

            <!--显示提示信息-->
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
                           FontSize="22" Foreground="Yellow"
                          Opacity="0.8" >滑动鼠标开始截屏 截图保存到剪切板 按ESC键退出</TextBlock>

            <StackPanel  Grid.RowSpan="3">
                <Grid >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto"></ColumnDefinition>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                </Grid>
                <!--截图指示框-->
                <Border x:Name="borderSelect" 
                HorizontalAlignment="Left" VerticalAlignment="Top"
                BorderThickness="1" BorderBrush="Red"></Border>
                <!--宽和高指示-->
                <Grid  HorizontalAlignment="Stretch" >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto"></ColumnDefinition>
                        <ColumnDefinition Width="auto"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <TextBlock x:Name="txtCutInfo"  Padding="8,5,8,5" 
                               HorizontalAlignment="Left"
                              Background="White" VerticalAlignment="Center">1</TextBlock>
                    <StackPanel x:Name="stackEdit" Orientation="Horizontal" HorizontalAlignment="Right" Grid.Column="1" Margin="2">
                        <RadioButton x:Name="radioArrow" GroupName="editType" Click="RadioArrow_Click" Foreground="Black" Padding="10,5,10,5" Style="{StaticResource stylePushButton}">↑</RadioButton>
                        <RadioButton  x:Name="radioText"  GroupName="editType" Click="RadioText_Click" Foreground="Black" Style="{StaticResource stylePushButton}">文</RadioButton>
                        <RadioButton  x:Name="radioClose" Click="RadioClose_Click" Foreground="Red" Style="{StaticResource stylePushButton}">X</RadioButton>
                    </StackPanel>
                </Grid>

            </StackPanel>

        </Grid>
    </Grid>

当鼠标移动时,不断的计算选中区域,设置borderSelect属性。

 private void ImgScreen_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_isMouseDown)
                return;

            gridCover.Visibility = Visibility.Visible;

            //计算鼠标选中区域
            Point currentPoint = e.GetPosition(imgScreen);
            Point borderPoint = e.GetPosition(borderSelect);

            double xDelta = xDelta_BoderToImgScreen;
            double yDelta = yDelta_BoderToImgScreen;

            _rectImgCut = ImageHelper.ToRect(currentPoint, _startPoint);

            Rect rectBoderCut = ImageHelper.ToRect(new Point(currentPoint.X + xDelta, currentPoint.Y + yDelta),
                new Point(_startPoint.X + xDelta, _startPoint.Y + yDelta));

            //设置方框位置和大小
            Thickness thickness = new Thickness(rectBoderCut.Left, rectBoderCut.Top, 0, 0);
            borderSelect.SetValue(FrameworkElement.MarginProperty, thickness);
            imgCut.SetValue(FrameworkElement.MarginProperty, thickness);

            thickness = new Thickness(rectBoderCut.Left, 3, 0, 0);
            txtCutInfo.SetValue(FrameworkElement.MarginProperty, thickness);

            borderSelect.Width = Math.Abs(_startPoint.X - currentPoint.X);
            borderSelect.Height = Math.Abs(_startPoint.Y - currentPoint.Y);
            borderSelect.Visibility = Visibility.Visible;

            //为了防止整个图 变暗,鼠标选中区域图像抠图,再在上层图像上显示
            imgCut.Source = GetBitmapCut();

            Int32Rect imgDestRect = GetCutRect();
            txtCutInfo.Text = string.Format($"宽:{imgDestRect.Width} 高:{imgDestRect.Height}");
        }

到此,程序主要逻辑处理完毕!

本文分享自微信公众号 - DotNet程序园(dotnetblog),作者:yuanchenhui

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 记一次神奇的sql查询经历,group by慢查询优化

    现网出现慢查询,在500万数量级的情况下,单表查询速度在30多秒,需要对sql进行优化,sql如下:

    梁规晓
  • .net core 认证与授权(一)

    .net core web并不是一个非常新的架构,很多文章提及到认证与授权这个过程,但是一般都会提及到里面的方法怎么用的,而不是模拟一个怎样的过程,所以我打算记...

    梁规晓
  • Asp.Net Core 轻松学-利用日志监视进行服务遥测

        在 Net Core 2.2 中,官方文档表示,对 EventListener 这个日志监视类的内容进行了扩充,同时赋予了跟踪 CoreCLR 事件的权...

    梁规晓
  • C语言实现顺序栈

    源代码已上传到 GitHub Data-Structure-of-C,欢迎大家下载 C语言实现数据结构

    忆想不到的晖
  • 推荐系统那点事 —— 基于Spark MLlib的特征选择

    在机器学习中,一般都会按照下面几个步骤:特征提取、数据预处理、特征选择、模型训练、检验优化。那么特征的选择就很关键了,一般模型最后效果的好坏往往都是跟特征的选...

    用户1154259
  • 美团点评云真机平台实践

    随着美团点评业务越来越多,研发团队越来越庞大,对测试手机的需求显著增长。这对公司来说是一笔不小的开支,但现有测试手机资源分配不均,利用率也非常有限,导致各个团队...

    美团技术团队
  • 美团点评云真机平台实践

    随着美团点评业务越来越多,研发团队越来越庞大,对测试手机的需求显著增长。这对公司来说是一笔不小的开支,但现有测试手机资源分配不均,利用率也非常有限,导致各个团队...

    测试开发社区
  • 这个感恩节,滴滴用讲故事的方式温暖你我

    感恩节虽然是西方舶来品,但与中华文化的“感恩”不谋而合。一向以温情的营销著称的滴滴快车自然没有忘记这样的日子,在感恩节发起了一次通过视频、微信、线下多渠道互动传...

    曾响铃
  • Hive启用Sentry后如何限制用户提交Yarn资源池

    在前面Fayson介绍了《如何使用Cloudera Manager设置使用YARN队列的ACL》和《如何在Cloudera Manager中配置Yarn放置规则...

    Fayson
  • centOS7中配置安装nodejs和node、npm、cnpm

    前提是在CentOS7中安装配置好node和环境变量,关于nodejs可以从官网http://nodejs.cn/download/下载,选择https://n...

    ccf19881030

扫码关注云+社区

领取腾讯云代金券