参加实习(WPF)已经有两个多周的时间了,踩了一些坑,也算积累了一些小东西,准备慢慢拿出来分享一下。(●'◡'●)

这次呢就讲讲一个简单的电子签名板的实现。

先上张图(PS:字写得比较丑,不要太在意哈):

1.任务目标

最基本的需求:1.签名功能 2.清除签名 3.保存签名(让用户选择文件夹、签名保存为PNG格式的图片)

尝试额外功能:1.Ctrl + Z实现撤销功能 2.Ctrl + Y实现重做功能 3.保存签名后打开文件位置并选中文件

2.搞事情

1)UI方面

如图,总体来说,一个InkCanvas加上两个Button就解决问题了。

A. InkCanvas

<InkCanvas Grid.Column="1" Grid.Row="1" Background="White" Height="240" Name="ink"><InkCanvas.DefaultDrawingAttributes><DrawingAttributes Color="#FF000000" StylusTip="Ellipse" Height="6" Width="6" IgnorePressure="False" FitToCurve="False"><!--调整画笔形状--><DrawingAttributes.StylusTipTransform><!--https://msdn.microsoft.com/library/system.windows.media.matrix(v=vs.110).aspx--><Matrix M11="1" M12="0" M21="0" M22="1" OffsetX="0" OffsetY="0"/></DrawingAttributes.StylusTipTransform></DrawingAttributes></InkCanvas.DefaultDrawingAttributes>
</InkCanvas>

关于调整画笔形状的部分(对,就是那个矩阵),就我个人来说并不是很了解,所以就不作什么解释了,感兴趣的童鞋可以访问对应的微软官方文档查看相关资料。

B. Button

<Button x:Name="btnClearSign" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Padding="0" Margin="12,6,0,0" Click="btnClearSign_Click"><Button.Template><ControlTemplate><Grid><Label Cursor="Hand" Foreground="Red" FontFamily="Microsoft YaHei UI" FontSize="20"><Underline><Run Text="清除签名"></Run></Underline></Label></Grid></ControlTemplate></Button.Template>
</Button>

图中的两个按钮都是同一个套路,所以就只展示一个按钮的代码。(PS:为了让按钮显得不要太俗,我们为按钮弄一个类似于超链接的样式)

2)逻辑代码

签名功能我们就不用操心了,InkCanvas会处理好的。

A. 清除签名

ink.Strokes.Clear();

这么一行代码就足够了。说明一下,这里的ink就是我们在UI部分写的那个InkCanvas。

B.将签名保存为PNG图片

// 判断签名板内是否有内容
if (ink.Strokes.Any())
{// 让用户自己选择文件夹保存// 需要在工程中添加对System.Windows.Forms的引用// References => Add Reference => 勾选 System.Windows.Forms 项 => OKvar folderPicker = new FolderBrowserDialog();var res = folderPicker.ShowDialog();// 判断用户有没有选中文件夹if (res == System.Windows.Forms.DialogResult.Cancel) return;// 文件保存路径var folderPath = folderPicker.SelectedPath;var fileName = DateTime.Now.ToString("yyyyMMddHHmmss");var fileUri = folderPath + "\\" + fileName + ".png";// windows系统下默认dpi貌似为96,但目前本机测试认为dpi设置为72较为合适// dpi的大小会直接影响签名保存结果是否完整,关于dpi的知识网上还是比较多的,请各位自行了解// 下一行代码的第三个参数用于确定位图的横向dpi,第四个参数为纵向dpivar renderBitmap = new RenderTargetBitmap((int)ink.ActualWidth, (int)ink.ActualHeight, 72d, 72d, PixelFormats.Pbgra32);renderBitmap.Render(ink);using (var stream = new FileStream(fileUri, FileMode.Create)){var encoder = new PngBitmapEncoder();encoder.Frames.Add(BitmapFrame.Create(renderBitmap));encoder.Save(stream);}undoList.Clear();// 打开签名文件所在位置
    FileUtil.LocateFile(fileUri);
}
else
{System.Windows.MessageBox.Show("尚未进行签名,不能执行保存操作!");
}

注:A.这个部分存在一定的问题,请容许我在另一篇的博客中进行相关解释。

B.代码中的undoList.Clear() 以及FileUtil.LocateFile(fileUri) 各位暂时不用理睬,稍后我会进行相关解释。

C.下方图片讲解的是如何添加对System.Windows.Forms的引用。

C.实现撤销和重做功能

