基于 WPF + Modern UI 的 公司OA小助手 开发总结
?
前言:
距離上一篇博客,整整一個(gè)月的時(shí)間了。人不能懶下來(lái),必須有個(gè)階段性的總結(jié),算是對(duì)我這個(gè)階段的一個(gè)反思。人只有在總結(jié)的過(guò)程中才會(huì)發(fā)現(xiàn)自己的不足。
公司每天都要在OA系統(tǒng)上上班點(diǎn)擊簽到,下班點(diǎn)擊簽退,每天都要寫(xiě)工作日志。有的時(shí)候頭腦不清醒或者忙過(guò)頭了(別說(shuō)你們沒(méi)有過(guò)),就會(huì)忘記簽到或者簽退,有時(shí)候甚至忘記寫(xiě)工作日志。這會(huì)直接導(dǎo)致扣人工啊有木有,所以我才有了這個(gè)想法。首先聲明,開(kāi)發(fā)這個(gè)東西并不是博主對(duì)工作不認(rèn)真不負(fù)責(zé)任,也并不是偷懶。相反,第一,可以避免因工作過(guò)忙忘記簽到扣工資;第二,在開(kāi)發(fā)的過(guò)程中你學(xué)到的東西是快速的,有趣的,讓自己受益的。對(duì)于每個(gè)公司來(lái)說(shuō),OA系統(tǒng)都是他們的公司機(jī)密,所以博主并不會(huì)貼源碼,只在這里闡述一個(gè)開(kāi)發(fā)流程與思想,讓你感覺(jué)到做一個(gè)自己覺(jué)得有趣的產(chǎn)品,思想的火花是多么不可思議。
?
一. 用到的模板與技術(shù)
1. WPF
相比傳統(tǒng)的WinForm,WPF真是太強(qiáng)大了,無(wú)論在UI還是在多線程的處理上,以及一些其它的改進(jìn),都預(yù)示著WinForm將被WPF取代(這只是理論上,事實(shí)上,因?yàn)楹芏喈a(chǎn)品都是多年前開(kāi)發(fā)的,用的是WinForm,如果要整個(gè)框架移植到WPF將是一件痛苦的事,反正產(chǎn)品沒(méi)功能上的問(wèn)題,這個(gè)移植就顯得沒(méi)必要了。所以目前,很多公司依然使用著WinForm的技術(shù),開(kāi)發(fā)者都在這個(gè)基礎(chǔ)上對(duì)產(chǎn)品縫縫補(bǔ)補(bǔ),更沒(méi)有機(jī)會(huì)接觸WPF了,就算是會(huì)這門技術(shù)的人,也找不到這個(gè)職位。比如我的公司就是)。本軟件全面采用WPF技術(shù),使用XAML布局以及做一些增強(qiáng)用戶體驗(yàn)的動(dòng)畫(huà)。
2. Modern UI
Modern UI 是基于WPF的一個(gè)開(kāi)源項(xiàng)目,托管在 code plex 上。你可以參考以下方法把 Modern UI 的模板添加到你的 Visual Studio 上:
- 在Visual Studio 2012中,打開(kāi)擴(kuò)展管理器(工具?>?擴(kuò)展和更新)
- 選擇在線?>?Visual Studio庫(kù)和搜索“?現(xiàn)代UI?“
- 選擇現(xiàn)代為WPF的UI模板,然后單擊“?下載“,下載并安裝。
關(guān)于 Modern UI 的介紹和使用,請(qǐng)參考http://mui.codeplex.com/,博主不再累贅。
3. 多線程
任何一個(gè)涉及到下載數(shù)據(jù)的程序都應(yīng)該使用多線程編程,我們另開(kāi)一個(gè)線程去下載數(shù)據(jù)的話,界面就不會(huì)有“假死”現(xiàn)象,用戶體驗(yàn)顯著提升。在WPF中,如果要在子線程中獲取或者設(shè)置界面UI的值也是很簡(jiǎn)單的事情,這個(gè)WPF都為我們處理好了,很方便使用。
4. Lambda表達(dá)式
Lambda表達(dá)式是一個(gè)匿名方法,你不必再為只使用一次的方法獨(dú)立寫(xiě)成一個(gè)函數(shù)(比如委托)。在WPF中在子線程里獲取界面控件的值的時(shí)候就使用了Lambda表達(dá)式。Lambda表達(dá)式是委托的實(shí)現(xiàn)方法。
5. MVVM設(shè)計(jì)模式
MVVM 是 Model-View-ViewModel 的縮寫(xiě),看字面你就能想到是什么意思吧。使用它的好處是,如果綁定的數(shù)據(jù)上下文改變了,會(huì)自動(dòng)通知UI做出相應(yīng)的更改。這也是比WinForm進(jìn)步的地方。對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)相當(dāng)方便。當(dāng)然MVVM不只這些內(nèi)容。
6. XML配置文件的操作
因?yàn)橐4嬗脩裘艽a、是否開(kāi)啟自動(dòng)簽到動(dòng)能、自動(dòng)簽到的時(shí)間等等數(shù)據(jù),就用到了App.config,實(shí)際上這是一個(gè)XML文件。
7. 系統(tǒng)托盤(pán)的處理
很多程序都有這個(gè)功能,主要是為了讓程序在后臺(tái)繼續(xù)運(yùn)行,以便時(shí)間到了就自動(dòng)簽到或者簽退。
8. 開(kāi)機(jī)自啟
早上一來(lái)開(kāi)機(jī)就自動(dòng)啟動(dòng),然后自動(dòng)簽到,會(huì)很爽吧,都完全不用自己動(dòng)手。主要是寫(xiě)入注冊(cè)表操作。
9. 模擬瀏覽器請(qǐng)求(重點(diǎn))
使用HttpWatch來(lái)抓包,使用HttpWebRequest和HttpWebResponse來(lái)模擬瀏覽器的行為,要理解HTTP請(qǐng)求協(xié)議,當(dāng)然在asp.net下還要理解asp.net網(wǎng)站與普通網(wǎng)站的差異(asp.net的原理)。asp.net的網(wǎng)站,使用服務(wù)器控件開(kāi)發(fā)的話,頁(yè)面上會(huì)有一大堆“垃圾代碼”,用來(lái)保存頁(yè)面狀態(tài),控件狀態(tài)等等信息,這些信息在發(fā)送post報(bào)文的時(shí)候也需要發(fā)送過(guò)去,而且它的值的長(zhǎng)度很長(zhǎng)。
10. 正則表達(dá)式
軟件里面大量使用了正則表達(dá)式從服務(wù)器返回的html頁(yè)面來(lái)獲取我們需要的數(shù)據(jù),比如我的工作日志列表,簽到記錄等等。關(guān)于正則表達(dá)式無(wú)非兩個(gè)類,Match/MatchCollection、Regex。有興趣的自己去了解一下正則表達(dá)式的使用,即便是做前端開(kāi)發(fā)的,也需要掌握js下的正則表達(dá)式來(lái)做客戶端的表單驗(yàn)證。
?
?
二. 核心代碼HtmlHelper
?
public static class HtmlHelper{private static string cookieHeader = string.Empty;/// <summary>/// 添加日志/// </summary>/// <param name="strUrl">請(qǐng)求的url</param>/// <param name="param">參數(shù)</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);//把數(shù)據(jù)寫(xiě)入HttpWebRequest的Request流 MyStreamWriter.Write(param);//關(guān)閉打開(kāi)對(duì)象 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-8strResult = sr.ReadToEnd();return strResult;}/// <summary>/// 功能描述:模擬登錄頁(yè)面,提交登錄數(shù)據(jù)進(jìn)行登錄,并記錄Header中的cookie/// </summary>/// <param name="strURL">登錄數(shù)據(jù)提交的頁(yè)面地址</param>/// <param name="strArgs">用戶登錄數(shù)據(jù)</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);//把數(shù)據(jù)寫(xiě)入HttpWebRequest的Request流 MyStreamWriter.Write(strArgs);//關(guān)閉打開(kāi)對(duì)象 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-8strResult = sr.ReadToEnd();return strResult;}/**//// <summary>/// 功能描述:在PostLogin成功登錄后記錄下Headers中的cookie,然后獲取此網(wǎng)站上其他頁(yè)面的內(nèi)容/// </summary>/// <param name="strURL">獲取網(wǎng)站的某頁(yè)面的地址</param>/// <param name="strReferer">引用的地址</param>/// <returns>返回頁(yè)面內(nèi)容</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-8strResult = sr.ReadToEnd();return strResult;}/// <summary>/// 獲取隱藏控件的value/// </summary>/// <param name="loginHtml">HTML頁(yè)面代碼字符串</param>/// <param name="regex">要獲取的值的正則表達(dá)式</param>/// <param name="replaceLeft">左邊要?jiǎng)h除的字符串</param>/// <param name="replaceRight">右邊要?jiǎng)h除的字符串</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
?
吐槽一下自己,這個(gè)類其實(shí)可以優(yōu)化,比如PostLogin和PostData其實(shí)可以合并,GetHiddenValue也可以寫(xiě)得更好,只是工作這邊還比較忙,剛剛接手了一個(gè)任務(wù),是把項(xiàng)目的結(jié)構(gòu)全面改版,使得每一個(gè)功能就是一個(gè)小項(xiàng)目,這樣管理起來(lái)方便很多,所以沒(méi)有時(shí)間進(jìn)行優(yōu)化,做好了自己能用就用著先,還是工作比較重要。
很明顯,里面有4個(gè)方法,每個(gè)方法的作用以及調(diào)用方式都有很詳細(xì)的注釋,我就不重復(fù)了。至于更多的代碼我就不貼了,畢竟涉及到商業(yè)機(jī)密的問(wèn)題。講講原理吧。
?
三. 原理
首先,我們知道http是無(wú)狀態(tài)連接,每次瀏覽器向服務(wù)器端發(fā)送請(qǐng)求,服務(wù)器返回?cái)?shù)據(jù)之后就斷開(kāi)了,你下一次請(qǐng)求的時(shí)候服務(wù)器并不知道你是否已經(jīng)登錄,那么asp.net下服務(wù)器怎么知道你的登陸狀態(tài)呢?當(dāng)你登陸之后,服務(wù)器會(huì)給瀏覽器發(fā)送一個(gè)cookie,用來(lái)標(biāo)識(shí)你的登錄狀態(tài),下一次請(qǐng)求的時(shí)候?yàn)g覽器會(huì)把這個(gè)cookie一同發(fā)給服務(wù)器,服務(wù)器接收到之后驗(yàn)證你發(fā)過(guò)來(lái)的cookie數(shù)據(jù),然后就知道你是否已經(jīng)登陸過(guò)。如果沒(méi)登陸,就不讓你請(qǐng)求別的頁(yè)面數(shù)據(jù)。我們可以使用HttpWebRequest和HttpWebResponse類來(lái)模擬瀏覽器的請(qǐng)求。
登陸之后,我們要寫(xiě)工作日志,就要把日志內(nèi)容拼成要提交的報(bào)文,然后post到服務(wù)器,這就是一個(gè)post請(qǐng)求過(guò)程。還有一種請(qǐng)求叫做get請(qǐng)求,這種請(qǐng)求是不提交報(bào)文的,直接發(fā)請(qǐng)求,然后服務(wù)器就會(huì)返回一個(gè)html頁(yè)面,然后我們就可以利用正則表達(dá)式來(lái)獲取我們需要的數(shù)據(jù)了。
這里有一個(gè)值得注意的地方,因?yàn)槲覀兊某绦蛞粋€(gè)掛在電腦上,以便它可以到時(shí)間后自動(dòng)簽退或者簽到,但是服務(wù)器為你保存的登錄狀態(tài)是有時(shí)間段的,如果過(guò)了一段時(shí)間你沒(méi)有請(qǐng)求操作,服務(wù)器認(rèn)為你已經(jīng)斷開(kāi)連接,不再為你保持登錄狀態(tài)(我們公司的OA系統(tǒng)似乎是半個(gè)小時(shí)),所以我們進(jìn)行一個(gè)請(qǐng)求之前要判斷一下登錄狀態(tài)是否還保持著,如果斷開(kāi)了就重新登錄一下再進(jìn)行請(qǐng)求。怎么判斷登錄狀態(tài)呢?我們公司的OA系統(tǒng)會(huì)彈出一個(gè)提示框提示身份驗(yàn)證過(guò)期,而這個(gè)提示框當(dāng)然是在html頁(yè)面上的,我們只需要請(qǐng)求一下主頁(yè),看它返回的html頁(yè)面中是否包含身份驗(yàn)證過(guò)期這個(gè)信息就行了(別說(shuō)你不知道html頁(yè)面其實(shí)就是一個(gè)字符串)。
?
四. 曬圖
1.登陸(其實(shí)只是保存了用戶名密碼,并沒(méi)有真正登陸,到需要進(jìn)行登陸操作的時(shí)候才登陸,比如提交日志、簽到、簽退、獲取日志列表、登錄信息等等,當(dāng)然并不是每次這些操作都登陸,只要登錄狀態(tài)還保持著,就不需要重新登陸了)
?
2.主頁(yè)
?
3.添加工作日志
?
4.個(gè)性化(模板自帶)
?
5.用戶信息(用于登陸)
?
6.系統(tǒng)托盤(pán)
?
7.簽到成功提示
?
五. 后話
在這個(gè)浮躁的世界里,我們不可以浮躁。人要有夢(mèng)想,不然跟條咸魚(yú)有什么分別。雖然我在追求自己喜歡的生活方式上有著各種阻礙,但是我還是認(rèn)為,以自己喜歡的方式生活才是最開(kāi)心的。人就一輩子,只要能承擔(dān)責(zé)任,還有什么必要跟自己過(guò)不去呢?昨天我朋友從鳳凰古城給我寄來(lái)一張明信片,我感慨萬(wàn)千,于是回復(fù)了一句:祝前程似錦,山明水秀,兄弟情誼,萬(wàn)古長(zhǎng)青。切記,莫屈服,要以自己喜歡的方式生活。
也獻(xiàn)給你們,親愛(ài)的園友。
轉(zhuǎn)載于:https://www.cnblogs.com/rainlam163/p/3365181.html
總結(jié)
以上是生活随笔為你收集整理的基于 WPF + Modern UI 的 公司OA小助手 开发总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 看电影玩游戏门窗隔音要用什么
- 下一篇: 如何创建一个基础jQuery插件