当前位置: 首页 > news >正文

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要好的,尤其是支持任意形状,这样就有利于后期划线截屏的实现了。

相关文章:

  • C++11手撕线程池 call_once 单例模式 Singleton / condition_variable 与其使用场景
  • 一文(10图)了解Cornerstone3D核心概念(万字总结附导图)
  • 【Emotion】 自动驾驶最近面试总结与反思
  • 【深度学习】BasicSR训练过程记录,如何使用BasicSR训练GAN
  • elastic search入门
  • 为什么 HTTPS 协议能保障数据传输的安全性?
  • 【Unity学习笔记】New Input System 部分源码和测试用例补充
  • 数学建模学习笔记||层次分析法
  • C# 创建多线程的函数
  • LeetCode59 螺旋矩阵 II
  • 轻量化CNN网络 - MobileNet
  • 知识笔记(八十九)———链式语句中partition和strict用法
  • spring和springboot、springMVC有什么区别?
  • C# CefSharp 根据输入日期段自动选择日期
  • LeetCode454. 四数相加 II和1.两数之和
  • [LeetCode] Wiggle Sort
  • 【5+】跨webview多页面 触发事件(二)
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • crontab执行失败的多种原因
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • Javascript 原型链
  • java取消线程实例
  • orm2 中文文档 3.1 模型属性
  • PHP面试之三:MySQL数据库
  • session共享问题解决方案
  • TypeScript迭代器
  • Vue.js-Day01
  • 从零开始的无人驾驶 1
  • 关于for循环的简单归纳
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 深度学习在携程攻略社区的应用
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 线性表及其算法(java实现)
  • 小程序开发之路(一)
  • 自动记录MySQL慢查询快照脚本
  • 7行Python代码的人脸识别
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #vue3 实现前端下载excel文件模板功能
  • (10)STL算法之搜索(二) 二分查找
  • (2020)Java后端开发----(面试题和笔试题)
  • (4) PIVOT 和 UPIVOT 的使用
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (C语言)fgets与fputs函数详解
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (笔试题)分解质因式
  • (待修改)PyG安装步骤
  • (第27天)Oracle 数据泵转换分区表
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)计算机毕业设计ssm电影分享网站
  • (附源码)计算机毕业设计高校学生选课系统
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表