由于InkCanvas自身实现貌似并没这样的方法,所以,我们就自己动动手吧。方法其实还是比较简单的:首先我们需要明白的是,InkCanvas将每一个笔划都以一个Stroke类的对象保存在一个集合里边(InkCanvas的Strokes属性,StrokeCollection类型)。所以,实现撤销/重做功能就变成了对一个Collection的操作,撤销即移除顶部的元素(当然我们需要将移除的元素暂存一下,以便后续的重做操作),重做即向Collection顶部增添一项。下面来看看代码:

Stack<Stroke> undoList = new Stack<Stroke>();

声明一个全局变量(Stroke的一个栈),用于存储进行撤销操作时移除的Stroke,也用于在进行重做功能时提供资源。

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{this.KeyDown += (s, args) =>{// Undo => 检测 Ctrl + Zif((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control && args.Key == Key.Z){if (ink.Strokes.Any()){undoList.Push(ink.Strokes[ink.Strokes.Count - 1]);ink.Strokes.RemoveAt(ink.Strokes.Count - 1);}}// Redo => 检测 Ctrl + Yif ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control && args.Key == Key.Y){if (undoList.Any()){ink.Strokes.Add(undoList.Pop());}}};
}

在Window的Loaded事件里加上对Ctrl + Z以及Ctrl + Y的检测,具体套路就如上方代码中显示的那样。

D.打开签名所在位置

先扯点题外话,这个地方我使用的时P/Invoke的方式,调用C++的方法进行实现的。由于我自己对跨语言调用这一块知之甚少,所以无法做出多少解释,只是在运气作用下一番摸索后达到了目的而已。如果以后感觉对这一块了解更多一些东西后,再单独写一篇博客进行相关解释。

回到正题,先上代码:

public static class FileUtil
{/// <summary>/// 依据给定文件路径,打开文件位置并选中/// </summary>/// <param name="path">文件完全路径</param>public static void LocateFile(string path){/* // 此方法会导致每次新开一个文件资源管理器窗口,不喜欢* string domain = "";* var psi = new ProcessStartInfo("Explorer.exe");* psi.Arguments = "/c,/select," + path;* domain = psi.Domain;* var p = Process.Start(psi);*/IntPtr ppidl = IntPtr.Zero;uint psfgaoOut;FileManager.SHParseDisplayName(path, IntPtr.Zero, out ppidl, 0, out psfgaoOut);var res = FileManager.OpenFolderAndSelectItems(ppidl, 0, IntPtr.Zero, 0);}class FileManager{[DllImport("shell32.dll", EntryPoint = "SHOpenFolderAndSelectItems")]public static extern long OpenFolderAndSelectItems(IntPtr pidlFolder, UInt32 cidl, IntPtr apidl, UInt32 dwFlags);[DllImport("shell32.dll", EntryPoint = "SHParseDisplayName")]public static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, [Out()] out IntPtr pidl, uint sfgaoIn, [Out()] out uint psfgaoOut);}
}

这个家伙又要开始偏(哔)题(哔)了,请不用理睬:

正如代码中所说的,注释的部分也可以在一定程度上实现我们的需求,但它存在一定的问题。所以我就果断寻求另一个解决方案,终于打探到shell32.dll(位于Windows\System32目录下)里的SHOpenFolderAndSelectItems方法可以满足我的需求。在经历了一段时间的搜索相关资料,又看了看这位哥的经验分享后,我终于用C#的方式把SHOpenFolderAndSelectItems方法怼成了上方代码中的模样。但是我悲催的发现,只有OpenFolderAndSelectItems方法貌似依旧不行(根本没有正确的定位到对应的文件/文件夹),在经过一番资料查阅[msdn, pinvoke.net]后,总算是搞出了个可用的版本。

3.Demo

http://files.cnblogs.com/files/lary/UserSignatureDemo.rar

转载于:https://www.cnblogs.com/lary/p/6784171.html

【WPF】学习笔记(一)——做一个简单的电子签名板相关推荐

  1. tensorflow学习笔记二——建立一个简单的神经网络拟合二次函数

    tensorflow学习笔记二--建立一个简单的神经网络 2016-09-23 16:04 2973人阅读 评论(2) 收藏 举报  分类: tensorflow(4)  目录(?)[+] 本笔记目的 ...

  2. CocosCreator学习1:做一个简单的游戏

    把计步器写好了,到了写游戏场景.控件什么的时候,傻眼了.想做一个简单的地图,可以在地图上点击选择城市,发现用Cocos2D-X代码码出来好麻烦,尤其是城市位置问题,需要调试去找对像素区域做一个按钮控制 ...

  3. ROS2学习笔记13--编写一个简单的发布器和侦听器(C++)

    概要:这篇主要介绍编写发布器和侦听器的简单套路(C++) 环境:ubuntu20.04,ros2-foxy,vscode 最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现. 2. ...

  4. Django学习笔记2:一个简单的开发实例

    Technorati 标签: Python,Django 目标:通过开发一个简单的Todo管理应用,熟悉Django的基本概念.和使用. 运行环境 Windows Vista + Python 2.7 ...

  5. JSP/Servlet Web 学习笔记 DayFour —— 实现一个简单的JSP/Servlet交互

    小实例说明: a)实现一个由JSP负责前台显示,Servlet负责后台处理的交互小实例 b)JSP页面由表单获取一个开始数字,一个结束数字,交给Servlet打印响应的乘法表. 未解决的问题: a)跳 ...

  6. 【Python】如何用python做一个简单的输入输出交互界面?

    看到知乎上有人在问,如何使用Python做一个简单的输入输出交互界面? 交互界面就涉及到GUI编程. Python有很多GUI框架,功能大同小异. 其中比较出名的有「PyQT」.**wxPython. ...

  7. WPF学习笔记(数据绑定篇3)

    接上回的<WPF学习笔记(数据绑定篇2)>,继续 BindValidation 此示例演示了: 如何使用错误模板: 使用样式显示错误信息: 如何在校验发生异常时执行回调: 首先,你可以看见 ...

  8. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截...

    程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少.数据 ...

  9. python自己做个定时器_技术图文:如何利用 Python 做一个简单的定时器类?

    原标题:技术图文:如何利用 Python 做一个简单的定时器类? 背景 今天在B站上看有关 Python 最火的一个教学视频 -- "零基础入门学习 Python",这也是我们 P ...

  10. FPGA学习笔记(1)简单的时序逻辑电路——流水灯

    FPGA学习笔记(1)简单的时序逻辑电路--流水灯 编程语言为Verilog HDL 原理 (1)设计一个计数器,使开发板上的4个LED状态每500ms翻转一次.开发板上的晶振输出时钟频率为50MHz ...

