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

 

五. 后話        

在這個浮躁的世界里,我們不可以浮躁。人要有夢想,不然跟條咸魚有什么分別。雖然我在追求自己喜歡的生活方式上有着各種阻礙,但是我還是認為,以自己喜歡的方式生活才是最開心的。人就一輩子,只要能承擔責任,還有什么必要跟自己過不去呢?昨天我朋友從鳳凰古城給我寄來一張明信片,我感慨萬千,於是回復了一句:祝前程似錦,山明水秀,兄弟情誼,萬古長青。切記,莫屈服,要以自己喜歡的方式生活。

也獻給你們,親愛的園友。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM