基于 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

  1. public static class HtmlHelper
  2. {
  3. private static string cookieHeader = string.Empty;
  4.  
  5. /// <summary>
  6. /// 添加日志
  7. /// </summary>
  8. /// <param name="strUrl">请求的url</param>
  9. /// <param name="param">参数</param>
  10. /// <returns></returns>
  11. public static string PostData(string strUrl, string param)
  12. {
  13. string strResult = "";
  14. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strUrl);
  15. myHttpWebRequest.AllowAutoRedirect = true;
  16. myHttpWebRequest.KeepAlive = true;
  17. myHttpWebRequest.Accept = "image/gif, image/x-xbitmap, image/jpeg, imagepeg, applicationnd.ms-excel, application/msword, application/x-shockwave-flash, */*";
  18. myHttpWebRequest.Timeout = 10000;
  19. myHttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 2.0.50727)";
  20. myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
  21. myHttpWebRequest.Method = "POST";
  22. myHttpWebRequest.Headers.Add("cookie:" + cookieHeader);
  23.  
  24. Stream MyRequestStrearm = myHttpWebRequest.GetRequestStream();
  25. StreamWriter MyStreamWriter = new StreamWriter(MyRequestStrearm, Encoding.ASCII);
  26. //把数据写入HttpWebRequest的Request流
  27. MyStreamWriter.Write(param);
  28. //关闭打开对象
  29. MyStreamWriter.Close();
  30. MyRequestStrearm.Close();
  31.  
  32. HttpWebResponse response = null;
  33. System.IO.StreamReader sr = null;
  34. response = (HttpWebResponse)myHttpWebRequest.GetResponse();
  35.  
  36. sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); // //utf-8
  37. strResult = sr.ReadToEnd();
  38. return strResult;
  39. }
  40.  
  41. /// <summary>
  42. /// 功能描述:模拟登录页面,提交登录数据进行登录,并记录Header中的cookie
  43. /// </summary>
  44. /// <param name="strURL">登录数据提交的页面地址</param>
  45. /// <param name="strArgs">用户登录数据</param>
  46. /// <returns></returns>
  47. public static string PostLogin(string strURL, string strArgs)
  48. {
  49. string strResult = "";
  50. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strURL);
  51. myHttpWebRequest.AllowAutoRedirect = true;
  52. myHttpWebRequest.KeepAlive = true;
  53. myHttpWebRequest.Accept = "image/gif, image/x-xbitmap, image/jpeg, imagepeg, applicationnd.ms-excel, application/msword, application/x-shockwave-flash, */*";
  54.  
  55. myHttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 2.0.50727)";
  56. myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
  57. myHttpWebRequest.Method = "POST";
  58. myHttpWebRequest.Headers.Add("cookie:" + cookieHeader);
  59.  
  60. CookieContainer myCookieContainer = new CookieContainer();
  61. myHttpWebRequest.CookieContainer = myCookieContainer;
  62.  
  63. Stream MyRequestStrearm = myHttpWebRequest.GetRequestStream();
  64. StreamWriter MyStreamWriter = new StreamWriter(MyRequestStrearm, Encoding.ASCII);
  65. //把数据写入HttpWebRequest的Request流
  66. MyStreamWriter.Write(strArgs);
  67. //关闭打开对象
  68. MyStreamWriter.Close();
  69. MyRequestStrearm.Close();
  70.  
  71. HttpWebResponse response = null;
  72. System.IO.StreamReader sr = null;
  73. response = (HttpWebResponse)myHttpWebRequest.GetResponse();
  74.  
  75. cookieHeader = myHttpWebRequest.CookieContainer.GetCookieHeader(new Uri(strURL));
  76.  
  77. sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); // //utf-8
  78. strResult = sr.ReadToEnd();
  79. return strResult;
  80. }
  81.  
  82. /**/
  83. /// <summary>
  84. /// 功能描述:在PostLogin成功登录后记录下Headers中的cookie,然后获取此网站上其他页面的内容
  85. /// </summary>
  86. /// <param name="strURL">获取网站的某页面的地址</param>
  87. /// <param name="strReferer">引用的地址</param>
  88. /// <returns>返回页面内容</returns>
  89. public static string GetPage(string strURL)
  90. {
  91. string strResult = "";
  92. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strURL);
  93. myHttpWebRequest.ContentType = "textml";
  94. myHttpWebRequest.Method = "GET";
  95. myHttpWebRequest.Headers.Add("cookie:" + cookieHeader);
  96.  
  97. HttpWebResponse response = null;
  98. System.IO.StreamReader sr = null;
  99. response = (HttpWebResponse)myHttpWebRequest.GetResponse();
  100. sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); // //utf-8
  101. strResult = sr.ReadToEnd();
  102. return strResult;
  103. }
  104.  
  105. /// <summary>
  106. /// 获取隐藏控件的value
  107. /// </summary>
  108. /// <param name="loginHtml">HTML页面代码字符串</param>
  109. /// <param name="regex">要获取的值的正则表达式</param>
  110. /// <param name="replaceLeft">左边要删除的字符串</param>
  111. /// <param name="replaceRight">右边要删除的字符串</param>
  112. /// <returns></returns>
  113. public static string GetHiddenValue(string loginHtml, string regex, string replaceLeft, string replaceRight)
  114. {
  115. string viewState = string.Empty;
  116. Match match = new Regex(regex).Match(loginHtml);
  117. if (match.Success)
  118. {
  119. viewState = match.Value.Replace(replaceLeft, "");
  120. viewState = viewState.Replace(replaceRight, "");
  121. }
  122. return viewState;
  123. }

吐槽一下自己,这个类其实可以优化,比如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.签到成功提示

五. 后话        

在这个浮躁的世界里,我们不可以浮躁。人要有梦想,不然跟条咸鱼有什么分别。虽然我在追求自己喜欢的生活方式上有着各种阻碍,但是我还是认为,以自己喜欢的方式生活才是最开心的。人就一辈子,只要能承担责任,还有什么必要跟自己过不去呢?昨天我朋友从凤凰古城给我寄来一张明信片,我感慨万千,于是回复了一句:祝前程似锦,山明水秀,兄弟情谊,万古长青。切记,莫屈服,要以自己喜欢的方式生活。

也献给你们,亲爱的园友。

 
 
分类: XAML
标签: WPFModern UI

OA小助手的更多相关文章

  1. (转)基于 WPF + Modern UI 的 公司OA小助手 开发总结

    原文地址:http://www.cnblogs.com/rainlam163/p/3365181.html 前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个 ...

  2. 基于 WPF + Modern UI 的 公司OA小助手 开发总结

    前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会发现自己的不足. 公司每天都要在OA系统上上班点击签到,下班点击签退 ...

  3. 发布代码小助手V2.1发布了——Code2HTML工具

    设计起源: 新浪博客似乎没有插入代码的功能,所以不得不用打空格的方法格式化代码.而且没法显示行号. 描述: 发布代码小助手用python和Tkinter开发,可以在任何常见操作系统上运行.主要用于在不 ...

  4. 书签小助手V1.1发布了

    更新信息: 1.修改了部分BUG;2.添加了一些不错的网站:3.重新设计了添加书签和编辑书签的界面. 安装说明: 类Ubuntu系统: 1.安装Python3解释器和Python3-tk sudo a ...

  5. 环境监测小助手V1.1的Windows版

    环境监测小助手V1.1——可以实时查看空气质量和城市排名 一款跨平台空气质量监测软件 数据来源互联网,请联网使用. 暂不支持效果预览. 下载地址:http://files.cnblogs.com/py ...

  6. Windows版词汇小助手V3.0发布了

    欢迎使用词汇小助手 作者:IT小小龙 电子邮箱:long_python@126.com 个人博客:http://blog.sina.com.cn/buduanqs 一款跨平台词汇查询记忆学习软件. 已 ...

  7. 词汇小助手V3.0发布了——不只是一个查单词的软件

    欢迎使用词汇小助手 作者:IT小小龙 电子邮箱:long_python@126.com 个人博客:http://blog.sina.com.cn/buduanqs 一款跨平台词汇查询记忆学习软件. 已 ...

  8. 生日小助手V4.0——迁移到Python3

    生日小助手V4.0——迁移到Python3 生日小助手V4.0只支持Linux系统,依赖命令行软件lunar Ubuntu系统安装方法:1.安装lunarsudo apt-get install lu ...

  9. 通达OA 小飞鱼工作流在线培训教程文件夹及意见征集

    最近通达OA技术交流群有不少朋友反映说表单设计这块 改动样式的问题,这块须要html和css的改动.本来最近正好要在工作流这块准备做一个系列的课程,都是基础的设置主要是给刚接触工作流的朋友用的,大家有 ...

随机推荐

  1. 从头开始学JavaScript (十一)——Object类型

    原文:从头开始学JavaScript (十一)--Object类型 一.object类型 一个object就是一系列属性的集合,一个属性包含一个名字(属性名)和一个值(属性值). object对于在应 ...

  2. jQuery自定义右键菜单

    首先看下效果,效果在最下面: 代码: body { font-size: 12px; margin: 0px; padding: 0px; } form,div,ul,li { margin: 0px ...

  3. oracle_oracle中修改日期的显示格式

    我的现在的日期格式是          ,要改成英文的需要输入一下命令: ALTER SESSION SET NLS_DATE_LANGUAGE=AMERICAN; 修改后变为: 同样也得若是英文要想 ...

  4. 为代码减负之&lt;一&gt;触发器(SQL)

    对触发器一词早有耳闻(最早是在耿大妈的数据库视频中),当初看完视频后,对理解不深刻的东西如:触发器,存储过程,事务,日志等等没有具体的去查阅,也没有具体的去尝试,应用.所以才导致了今天的博客(把曾经丢 ...

  5. php_ThinkPHP的RBAC(基于角色权限控制)详解

    一.什么是RBAC 基于角色的访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注. 在RBAC中,权限与角色相关联,用户通 ...

  6. 搜集朋友写的几篇Android Elf相关的文档

    对android elf的资料学习,多数是在看雪找的资料,另一部分朋友的研究,当然,给他们提议过整理成一系列文章,只是大家工作都太忙,也都没顾上,这里简单整理放上一些pdf的资料,有兴趣的朋友能够看看 ...

  7. 如何使用Ubuntu online account API创建微博HTML5申请书

    在这篇文章中.我们将使用Ubuntu SDK提供online account API来訪问微博的API并显示所须要的内容.这篇文章的重点是展示怎样在HTML 5中使用online account AP ...

  8. Spark入门级小玩

    ·背景 随着周边吐槽hadoop的声音渐渐多起来之后,spark也逐渐进入了大家的视野.之前,笔者有粗略的写过一篇spark的安装和性能比较[http://www.cnblogs.com/zacard ...

  9. 加载xib文件的两种方式

    一.加载xib文件的两种方式 1.方法一(NewsCell是xib文件的名称) NSArray *objects = [[NSBundle mainBundle] loadNibNamed:@&quo ...

  10. C#映射网络驱动器

    using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] public class NetResource ...