最新文章

  1. 那些商人败类们在想什么
  2. SpringBoot整合RocketMQ之环境搭建以及Producer发送消息
  3. javascript的程序控制结构及语句------(2)循环控制语句、跳转语句、对话框
  4. Angular refreshView里Component template函数的执行原理
  5. FreeEIM 来点新知识iOS UIScrollView详解
  6. 计算机进程调度论文,计算机操作系统小论文Linux进程调度.doc
  7. javascript 公历与农历相互转换工具类
  8. MySQL性能优化 分区
  9. 通信电子线路高如云pdf_通信电子线路 第三版 教学课件 高如云 封面及目录.pdf...
  10. Mongo 创建数据库
  11. u盘安全删除 linux,如何安全删除U盘技巧
  12. 英语词根词缀记忆法(全集)_语言学习 | 英语词根词缀学习参考
  13. 寒假大一2.15考试
  14. 【web-ctf】ctf_BUUCTF_web(2)
  15. 一文看懂芯片后端报告
  16. Python实现isPrime()函数,参数为整数,要有异常处理。如果整数是质数,返回True,否则返回False。通过调用isPrime()函数,打印200以内的所有质数,以空格分割。
  17. Git快速提交Github步骤
  18. ubuntu12.04编译安装crtmpserver
  19. flex布局左边固定,右边自适应,右边内容超出会影响布局
  20. 华为服务器web界面配置文件,如何查看服务器web配置文件

热门文章

  1. Lockdir 文件夹加密 破解
  2. java excel 2007兼容包_office2007兼容包
  3. Microsoft Office2010 安装包和安装方法
  4. 编译ElasticFusion过程(Ubuntu14.04)
  5. 实验室纳新语音文字记录
  6. 学以致用深入浅出数字信号处理 pdf_数字阵列雷达:零中频接收机的优缺点
  7. FreeMarker模板导出word报表
  8. pta查验身份证。个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。
  9. java 8 中文字体_jdk安装中文字体,解决Can't read the embedded font LNUHUF+SimSun
  10. 武忠祥<高等数学辅导讲义> 第一章