WPF实现签名拍照功能

news2024/11/16 17:40:02

在这里插入图片描述

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:WPF 案例及知识分享专栏
✨特色专栏:乐趣国学-心性养成之路
🥭本文内容:WPF实现签名拍照功能
  当你使用WPF(Windows Presentation Foundation)技术编写一个签名拍照软件时,需要使用C#来处理界面和相机操作。以下是一个简单示例,展示如何创建一个WPF应用程序来实现这一功能。

在这里插入图片描述

  首先,确保你已经安装了Visual Studio和.NET框架。然后,创建一个新的WPF应用程序项目,我们将称之为"SignatureCaptureApp"。

  在MainWindow.xaml中,创建界面布局,包括一个显示相机预览的区域、一个签名区域、一个按钮来拍照并保存签名。

<Window x:Class="SignatureCaptureApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Signature Capture App" Height="400" Width="600">
    <Grid>
        <Image Name="CameraPreview" Width="400" Height="300" Stretch="UniformToFill" Margin="10"/>
        <InkCanvas Name="SignatureCanvas" Width="400" Height="100" Margin="10"/>
        <Button Content="拍照并保存签名" Click="CaptureSignature_Click" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10"/>
    </Grid>
</Window>

  在MainWindow.xaml.cs中,添加相机和签名捕捉的逻辑。

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Media.Imaging;
using System.Windows.Media.Imaging;

namespace SignatureCaptureApp
{
    public partial class MainWindow : Window
    {
        private CameraCapture _cameraCapture;

        public MainWindow()
        {
            InitializeComponent();
            _cameraCapture = new CameraCapture(CameraPreview);
            _cameraCapture.InitializeCamera();
        }

        private void CaptureSignature_Click(object sender, RoutedEventArgs e)
        {
            // 拍照
            BitmapSource photo = _cameraCapture.CapturePhoto();

            // 保存签名
            if (SignatureCanvas.Strokes.Count > 0 && photo != null)
            {
                SaveSignatureAndPhoto(SignatureCanvas.Strokes, photo);
            }
        }

        private void SaveSignatureAndPhoto(StrokeCollection strokes, BitmapSource photo)
        {
            try
            {
                // 为签名创建一个独一无二的文件名
                string signatureFileName = $"Signature_{DateTime.Now:yyyyMMddHHmmss}.png";
                string photoFileName = $"Photo_{DateTime.Now:yyyyMMddHHmmss}.jpg";

                // 获取保存签名和照片的文件夹路径
                string saveFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

                // 创建签名图片文件
                SaveSignatureToFile(strokes, Path.Combine(saveFolderPath, signatureFileName));

                // 创建照片文件
                SavePhotoToFile(photo, Path.Combine(saveFolderPath, photoFileName));
            }
            catch (Exception ex)
            {
                // 处理错误,例如显示错误消息框
                MessageBox.Show($"保存签名和照片时发生错误:{ex.Message}");
            }
        }

        private void SaveSignatureToFile(StrokeCollection strokes, string filePath)
        {
            // 创建一个RenderTargetBitmap用于绘制签名
            RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(400, 100, 96, 96, PixelFormats.Default);
            renderTargetBitmap.Render(SignatureCanvas);

            // 创建一个PngBitmapEncoder保存签名
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

            // 将签名保存到文件
            using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
            {
                encoder.Save(fileStream);
            }
        }

        private void SavePhotoToFile(BitmapSource photo, string filePath)
        {
            // 创建一个JpegBitmapEncoder保存照片
            JpegBitmapEncoder encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(photo));

            // 将照片保存到文件
            using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
            {
                encoder.Save(fileStream);
            }
        }
    }
}

  生成唯一的文件名,然后获取用于保存的文件夹路径(这里以"我的图片"文件夹为例)。接下来,它使用RenderTargetBitmap将签名画布渲染为位图,然后使用PngBitmapEncoder保存签名。同时,也使用JpegBitmapEncoder保存相机拍摄的照片。

  创建CameraCapture.cs类来处理相机操作。你需要使用WPF的MediaElement和System.Windows.Media.Imaging来实现相机预览和拍照功能。这里提供一个简化的示例,实际应用中需要更多的处理和错误处理。

  当你需要在WPF应用中初始化相机并进行拍照时,你可以使用MediaCapture类来实现。下面是更详细的示例,展示如何初始化相机并进行拍照:

using System;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows;
using System.IO;
using System.Threading.Tasks;
using Windows.Media.Capture;
using Windows.Media.MediaProperties;
using Windows.Storage;
using Windows.Storage.Streams;

namespace SignatureCaptureApp
{
    public class CameraCapture
    {
        private MediaElement _cameraPreview;
        private MediaCapture _mediaCapture;

        public CameraCapture(MediaElement cameraPreview)
        {
            _cameraPreview = cameraPreview;
        }

        public async void InitializeCamera()
        {
            try
            {
                // 初始化MediaCapture对象
                _mediaCapture = new MediaCapture();
                await _mediaCapture.InitializeAsync();

                // 将相机预览显示在MediaElement中
                _cameraPreview.SetMediaPlayer(_mediaCapture);

                // 启动相机预览
                await _mediaCapture.StartPreviewAsync();
            }
            catch (Exception ex)
            {
                // 处理错误,例如显示错误消息框
                MessageBox.Show($"初始化相机时发生错误:{ex.Message}");
            }
        }

