原文地址:http://www.cnblogs.com/rainlam163/p/3365181.html

前言:

距离上一篇博客,整整一个月的时间了。人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思。人只有在总结的过程中才会发现自己的不足。

公司每天都要在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 = 10000;
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小助手 开发总结的更多相关文章

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

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

  2. OA小助手

    基于 WPF + Modern UI 的 公司OA小助手 开发总结 前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会 ...

  3. WPF Modern UI 主题更换原理

    WPF Modern UI 主题更换原理 一 . 如何更换主题? 二 . 代码分析 代码路径 : FirstFloor.ModernUI.App / Content / SettingsAppeara ...

  4. VSTO学习笔记(七)基于WPF的Excel分析、转换小程序

    原文:VSTO学习笔记(七)基于WPF的Excel分析.转换小程序 近期因为工作的需要,要批量处理Excel文件,于是写了一个小程序,来提升工作效率. 小程序的功能是对Excel进行一些分析.验证,然 ...

  5. 通达OA 小飞鱼开发培训第四讲 工作流介绍(图文)

    本次课程主要解说了OA工作流相关内容,有些涉及到工作流的程序开发假设对工作流不熟悉也是有非常大难度,因此在这里进行了内容补充.   1.工作流介绍

  6. WCF/WPF公司内部订餐程序开发

    WCF/WPF公司内部订餐程序开发 (服务端篇) 上班的第一天,群里讨论关于订餐的问题,所以想到了要不要自己开发一个公司内部的订餐系统呢?方便公司内部员工的订餐,有了想法就简单的实践了下 . 实现还是 ...

  7. WPF MVVM UI分离之《交互与数据分离》

    在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架. 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下面的问题: 删除操作,假如需要先执行一部分数据的处理,然后删除界 ...

  8. 基于Vue的Ui框架

    基于Vue的Ui框架 饿了么公司基于vue开的的vue的Ui组件库 Element Ui 基于vue pc端的UI框架 http://element.eleme.io/ MintUi 基于vue 移动 ...

  9. WPF MVVM UI分离之《交互与数据分离》 基础才是重中之重~delegate里的Invoke和BeginInvoke 将不确定变为确定系列~目录(“机器最能证明一切”) 爱上MVC3系列~全局异常处理与异常日志 基础才是重中之重~lock和monitor的区别 将不确定变成确定~我想监视我的对象,如果是某个值,就叫另一些方法自动运行 将不确定变成确定~LINQ DBML模型可以对

    WPF MVVM UI分离之<交互与数据分离>   在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架. 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下 ...

随机推荐

  1. Java 传递可变参数和方法重载

    形式:类型... 参数名 示例:public void show(int... a) {}; 可变参数在方法中被当作数组来处理 可变参数传值的四种方式: 一个值也不传,可变参数会接收到长度为0的数组 ...

  2. 根据wsdl文件生成WebService客户端代码

    有时候在项目中,一个项目可能有好几个公司在做.系统之间难免会出现互相调用接口的现象,这时候有一种办法就是使用webService.本篇文章将介绍如何将对接系统提供的WebService接口,根据对方提 ...

  3. POJ2478(欧拉函数)

    Farey Sequence Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 15242   Accepted: 6054 D ...

  4. Object-C 多线程中锁的使用-NSLock

    在多线程的编程环境中,锁的使用必不可少! 于是,今天来总结一下为共享资源加锁的操作方法.   一.使用synchronized方式   //线程1 dispatch_async(dispatch_ge ...

  5. Javascript 推荐一个图形化展示库

    觉得这个库不错: http://almende.github.io/chap-links-library/index.html

  6. jQuery笔记——基础知识

    jQuery是一个JavaScript库,它通过封装原生的JavaScript函数得到一整套定义好的方法.在jQuery程序中,不管是页面元素的选择.内置的功能函数,都是美元符号“$”来起 始的.而这 ...

  7. mysql跟踪执行的sql语句

    修改my.cnf配置文件 /usr/local/mysql/bin/mysql --verbose --help | grep -A 1 'Default options' Default optio ...

  8. Halcon学习之两幅图像处理

    sub_image ( ImageMinuend, ImageSubtrahend : ImageSub : Mult, Add : ) 对两幅图像做减法   g' := (g1 - g2) * Mu ...

  9. linux启动优化:mdev&nbsp;-s自从…

    转载请说明出处:http://control.blog.sina.com.cn/admin/article/article_add.php 近期工作中遇到的问题如下: linux启动后加载根文件系统时 ...

  10. RequestParam注解的Url参数被省略时该如何处理

    RequestParam注解的Url参数被省略时该如何处理 1.RequestParam的用法 ==================== RequestParam注解可以把包含在Url中的参数映射到U ...