基于 WPF + Modern UI 的 公司OA小助手 开发总结
前言:
距离上一篇博客,整整一个月的时间了。人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思。人只有在总结的过程中才会发现自己的不足。
公司每天都要在OA系统上上班点击签到,下班点击签退,每天都要写工作日志。有的时候头脑不清醒或者忙过头了(别说你们没有过),就会忘记签到或者签退,有时候甚至忘记写工作日志。这会直接导致扣人工啊有木有,所以我才有了这个想法。首先声明,开发这个东西并不是博主对工作不认真不负责任,也并不是偷懒。相反,第一,可以避免因工作过忙忘记签到扣工资;第二,在开发的过程中你学到的东西是快速的,有趣的,让自己受益的。对于每个公司来说,OA系统都是他们的公司机密,所以博主并不会贴源码,只在这里阐述一个开发流程与思想,让你感觉到做一个自己觉得有趣的产品,思想的火花是多么不可思议。
一. 用到的模板与技术
1. WPF
相比传统的WinForm,WPF真是太强大了,无论在UI还是在多线程的处理上,以及一些其它的改进,都预示着WinForm将被WPF取代(这只是理论上,事实上,因为很多产品都是多年前开发的,用的是WinForm,如果要整个框架移植到WPF将是一件痛苦的事,反正产品没功能上的问题,这个移植就显得没必要了。所以目前,很多公司依然使用着WinForm的技术,开发者都在这个基础上对产品缝缝补补,更没有机会接触WPF了,就算是会这门技术的人,也找不到这个职位。比如我的公司就是)。本软件全面采用WPF技术,使用XAML布局以及做一些增强用户体验的动画。
2. Modern UI
Modern UI 是基于WPF的一个开源项目,托管在 code plex 上。你可以参考以下方法把 Modern UI 的模板添加到你的 Visual Studio 上:
- 在Visual Studio 2012中,打开扩展管理器(工具 > 扩展和更新)
- 选择在线 > Visual Studio库和搜索“ 现代UI “
- 选择现代为WPF的UI模板,然后单击“ 下载“,下载并安装。
关于 Modern UI 的介绍和使用,请参考http://mui.codeplex.com/,博主不再累赘。
3. 多线程
任何一个涉及到下载数据的程序都应该使用多线程编程,我们另开一个线程去下载数据的话,界面就不会有“假死”现象,用户体验显著提升。在WPF中,如果要在子线程中获取或者设置界面UI的值也是很简单的事情,这个WPF都为我们处理好了,很方便使用。
4. Lambda表达式
Lambda表达式是一个匿名方法,你不必再为只使用一次的方法独立写成一个函数(比如委托)。在WPF中在子线程里获取界面控件的值的时候就使用了Lambda表达式。Lambda表达式是委托的实现方法。
5. MVVM设计模式
MVVM 是 Model-View-ViewModel 的缩写,看字面你就能想到是什么意思吧。使用它的好处是,如果绑定的数据上下文改变了,会自动通知UI做出相应的更改。这也是比WinForm进步的地方。对于开发人员来说相当方便。当然MVVM不只这些内容。
6. XML配置文件的操作
因为要保存用户名密码、是否开启自动签到动能、自动签到的时间等等数据,就用到了App.config,实际上这是一个XML文件。
7. 系统托盘的处理
很多程序都有这个功能,主要是为了让程序在后台继续运行,以便时间到了就自动签到或者签退。
8. 开机自启
早上一来开机就自动启动,然后自动签到,会很爽吧,都完全不用自己动手。主要是写入注册表操作。
9. 模拟浏览器请求(重点)
使用HttpWatch来抓包,使用HttpWebRequest和HttpWebResponse来模拟浏览器的行为,要理解HTTP请求协议,当然在asp.net下还要理解asp.net网站与普通网站的差异(asp.net的原理)。asp.net的网站,使用服务器控件开发的话,页面上会有一大堆“垃圾代码”,用来保存页面状态,控件状态等等信息,这些信息在发送post报文的时候也需要发送过去,而且它的值的长度很长。
10. 正则表达式
软件里面大量使用了正则表达式从服务器返回的html页面来获取我们需要的数据,比如我的工作日志列表,签到记录等等。关于正则表达式无非两个类,Match/MatchCollection、Regex。有兴趣的自己去了解一下正则表达式的使用,即便是做前端开发的,也需要掌握js下的正则表达式来做客户端的表单验证。
二. 核心代码HtmlHelper
- public static class HtmlHelper
- {
- private static string cookieHeader = string.Empty;
- /// <summary>
- /// 添加日志
- /// </summary>
- /// <param name="strUrl">请求的url</param>
- /// <param name="param">参数</param>
- /// <returns></returns>
- public static string PostData(string strUrl, string param)
- {
- string strResult = "";
- HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strUrl);
- myHttpWebRequest.AllowAutoRedirect = true;
- myHttpWebRequest.KeepAlive = true;
- myHttpWebRequest.Accept = "image/gif, image/x-xbitmap, image/jpeg, imagepeg, applicationnd.ms-excel, application/msword, application/x-shockwave-flash, */*";
- myHttpWebRequest.Timeout = ;
- myHttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 2.0.50727)";
- myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
- myHttpWebRequest.Method = "POST";
- myHttpWebRequest.Headers.Add("cookie:" + cookieHeader);
- Stream MyRequestStrearm = myHttpWebRequest.GetRequestStream();
- StreamWriter MyStreamWriter = new StreamWriter(MyRequestStrearm, Encoding.ASCII);
- //把数据写入HttpWebRequest的Request流
- MyStreamWriter.Write(param);
- //关闭打开对象
- MyStreamWriter.Close();
- MyRequestStrearm.Close();
- HttpWebResponse response = null;
- System.IO.StreamReader sr = null;
- response = (HttpWebResponse)myHttpWebRequest.GetResponse();
- sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); // //utf-8
- strResult = sr.ReadToEnd();
- return strResult;
- }
- /// <summary>
- /// 功能描述:模拟登录页面,提交登录数据进行登录,并记录Header中的cookie
- /// </summary>
- /// <param name="strURL">登录数据提交的页面地址</param>
- /// <param name="strArgs">用户登录数据</param>
- /// <returns></returns>
- public static string PostLogin(string strURL, string strArgs)
- {
- string strResult = "";
- HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strURL);
- myHttpWebRequest.AllowAutoRedirect = true;
- myHttpWebRequest.KeepAlive = true;
- myHttpWebRequest.Accept = "image/gif, image/x-xbitmap, image/jpeg, imagepeg, applicationnd.ms-excel, application/msword, application/x-shockwave-flash, */*";
- myHttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 2.0.50727)";
- myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
- myHttpWebRequest.Method = "POST";
- myHttpWebRequest.Headers.Add("cookie:" + cookieHeader);
- CookieContainer myCookieContainer = new CookieContainer();
- myHttpWebRequest.CookieContainer = myCookieContainer;
- Stream MyRequestStrearm = myHttpWebRequest.GetRequestStream();
- StreamWriter MyStreamWriter = new StreamWriter(MyRequestStrearm, Encoding.ASCII);
- //把数据写入HttpWebRequest的Request流
- MyStreamWriter.Write(strArgs);
- //关闭打开对象
- MyStreamWriter.Close();
- MyRequestStrearm.Close();
- HttpWebResponse response = null;
- System.IO.StreamReader sr = null;
- response = (HttpWebResponse)myHttpWebRequest.GetResponse();
- cookieHeader = myHttpWebRequest.CookieContainer.GetCookieHeader(new Uri(strURL));
- sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); // //utf-8
- strResult = sr.ReadToEnd();
- return strResult;
- }
- /**/
- /// <summary>
- /// 功能描述:在PostLogin成功登录后记录下Headers中的cookie,然后获取此网站上其他页面的内容
- /// </summary>
- /// <param name="strURL">获取网站的某页面的地址</param>
- /// <param name="strReferer">引用的地址</param>
- /// <returns>返回页面内容</returns>
- public static string GetPage(string strURL)
- {
- string strResult = "";
- HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strURL);
- myHttpWebRequest.ContentType = "textml";
- myHttpWebRequest.Method = "GET";
- myHttpWebRequest.Headers.Add("cookie:" + cookieHeader);
- HttpWebResponse response = null;
- System.IO.StreamReader sr = null;
- response = (HttpWebResponse)myHttpWebRequest.GetResponse();
- sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); // //utf-8
- strResult = sr.ReadToEnd();
- return strResult;
- }
- /// <summary>
- /// 获取隐藏控件的value
- /// </summary>
- /// <param name="loginHtml">HTML页面代码字符串</param>
- /// <param name="regex">要获取的值的正则表达式</param>
- /// <param name="replaceLeft">左边要删除的字符串</param>
- /// <param name="replaceRight">右边要删除的字符串</param>
- /// <returns></returns>
- public static string GetHiddenValue(string loginHtml, string regex, string replaceLeft, string replaceRight)
- {
- string viewState = string.Empty;
- Match match = new Regex(regex).Match(loginHtml);
- if (match.Success)
- {
- viewState = match.Value.Replace(replaceLeft, "");
- viewState = viewState.Replace(replaceRight, "");
- }
- return viewState;
- }
HtmlHelper
吐槽一下自己,这个类其实可以优化,比如PostLogin和PostData其实可以合并,GetHiddenValue也可以写得更好,只是工作这边还比较忙,刚刚接手了一个任务,是把项目的结构全面改版,使得每一个功能就是一个小项目,这样管理起来方便很多,所以没有时间进行优化,做好了自己能用就用着先,还是工作比较重要。
很明显,里面有4个方法,每个方法的作用以及调用方式都有很详细的注释,我就不重复了。至于更多的代码我就不贴了,毕竟涉及到商业机密的问题。讲讲原理吧。
三. 原理
首先,我们知道http是无状态连接,每次浏览器向服务器端发送请求,服务器返回数据之后就断开了,你下一次请求的时候服务器并不知道你是否已经登录,那么asp.net下服务器怎么知道你的登陆状态呢?当你登陆之后,服务器会给浏览器发送一个cookie,用来标识你的登录状态,下一次请求的时候浏览器会把这个cookie一同发给服务器,服务器接收到之后验证你发过来的cookie数据,然后就知道你是否已经登陆过。如果没登陆,就不让你请求别的页面数据。我们可以使用HttpWebRequest和HttpWebResponse类来模拟浏览器的请求。
登陆之后,我们要写工作日志,就要把日志内容拼成要提交的报文,然后post到服务器,这就是一个post请求过程。还有一种请求叫做get请求,这种请求是不提交报文的,直接发请求,然后服务器就会返回一个html页面,然后我们就可以利用正则表达式来获取我们需要的数据了。
这里有一个值得注意的地方,因为我们的程序要一个挂在电脑上,以便它可以到时间后自动签退或者签到,但是服务器为你保存的登录状态是有时间段的,如果过了一段时间你没有请求操作,服务器认为你已经断开连接,不再为你保持登录状态(我们公司的OA系统似乎是半个小时),所以我们进行一个请求之前要判断一下登录状态是否还保持着,如果断开了就重新登录一下再进行请求。怎么判断登录状态呢?我们公司的OA系统会弹出一个提示框提示身份验证过期,而这个提示框当然是在html页面上的,我们只需要请求一下主页,看它返回的html页面中是否包含身份验证过期这个信息就行了(别说你不知道html页面其实就是一个字符串)。
四. 晒图
1.登陆(其实只是保存了用户名密码,并没有真正登陆,到需要进行登陆操作的时候才登陆,比如提交日志、签到、签退、获取日志列表、登录信息等等,当然并不是每次这些操作都登陆,只要登录状态还保持着,就不需要重新登陆了)
2.主页
3.添加工作日志
4.个性化(模板自带)
5.用户信息(用于登陆)
6.系统托盘
7.签到成功提示
五. 后话
在这个浮躁的世界里,我们不可以浮躁。人要有梦想,不然跟条咸鱼有什么分别。虽然我在追求自己喜欢的生活方式上有着各种阻碍,但是我还是认为,以自己喜欢的方式生活才是最开心的。人就一辈子,只要能承担责任,还有什么必要跟自己过不去呢?昨天我朋友从凤凰古城给我寄来一张明信片,我感慨万千,于是回复了一句:祝前程似锦,山明水秀,兄弟情谊,万古长青。切记,莫屈服,要以自己喜欢的方式生活。
也献给你们,亲爱的园友。
基于 WPF + Modern UI 的 公司OA小助手 开发总结的更多相关文章
- (转)基于 WPF + Modern UI 的 公司OA小助手 开发总结
原文地址:http://www.cnblogs.com/rainlam163/p/3365181.html 前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个 ...
- OA小助手
基于 WPF + Modern UI 的 公司OA小助手 开发总结 前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会 ...
- VSTO学习笔记(七)基于WPF的Excel分析、转换小程序
原文:VSTO学习笔记(七)基于WPF的Excel分析.转换小程序 近期因为工作的需要,要批量处理Excel文件,于是写了一个小程序,来提升工作效率. 小程序的功能是对Excel进行一些分析.验证,然 ...
- WPF Modern UI 主题更换原理
WPF Modern UI 主题更换原理 一 . 如何更换主题? 二 . 代码分析 代码路径 : FirstFloor.ModernUI.App / Content / SettingsAppeara ...
- 通达OA 小飞鱼开发培训第四讲 工作流介绍(图文)
本次课程主要解说了OA工作流相关内容,有些涉及到工作流的程序开发假设对工作流不熟悉也是有非常大难度,因此在这里进行了内容补充. 1.工作流介绍
- .NET Core 小程序开发零基础系列(2)——小程序服务通知(模板消息)
基于上一篇文件“.NET Core 小程序开发零基础系列(1)——开发者启用并校验牵手成功”的反映,个人觉得效果很不错,大家对公众号开发还是有很大需求的,同时也收到了很多同学的问题,后面我也会通过实战 ...
- Pyqt5开发一款小工具(翻译小助手)
翻译小助手 开发需求 首先五月份的时候,正在学习爬虫的中级阶段,这时候肯定要接触到js逆向工程,于是上网找了一个项目来练练手,这时碰巧有如何进行对百度翻译的API破解思路,仿造网上的思路,我摸索着完成 ...
- .NET6: 开发基于WPF的摩登三维工业软件 (8) - MVVM
基于WPF开发界面的一个很大优势是可以方便地基于MVVM设计模式开发应用.本文从应用的角度基于MVVM实现参数化管材的创建界面. 1 MVVM MVVM是Model-View-ViewModel的简写 ...
- WCF/WPF公司内部订餐程序开发
WCF/WPF公司内部订餐程序开发 (服务端篇) 上班的第一天,群里讨论关于订餐的问题,所以想到了要不要自己开发一个公司内部的订餐系统呢?方便公司内部员工的订餐,有了想法就简单的实践了下 . 实现还是 ...
随机推荐
- Codeforces Round #418 (Div. 2) C
Description Nadeko's birthday is approaching! As she decorated the room for the party, a long garlan ...
- httpclient 3.1跳过https请求SSL的验证
一.因为在使用https发送请求的时候会涉及,验证方式.但是这种方式在使用的时候很不方便.特别是在请求外部接口的时候,所以这我写了一个跳过验证的方式.(供参考) 二.加入包,这里用的是commons- ...
- MAX458X多通道模拟切换开关(类似74HC4051)
- Apache下禁止使用IP直接访问本站的配置方法
现在管的严啊,上面要求不能使用IP直接访问服务器,把apache配置做下调整就行了.方法如下: 打开apache的配置文件 # vi /usr/local/apache2/conf/extra/htt ...
- 初学.net增删改查
分页显示 DAL: public List GetListByPager(int PageIndex, int PageSize, out int RowCount) { string sql = & ...
- client系列、offset系列、scroll系列
一.client系列 clientWidth/clientHeight 是我们设置的宽和高加上内边距(没有边框) clientLeft/clientTop 就是我们设置的边框值 二.offset ...
- myeclipse 安装svn(subeclipsesite)插件
(1)到官网下载subeclipsesite,下载最新的版本:http://subclipse.tigris.org/servlets/ProjectDocumentList?folderID=224 ...
- Jmeter官网文档翻译
Jmeter目录 入门 1.0概述 测试计划建设 负载测试运行 负载测试分析 开始吧 1.1要求 1.1.1 Java版本 1.1.2操作系统 1.2可选 1.2.1 Java编译器 1.2.2 SA ...
- HYSBZ 1588 营业额统计 (Splay树)
题意:给出一个公司每一天的营业额,求每天的最小波动值之和.该天的最小波动值= min { 绝对值| 该天以前某一天的营业额-该天的营业额 | }.第一天的最小波动值就是其自己. 思路:Splay伸展树 ...
- Java从入门到放弃18---Map集合/HashMap/LinkedHashMap/TreeMap/集合嵌套/Collections工具类常用方法
Java从入门到放弃18—Map集合/HashMap/LinkedHashMap/TreeMap/集合嵌套/Collections工具类常用方法01 Map集合Map集合处理键值映射关系的数据为了方便 ...