基於 WPF + Modern UI 的 公司OA小助手 開發總結

 

前言:html

距離上一篇博客,整整一個月的時間了。人不能懶下來,必須有個階段性的總結,算是對我這個階段的一個反思。人只有在總結的過程當中纔會發現本身的不足。前端

公司天天都要在OA系統上上班點擊簽到,下班點擊簽退,天天都要寫工做日誌。有的時候頭腦不清醒或者忙過頭了(別說大家沒有過),就會忘記簽到或者簽退,有時候甚至忘記寫工做日誌。這會直接致使扣人工啊有木有,因此我纔有了這個想法。首先聲明,開發這個東西並非博主對工做不認真不負責任,也並非偷懶。相反,第一,能夠避免因工做過忙忘記簽到扣工資;第二,在開發的過程當中你學到的東西是快速的,有趣的,讓本身受益的。對於每一個公司來講,OA系統都是他們的公司機密,因此博主並不會貼源碼,只在這裏闡述一個開發流程與思想,讓你感受到作一個本身以爲有趣的產品,思想的火花是多麼難以想象。web

 

一. 用到的模板與技術正則表達式

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/,博主再也不累贅。cookie

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.簽到成功提示

 

五. 後話        

在這個浮躁的世界裏,咱們不能夠浮躁。人要有夢想,否則跟條鹹魚有什麼分別。雖然我在追求本身喜歡的生活方式上有着各類阻礙,可是我仍是認爲,以本身喜歡的方式生活纔是最開心的。人就一生,只要能承擔責任,還有什麼必要跟本身過不去呢?昨天我朋友從鳳凰古城給我寄來一張明信片,我感慨萬千,因而回復了一句:祝前程似錦,山明水秀,兄弟情誼,萬古長青。切記,莫屈服,要以本身喜歡的方式生活。

也獻給大家,親愛的園友。

相關文章
相關標籤/搜索