        public async Task<BitmapSource> CapturePhoto()
        {
            try
            {
                // 创建一个临时文件来保存照片
                StorageFile photoFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("TempPhoto.jpg", CreationCollisionOption.GenerateUniqueName);

                // 拍照并保存到临时文件
                ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg();
                await _mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile);

                // 从临时文件中读取照片数据
                using (IRandomAccessStream photoStream = await photoFile.OpenReadAsync())
                {
                    // 创建一个BitmapDecoder来解码照片数据
                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(photoStream);

                    // 将照片解码为BitmapSource
                    SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
                    SoftwareBitmap softwareBitmapBGR8 = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
                    BitmapSource photo = await ConvertToBitmapSource(softwareBitmapBGR8);

                    // 删除临时文件
                    await photoFile.DeleteAsync();

                    // 返回照片
                    return photo;
                }
            }
            catch (Exception ex)
            {
                // 处理错误,例如显示错误消息框
                MessageBox.Show($"拍照时发生错误:{ex.Message}");
                return null;
            }
        }

        private async Task<BitmapSource> ConvertToBitmapSource(SoftwareBitmap softwareBitmap)
        {
            // 创建一个WriteableBitmap来保存照片数据
            WriteableBitmap bitmap = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
            softwareBitmap.CopyToBuffer(bitmap.PixelBuffer);

            // 等待UI线程空闲,然后在UI线程上创建BitmapSource
            await Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                bitmap.Invalidate();
            }), DispatcherPriority.ApplicationIdle);

            return bitmap;
        }
    }
}

  在InitializeCamera方法中,我们使用MediaCapture类来初始化相机,并将相机预览显示在MediaElement中。在CapturePhoto方法中,我们使用CapturePhotoToStorageFileAsync方法拍照并将照片保存到临时文件中。然后,我们使用BitmapDecoder类解码照片数据,并将其转换为BitmapSource对象。最后,我们删除临时文件并返回照片。

  请注意,这段代码使用了异步方法,因此需要在方法前面加上async关键字,并使用await关键字来等待异步操作完成。此外,我们还使用了SoftwareBitmap和WriteableBitmap来处理照片数据。


  码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

在这里插入图片描述

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

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

相关文章

低代码:时代的选择

低代码&#xff0c;作为一种快速开发应用的软件&#xff0c;将通用、可重复利用的代码形成组件化的模块&#xff0c;通过图形化的界面来拖拽组件并形成应用。低代码能够实现只写少量代码或不写代码&#xff0c;类似用“乐高积木”的方式来开发。 既减少了不必要的工作量&#x…

【硬件+软件】示波器的自动化控制

有没有这样一种情况&#xff0c;就是通过脚本控制你的测量设备&#xff0c;比如示波器&#xff0c;那是不是就可以不用人为的去分析数据&#xff0c;直接由脚本就可以完成整个操作了。 是的&#xff0c;有这样一种方式就可以&#xff0c;就是利用python中的pyvisa库&#xff0c…

TODO Vue typescript forEach的bug,需要再核實

forEach 一個string[]&#xff0c;只有最後一個匹配條件有效&#xff0c;其它條件無效。 所以&#xff0c;只能替換成普通的for循環。 console.log(taskList)// for (const _task of taskList.value) {// if (_task invoiceSendEmail) {// form.value.invoiceSendEmail…

license授权方案

文章目录 概要license文件内容架构流程图实现 概要 当我们的商业软件售卖给客户后&#xff0c;往往需要对分发出去的软件进行限制&#xff0c;比如指定使用者&#xff0c;限制软件使用有效时间&#xff0c;声明版权信息、限制软件使用磁盘大小等。很多场景又是离线使用&#x…

JMeter 随机数生成器简介:使用 Random 和 UUID 算法

在压力测试中&#xff0c;经常需要生成随机值来模拟用户行为。JMeter 提供了多种方式来生成随机值&#xff0c;本文来具体介绍一下。 随机数函数 JMeter 提供了多个用于生成随机数的函数&#xff0c;其中最常用的是 __Random 函数。该函数可以生成一个指定范围内的随机整数或…

Python名侦探柯南

文章目录 系列文章前言Turtle入门名侦探柯南尾声 系列文章 序号文章目录直达链接1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://want595.blog.csdn.net/…

Java中的日期类整理

文章目录 第一代日期Date类第二代日期Calendar类第三代日期LocalDateTime类4. Instant类 时间戳 第一代日期Date类 jdk1.0引入 1.第一代日期类 包含两个构造器&#xff0c;Date()和Date(long)&#xff1b; new Date()&#xff1b;精确到毫秒 Date(time)如果time是Long类型的会将…

数据库安全运维是什么意思?数据库安全运维系统用哪家好?

