C# wpf利用Clip属性实现截屏框

wpf截屏系列

第一章 使用GDI+实现截屏
第二章 制作截屏框(本章)
______第一节 使用DockPanel制作截屏框
______第二节 利用Clip属性实现截屏框(本节)
第三章 实现截屏框热键截屏
第四章 实现截屏框实时截屏
第五章 使用ffmpeg命令行实现录屏


文章目录

  • wpf截屏系列
  • 前言
  • 一、实现步骤
    • 1、Clip穿透
    • 2、子控件同步Clip区域
    • 3、子控件实现拖动
    • 4、子控件实现拖动调整大小
    • 5、鼠标事件传递
  • 二、完整代码
    • 1、自行整合
    • 2、简化的实现
  • 三、效果预览
    • 1、矩形框
    • 2、圆形框
  • 总结


前言

第一节已经实现过截屏框,实现方法相对简单,也仅支持矩形框。最近使用wpf的clip时发现了一种用法,可以实现穿透效果。那显然我们基于clip也能实现截屏窗口,而且支持任意形状。


一、实现步骤

1、Clip穿透

使用GeometryGroup 且FillRule为EvenOdd就可以做出穿透的效果。

<Grid><Grid Width="200" Height="200" Background="SeaGreen" ><Grid.Clip><GeometryGroup  FillRule="EvenOdd"><!--底下的rect必须保持和容器大小一致--><RectangleGeometry  Rect="0 0 200 200" /><!--上层形状即为穿透区域--><RectangleGeometry x:Name="foreRect" Rect="0 0 200 200" RadiusX="100" RadiusY="100"/></GeometryGroup></Grid.Clip></Grid><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text= "Clip区域会穿透" Foreground="Black"></TextBlock>
</Grid>

在这里插入图片描述

2、子控件同步Clip区域

单纯Clip无法实现拖动和改变大小,尤其是改变大小直接通过鼠标事件结合clip也不好实现。比较简单的方法是定义一个子控件,子控件的位置和大小与Clip关联,这样只要实现子控件的拖动和调整大小功能就能控制Clip区域了。
通过LayoutUpdated事件就可以实时同步Clip区域。子控件就相当于截屏框。

<Border x:Name="clipBorder" LayoutUpdated="Border_LayoutUpdated">

下列代码的GetPosition是自定义拓展方法,自己实现获取控件坐标即可。

private void Border_LayoutUpdated(object sender, EventArgs e)
{//截屏框与上层clip rect保持一致foreRect.Rect = new Rect(clipBorder.GetPosition(), new Size(clipBorder.ActualWidth, clipBorder.ActualHeight));;
}

3、子控件实现拖动

参考《wpf拖动系列》,根据不同的容器选择不同实现(直接拷贝网页代码),此处使用第六章的功能简化实现。

<Border x:Name="clipBorder" ac:Move.IsDragMoveable="True" >

4、子控件实现拖动调整大小

参考《wpf拖动调整大小系列》,根据不同的容器选择不同实现(直接拷贝网页代码),此处使用第五章的功能简化实现。

<Border x:Name="clipBorder" ac:Resize.IsDragResizeable="True" >

5、鼠标事件传递

由于使用了Clip穿透,穿透区域的子控件是无法响应鼠标的,有幸的是穿透区域不会影响装饰层,所以我们需要在子控件里定义一个装饰器用于捕获鼠标消息。我们通过《wpf 附加属性实现界面上定义装饰器》简化装饰器的定义。

<Border x:Name="clipBorder"><!--截屏框的装饰层,用于捕获鼠标消息--><local:AdornerHelper.AdornerContent><Grid MouseDown="Grid_MouseDown" Background="Transparent"></Grid></local:AdornerHelper.AdornerContent>
</Border>
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{  //事件转移,触发拖动clipBorder.RaiseEvent(e);
}

二、完整代码

1、自行整合

通过上述步骤,将《wpf拖动系列》、《wpf拖动调整大小系列》、《wpf 附加属性实现界面上定义装饰器》网页上的代码(对应容器类型)整合到一起即可实现所有功能。

2、简化的实现

下列是使用wpf拖动系列第六章和wpf拖动调整大小系列第五章的实现,需要下载。
xaml

