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.rhkb.cn/news/243472.html

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

相关文章

Git--基本操作介绍(2)

Git 常用的是以下 6 个命令&#xff1a;git clone、git push、git add 、git commit、git checkout、git pull. 说明&#xff1a; workspace&#xff1a;工作区staging area&#xff1a;暂存区/缓存区local repository&#xff1a;版本库或本地仓库remote repository&#xf…

69.使用Go标准库compress/gzip压缩数据存入Redis避免BigKey

文章目录 一&#xff1a;简介二&#xff1a;Go标准库compress/gzip包介绍ConstantsVariablestype Headertype Reader 三&#xff1a;代码实践1、压缩与解压工具包2、单元测试3、为何压缩后还要用base64编码 代码地址&#xff1a; https://gitee.com/lymgoforIT/golang-trick/t…

搜索与图论第六期 最短路问题

前言 最短路问题真的很重要很重要希望大家都能够完全掌握所有最短路算法&#xff01;&#xff01; 一、最短路问题的分类 Dijkstra&#xff1a; Dijkstra算法是一种著名的图算法&#xff0c;主要用于求解有权图中的单源最短路径问题。它由荷兰计算机科学家艾兹赫尔戴克斯特…

vue2(Vuex)、vue3(Pinia)、react(Redux)状态管理

vue2状态管理Vuex Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它使用集中式存储管理应用的所有组件的状态&#xff0c;以及规则保证状态只能按照规定的方式进行修改。 State&#xff08;状态&#xff09;:Vuex 使用单一状态树&#xff0c;即一个对象包含全部的应用层…

线性代数的学习和整理23:用EXCEL和python 计算向量/矩阵的:内积/点积,外积/叉积

目录 1 乘法 1.1 标量乘法(中小学乘法) 1.1.1 乘法的定义 1.1.2 乘法符合的规律 1.2 向量乘法 1.2.1 向量&#xff1a;有方向和大小的对象 1.2.2 向量的标量乘法 1.2.3 常见的向量乘法及结果 1.2.4 向量的其他乘法及结果 1.2.5 向量的模长&#xff08;长度&#xff0…

【极数系列】Flink环境搭建(02)

【极数系列】Flink环境搭建&#xff08;02&#xff09; 引言 1.linux 直接在linux上使用jdk11flink1.18.0版本部署 2.docker 使用容器部署比较方便&#xff0c;一键启动停止&#xff0c;方便参数调整 3.windows 搭建Flink 1.18.0版本需要使用Cygwin或wsl工具模拟unix环境…

RAMROM

RAM&#xff08;Random Access Memory&#xff09;&#xff0c;随机存取存储器&#xff0c;也叫主存&#xff0c;又称内存&#xff08;动态ROM&#xff09;&#xff0c;是与CPU直接交换数据的内部存储器。它可以随时读写&#xff08;刷新时除外&#xff09;&#xff0c;而且速度…

Flutter 自定义AppBar实现滚动渐变

1、使用ListView实现上下滚动。 2、使用Stack&#xff1a;允许将其子部件放在彼此的顶部&#xff0c;第一个子部件将放置在底部。所以AppBar&#xff0c;写在ListView下面。 3、MediaQuery.removePadding&#xff1a;当使用ListView的时候发现&#xff0c;顶部有块默认的Padd…

服务器数据恢复—服务器进水导致阵列中磁盘同时掉线的数据恢复案例

服务器数据恢复环境&#xff1a; 数台服务器数台存储阵列柜&#xff0c;共上百块硬盘&#xff0c;划分了数十组lun。 服务器故障&检测&#xff1a; 外部因素导致服务器进水&#xff0c;进水服务器中一组阵列内的所有硬盘同时掉线。 北亚数据恢复工程师到达现场后发现机房内…

链路聚合原理与配置

链路聚合原理 随着网络规模不断扩大&#xff0c;用户对骨干链路的带宽和可靠性提出了越来越高的要求。在传统技术中&#xff0c;常用更换高速率的接口板或更换支持高速率接口板的设备的方式来增加带宽&#xff0c;但这种方案需要付出高额的费用&#xff0c;而且不够灵活。采用…

【GitHub项目推荐--一个语音机器人项目】【转载】

推荐一个腾讯大佬开源的语音对话机器人&#xff1a;wukong-robot &#xff0c;悟空机器人在 GitHub 上斩获 3.2K 的 Star。 这是一个简单灵活的中文语音对话机器人项目&#xff0c;目的是让中国的开发者也能快速打造个性化的智能音箱&#xff0c;同时&#xff0c;该项目还是第…

JMeter 设置请求头信息的详细步骤

在使用 JMeter 的过程中&#xff0c;我们会遇到需要设置请求头信息的场景。比如&#xff1a; POST 传过去的 Body 数据是 json 格式的。需要填添加头信息&#xff1a;Content-Type&#xff1a;application/json。在 header 中用 token 来传用户的认证信息。 下面&#xff0c;…

编程语言MoonBit新增矩阵函数的语法糖

MoonBit更新 1. 新增矩阵函数的语法糖 新增矩阵函数的语法糖&#xff0c;用于方便地定义局部函数和具有模式匹配的匿名函数&#xff1a; fn init {fn boolean_or { // 带有模式匹配的局部函数true, _ > true_, true > true_, _ > false}fn apply(f, x) {f(x)}le…

【Qt Quick 项目(第一集Qt Quick UI 项目项目创建)】

# Qt Quick 项目 到底什么是Qt Qml、什么是Qt Quick、QtQuick应用程序与Qt Widget程序有何区别,为了让读者在学习QML之前有一个整体认识,这里先介绍几个Quick项目。 01 Qt Quick UI 项目

【江科大】STM32:(超级详细)定时器输出比较

文章目录 输出比较单元特点 高级定时器&#xff1a;均有4个通道 PWM简介PWM&#xff08;Pulse Width Modulation&#xff09;脉冲宽度调制输出比较通道PWM基本结构基本定时器 参数计算捕获/比较通道的输出部分详细介绍如下&#xff1a; 舵机介绍硬件电路 直流电机介绍&#xff…

CRM的定义、功能,以及国内外CRM系统排名

什么是客户关系管理? CRM是(客户关系管理)的缩写&#xff0c;是一个管理与客户关系的系统。CRM的主要功能是管理基本客户信息和购买历史的客户管理、分析潜在客户和新客户的客户分析、对询问的自动回复的响应以及通过电子邮件通讯和研讨会吸引客户。它是加强和维护与客户和潜…

proxy 代理的接口报错301问题

项目系统里仅仅这个接口报错&#xff0c;反向代理错误导致。 默认情况下&#xff0c;不接受运行在HTTPS上&#xff0c;且使用了无效证书的后端服务器。如果你想要接受&#xff0c;修改配置&#xff1a;secure: false&#xff08;简单意思&#xff1a;如果本地没有进行过https相…

算法第二十二天-最大数

最大数 题目要求 解题思路 今天的题目&#xff0c;让我们将一组数字重新组合&#xff0c;构成一个最大的整数。由于构成的整数非常大&#xff0c;所以返回结果需要字符串格式。 分析一下规律&#xff1a; 为了避免用int型或者long型越界&#xff0c;所以我们需要把数字先转换…

用ChatGPT教学、科研!大学与OpenAI合作

亚利桑那州立大学&#xff08;简称“ASU”&#xff09;在官网宣布与OpenAI达成技术合作。从2024年2月份开始&#xff0c;为所有学生提供ChatGPT企业版访问权限&#xff0c;主要用于学习、课程作业和学术研究等。 为了帮助学生更好地学习ChatGPT和大语言模型产品&#xff0c;AS…

计算机视觉工程师就业前景如何?

计算机视觉作为一门快速发展的技术领域&#xff0c;其就业前景非常广阔。以下是对计算机视觉就业前景的分析&#xff1a; 市场规模&#xff1a;计算机视觉行业的市场规模正在持续扩大。根据行业分析报告&#xff0c;预计全球计算机视觉市场规模将在2025年达到530亿美元&#xf…