我们大家都直到数据在某些情况下容易丢失或被破坏&#xff0c;攻击者可能通过对数据库进行破坏或勒索等手段获取利益。所以保障数据库安全至关重要。今天我们就来聊聊数据库安全运维是什么意思&#xff1f;数据库安全运维系统用哪家好&#xff1f; 数据库安全运维是什么意思&…

如何使用Python实现发送邮件功能

目录 代码示例&#xff1a; 代码解释 注意事项&#xff1a; 总结&#xff1a; 在Python中&#xff0c;可以使用内置的smtplib库和email库来发送电子邮件。 代码示例&#xff1a; 下面是一个使用smtplib和email库来发送电子邮件的示例&#xff1a; import smtplib from …

一文带你快速掌握爬虫开发中的一些高级调试技巧

文章目录 1. 写在前面2. Reply XHR&#xff08;重新发起请求&#xff09;3. copy as fecth&#xff08;修改参数请求&#xff09;4. copy()复制变量5. Web网页全屏截图6. 控制台安装使用npm7. 控制台中引用上次执行结果8. 控制台表展示对象数组 1. 写在前面 做过爬虫开发的人都…

视频模板SDK,为企业带来无限创意与效率

在当今的数字化时代&#xff0c;视频已经成为了信息传播的主流方式之一&#xff0c;对于企业来说&#xff0c;制作高质量的视频已经成为了一项重要的业务需求。然而&#xff0c;制作一部高质量的企业视频需要耗费大量时间和金钱&#xff0c;对于许多企业来说是一个不小的负担。…

web漏洞挖掘指南-前端跨域漏洞

web漏洞挖掘指南 前端跨域漏洞 如果你对网络安全入门感兴趣&#xff0c;那么你需要的话可以点击这里&#x1f449;【入门&进阶全套282G学习资源包免费分享&#xff01;】 一、何为跨域 1.设想一种场景&#xff0c;一个恶意网站上嵌入了一个iframe标签去加载银行的登陆页…

求臻医学:重磅 共识发布

共识发布 近日&#xff0c;我国首个《胃癌分子残留病灶检测与临床应用中国专家共识&#xff08;2023版&#xff09;》[1]&#xff08;以下简称“共识”&#xff09;正式发表于《中华消化外科杂志》。 该共识由中国医师协会外科医师分会上消化道外科医师专家工作组发起&#xf…

【leetcode】2530.执行k次操作后的最大分数

前言 每次都取最大值的策略我是知道的&#xff0c;排序的时间复杂度太高&#xff0c;用栈也不行。没想到c里面已经内置了优先队列priority_queue 代码 #include<iostream> #include<algorithm> #include<vector> #include<stack> #include<queue…

从零开始了解协同OA办公系统,一篇文章就够了!

随着现代企业管理的不断发展&#xff0c;越来越多的企业开始关注协同OA办公系统&#xff0c;以提高工作效率和降低成本。但是&#xff0c;很多企业对协同OA办公系统并不了解&#xff0c;甚至不知道该如何开始使用。本文将从零开始&#xff0c;为大家详细介绍协同OA办公系统。 一…

.npmrc 使用详解

配置.npmrc之后需要&#xff1a; 清理项目目录中的 node _modules 目录(package-lock.json,umi)。清理 node cache: npm cache clear --force&#xff1b;{ 此步骤必须&#xff0c;主要是大家的电脑经过多年使用后&#xff0c;npm 配置比较混乱&#xff0c;为了避免或者减少配…

虚实融合 智兴百业 | 赵捷副市长莅临拓世科技集团筹备展台指导,本月19号!拓世科技集团与您相约世界VR产业大会

新时代科技革命中&#xff0c;虚拟现实技术、5G和“元宇宙”概念崛起&#xff0c;助力全球范围内的数字经济和产业转型。我国也正迈向高质量发展攻坚阶段&#xff0c;在中部腹地的江西&#xff0c;政府结合全球技术趋势和自身发展需求&#xff0c;选择虚拟现实为新的经济增长点…

SpringBoot集成Lettuce客户端操作Redis

目录 一、前言二、基础集成配置&#xff08;redis单节点&#xff09;2.1、POM2.2、添加配置文件application.yml2.3、编写配置文件2.4、编写启动类2.5、编写测试类测试是否连接成功 一、前言 spring-boot-starter-data-redis有两种实现 lettuce 和 jedis&#xff0c;spring bo…

【Vue项目】通过设置全局的异常处理来统一处理后端返回的异常

文章目录 简介方法一创建统一异常处理模块使用axios拦截器处理异常在页面中使用异常处理 方法二创建全局异常处理函数在main.js中配置全局异常处理在网络请求中捕获异常 方法三创建全局异常处理插件在main.js中注册全局异常处理插件在网络请求中捕获异常 总结 简介 在Vue项目中…

PaddleX解决分类、检测两大场景问题?实战精讲教程来了!

AI技术加速数字化进程&#xff0c;从制造、交通、能源等基础行业&#xff0c;到医疗、城市、零售、家居等与人们日常生活息息相关的行业&#xff0c;AI技术推动了数字化变革&#xff0c;也不断赋能于千行百业&#xff0c;但产业落地实践中依然面临着数据、算法等诸多困难。为了…