<Window x:Class="WpfClip.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfClip"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"WindowStyle="None"Background="Transparent"ResizeMode="NoResize"Topmost="True"WindowState="Maximized"Title="MainWindow" Height="450" Width="800"><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"   CaptionHeight="0"   /></WindowChrome.WindowChrome ><Grid x:Name="grid" Background="#80000000" LayoutUpdated="Grid_LayoutUpdated"><Grid.Clip><!--利用clip实现穿透--><GeometryGroup  FillRule="EvenOdd"><RectangleGeometry x:Name="backRect"/><!--实际的截屏形状--><RectangleGeometry x:Name="foreRect"  /></GeometryGroup></Grid.Clip><!--截屏框--><Border x:Name="clipBorder" Width="200" Height="200" ac:Move.IsDragMoveable="True" ac:Resize.IsResizeable="True" LayoutUpdated="Border_LayoutUpdated"><ac:Resize.ThumbsTemplate><ControlTemplate><Border Width="16" Height="16" Background=" Green"  BorderBrush="White" BorderThickness="2" CornerRadius="8"></Border></ControlTemplate></ac:Resize.ThumbsTemplate><ac:Resize.ThumbsPanel><ItemsPanelTemplate><Grid Margin="-8"></Grid></ItemsPanelTemplate></ac:Resize.ThumbsPanel><!--截屏框的装饰层,用于捕获鼠标消息--><local:AdornerHelper.AdornerContent><Grid MouseDown="Grid_MouseDown" Background="Transparent"></Grid></local:AdornerHelper.AdornerContent></Border></Grid>
</Window>

cs

using AC;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfClip
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void Grid_LayoutUpdated(object sender, EventArgs e){//确保底层clip rect与容器大小相同backRect.Rect=new Rect(0,0, grid.ActualWidth, grid.ActualHeight);}private void Border_LayoutUpdated(object sender, EventArgs e){//截屏框与上层clip rect保持一致foreRect.Rect = new Rect(clipBorder.GetPosition(), new Size(clipBorder.ActualWidth, clipBorder.ActualHeight));//圆形效果,去掉则是矩形foreRect.RadiusX = clipBorder.Width/2;foreRect.RadiusY= clipBorder.Height/2;}private void Grid_MouseDown(object sender, MouseButtonEventArgs e){  //事件转移,触发拖动clipBorder.RaiseEvent(e);}}internal class AdornerHelper{public static UIElement GetAdornerContent(DependencyObject obj){return (UIElement)obj.GetValue(AdornerContent);}public static void SetAdornerContent(DependencyObject obj, UIElement value){obj.SetValue(AdornerContent, value);}// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...public static readonly DependencyProperty AdornerContent =DependencyProperty.RegisterAttached("AdornerContent", typeof(UIElement), typeof(AdornerHelper), new PropertyMetadata(null, (d, e) =>{var c = d as FrameworkElement;if (c == null)return;var adronerContent = e.NewValue as UIElement;if (!c.IsLoaded){if (adronerContent != null){RoutedEventHandler l = null;l = (s, E) =>{var content = GetAdornerContent(c);if (content != null){var layer = AdornerLayer.GetAdornerLayer(c);if (layer == null)throw new Exception("获取控件装饰层失败,控件可能没有装饰层!");layer.Add(new NormalAdorner((UIElement)c, (UIElement)e.NewValue));}c.Loaded -= l;};c.Loaded += l;}}else{var layer = AdornerLayer.GetAdornerLayer(d as Visual);if (layer == null)throw new Exception("获取控件装饰层失败,控件可能没有装饰层!");if (e.OldValue != null){var adorners = layer.GetAdorners(c);foreach (var i in adorners){if (i is NormalAdorner){var na = i as NormalAdorner;if (na.Child == e.OldValue){layer.Remove(i);break;}}}}if (adronerContent != null){layer.Add(new NormalAdorner((UIElement)c, (UIElement)e.NewValue));}}}));class NormalAdorner : Adorner{UIElement _child;/// <summary>/// 构造方法/// </summary>/// <param name="adornedElement">被添加装饰器的元素</param>/// <param name="child">放到装饰器中的元素</param>public NormalAdorner(UIElement adornedElement, UIElement child) : base(adornedElement){_child = child;AddVisualChild(child);}public UIElement Child { get { return _child; } }protected override Visual GetVisualChild(int index){return _child;}protected override int VisualChildrenCount{get{return 1;}}protected override Size ArrangeOverride(Size finalSize){_child.Arrange(new Rect(new Point(0, 0), finalSize));return finalSize;}}}
}

三、效果预览

我们可以通过设置foreRect的类型改变形状,当然Border_LayoutUpdated的逻辑也要相应的修改。

1、矩形框

在这里插入图片描述

2、圆形框

在这里插入图片描述


总结

以上就是今天要讲的内容,有了之前的基础本文实现起来相对容易,当前目前的也是比较初步的功能,但灵活性是比DockPanel要好的,尤其是支持任意形状,这样就有利于后期划线截屏的实现了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640058.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript基础之JavaScript引入方式

JavaScript引入方式 JavaScript 程序不能独立运行&#xff0c;它需要被嵌入 HTML 中&#xff0c;然后浏览器才能执行 JavaScript 代码。通过 script 标签将 JavaScript 代码引入到 HTML 中&#xff0c;一般以下方式: 外部方式内部方式JavaScript元素事件通过JavaScript伪URL引…

网络数据传输过程

先验知识&#xff1a;OSI模型 OSI网络模型实际上是参考模型&#xff0c;在实际中并不使用&#xff0c;在网络出现问题的时候&#xff0c;可以从一个宏观的整体去分析和解决问题&#xff0c;而且搭建网络的时候并不需要划分为7层&#xff0c;当今互联网广泛使用的是TCP/IP网络模…

最短代码实现随机打乱数组各个元素的顺序

//最短代码实现随机打乱数组各个元素的顺序 randomSortArr(arr []) {return [...Array(arr.length)].map((v, i) > arr.splice(Math.round(Math.random() * (arr.length - 1)), 1)[0]); },

Unity 建造者模式(实例详解)

文章目录 说明实例1&#xff1a;构建游戏角色实例2&#xff1a;构建游戏场景实例3&#xff1a;构建UI界面 说明 在Unity中&#xff0c;建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它通过分离对象构建过程的复杂性&#xff0c;允许您以…

MySQL索引优化:深入理解索引下推原理与实践

随着MySQL的不断发展和升级&#xff0c;每个版本都为数据库性能和查询优化带来了新的特性。在MySQL 5.6中&#xff0c;引入了一个重要的优化特性——索引下推&#xff08;Index Condition Pushdown&#xff0c;简称ICP&#xff09;。ICP能够在某些查询场景下显著提高查询性能&a…

小程序学习-20

建议每次构建npm之前都先删除miniprogram_npm

Windows11家庭版上安装Hyper-V并导入虚拟机的方法

大纲 安装启用Hyper-V下载并安装启用 导入虚拟机代码地址 一般我们新买的电脑默认自带的是Windows家庭版。这个版本是没有Hyper-V的。如果安装自带Hyper-V的版本&#xff0c;则需要另外购买。但是我们还是有办法在Windows11的家庭版上安装和启用Hyper-V的。 安装启用Hyper-V …

前端模板字符串的使用

目录 1.说明 2.示例 3.总结 1.说明 模板字符串是用反引号&#xff08;&#xff09;分隔的字面量&#xff0c;允许多行字符串&#xff0c;带有嵌入表达式的字符串插值和一种带标签的模板的特殊结构。 是增强版的字符串&#xff0c;在进行字符串拼接时&#xff0c;可以拼接固…

11 python快速上手

函数进阶 函数进阶1.参数的补充1.1 参数内存地址相关【面试题】1.2 函数的返回值是内存地址1.3 参数的默认值【面试题】1.4 动态参数 2. 函数和函数名2.1 函数做元素2.2 函数名赋值2.3 函数名做参数和返回值 3.返回值和print4. 作用域4.1 函数为作用域4.2 全局和局部4.3 global…

在CentOS 7 中配置NFS服务器

目录 1、克隆两个虚拟机 2、安装 NFS 服务 3、NFS 服务使用 1、克隆两个虚拟机 nfs-servernfs-client&#xff08;修改ip地址&#xff09;[rootxnode1 ~]# cd /etc/sysconfig/network-scripts/[rootxnode1 network-scripts]# vi ifcfg-eno16777736 #修改内容如下 BOOTPROT…

【QT+QGIS跨平台编译】之二:【zlib+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、zlib介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、zlib介绍 zlib是一套通用的解压缩开源库&#xff0c;提供了内存&#xff08;in-memory&#xff09;压缩和解压函数。zlib是一套通用的解压缩开源库&#xff0c;提供了内存&#xff08;in-memory&am…

【小沐学GIS】基于C#绘制三维数字地球Earth(OpenGL)

&#x1f37a;三维数字地球系列相关文章如下&#x1f37a;&#xff1a;1【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;OpenGL、glfw、glut&#xff09;第一期2【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;OpenGL、glfw、glut&#xff09;第二期3【小沐学GIS】…

三、内存分配

1. JVM 简化架构概览 1.1 运行时数据区&#xff08;即内存区域&#xff0c;又称 JVM 内存结构&#xff09; 如上面的 JVM 简化架构图所示&#xff0c;内存区域分为如下五个部分&#xff08;这五个部分统称为运行时数据区&#xff09;&#xff1a; PC 寄存器&#xff08;又称程…

Python-setup进阶打包命令

一、setup.py文件的书写 这个资料有很多&#xff0c;不多赘述&#xff0c;setup 函数常用的参数如下&#xff1a; 基础描述信息&#xff1a; name 包名称&#xff08;起一个响亮的名字&#xff09;version (-V) 包版本author 程序的作者author_email 程序的作者的邮箱地址mai…

Kafka-服务端-DelayedOperationPurgatory

DelayedOperationPurgatory是一个相对独立的组件&#xff0c;它的主要功能是管理延迟操作。 DelayedOperationPurgatory的底层依赖于Kafka提供的时间轮实现。 我们可以使用JDK本身提供的java.util.Timer或是DelayQueue轻松实现定时任务的功能&#xff0c;为什么Kafka还要专门…

图像处理之《用于统一源跟踪和深度伪造检测的深度可分离水印SepMark》论文精读

一、文章摘要 恶意的Deepfakes导致了一场关于区分真脸和伪造脸的激烈冲突。尽管已经制定了许多事后检测Deepfakes的对策&#xff0c;但毫无疑问&#xff0c;在可预见的操作之前&#xff0c;被动取证没有考虑对原始人脸采取任何预防措施。为了完成这个取证生态系统&#xff0c;…

月薪2W的软件测试工程师,到底是做什么的?

在生活中&#xff0c;我们常常会遇到以下几种窘迫时刻&#xff1a; 准备骑共享单车出行&#xff0c;却发现扫码开锁半天&#xff0c;车子都没有反应&#xff1b;手机导航打车&#xff0c;却发现地图定位偏差很大&#xff0c;司机总是跑错地方&#xff1b;买个水&#xff0c;却…

Redis实战之-分布式锁-redission

一、分布式锁-redission功能介绍 基于setnx实现的分布式锁存在下面的问题&#xff1a; 重入问题&#xff1a;重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中&#xff0c;可重入锁的意义在于防止死锁&#xff0c;比如HashTable这样的代码中&#xff0c;他的方法都…

​第14节-高质量简历写作求职通关-在线测试

在线测试主要包括性格测试、综合能力测试、技能测试三类 性格测试 性格测试主要用于考察个人与工岗位的匹配程度 考察内容包含性格、能力、动机、价值观等&#xff0c;考察形式一般为给出相应的工作场景&#xff0c;让你选择最喜欢或者最不喜欢的答案 技能考试 这类测试一般是针…

SpringMVC第一天

简介 SpringMVC技术与Servlet技术功能等同&#xff0c;均属于web层开发技术 SpringMVC是一种基于java实现的MVC模型的轻量级Web框架 优点 使用简单,开发便捷(相比于Servlet) 灵活性强 入门案例 第一步、导入SpringMVC与Servlet坐标 <?xml version"1.0" encod…