天天看點

簡單幹淨的C#方法設計案例:SFCUI.AjaxValue()之三

 之前講到,方法聲明為:

[email protected](story.ID, effortValue.ToString(), Effort.EffortPlannedValues, "/SFC/Efforts/AjaxSetEffortPlanned?itemID=" + story.ID + "&value={0}", ajaxOnSuccess: "refreshLeftPad");   

調用的例子:

csharp] view [email protected](story.ID, effortValue.ToString(), Effort.EffortPlannedValues, "/SFC/Efforts/AjaxSetEffortPlanned?itemID={0}&value={1}", ajaxOnSuccess: "refreshLeftPad");   

感覺上toggle、彈出菜單、UpdateTargetID什麼的都沒有啊,怎麼能實作?

這就是設計的一種重要原則:把所有技術細節都隐藏在實作的内部。

下面的代碼僅作示例,與我自己用的不太一樣,簡化了一下,隻為說明問題。

print?        public static MvcHtmlString AjaxValue(int id, string value, string[] values, string urlFormat, string ajaxOnSuccess = null)   

        {   

            string ajaxUpdateTargetID = "ValueOf" + id;   

            string innerHtml = null;   

            //Build the div to show the final value.    

            TagBuilder theValue = new TagBuilder("div"); //這個是放置原始數值的DIV,日後更新的也是它,ID在前面生成好了。    

            theValue.InnerHtml = value;   

            ...   

            //Build the ajaxLink to popup the menu of values to be selected.    

            TagBuilder ajaxLink = new TagBuilder("div"); //這個是在Value後面放一個可以點的小圖檔,點一下就會彈出菜單    

            ...            ajaxLink.AddCssClass("clickshow"); //這個是我自己定義的Class,說明點選它,會彈出一樣東西,就是是$("#clickshowBodyOfAjaxLinkOf" + $(this).attr(id)).show()。    

            innerHtml += ajaxLink.ToString();   

            //Build the div to show the popup menu; and a "clickshowbody" container to hide the menu when mouse left.    

            TagBuilder content = new TagBuilder("div"); //這個是平時隐藏,點選後會彈出的那個菜單。    

            ... //這裡隐藏的比較多,因為我調用了很多我自己的函數比如ImageLink()等,寫出來也看不到細節。    

            ... //大緻就是:在菜單裡邊foreach(var v in  values),用id 和每個v,string.Format(urlFormat, id, v)出一個AjaxLink來。    

            ... //如果OnSuccess不是null,記得AjaxLink裡邊要放上OnSuccess。     

            ... //關鍵代碼:imglink(是個<A>tagbuilder).MergeAttribute("onclick", "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: '" + ajaxUpdateTargetID + "', onSuccess: Function.createDelegate(this, " + ajaxOnSuccess + ") });");    

            innerHtml += clickshowbody.ToString();   

            return new MvcHtmlString(innerHtml);        }   

</A>   

這樣,函數裡邊就把放置Value的DIV(也就是日後會被更新的那個)、彈出菜單、彈出菜單上應該寫的東西、點選後該幹什麼、彈出後該幹什麼等等都處理了,扔到哪裡都工作。

下面是點選後彈出的效果:

<a href="http://blog.51cto.com/attachment/201212/224511250.gif" target="_blank"></a>

上面的例子基本上解決了簡單的清單式彈出視窗的情況,如果彈出來的東西比較複雜怎麼辦?比如我想把一個任務配置設定給我負責的組或我參與的組的某個組員,而且不希望這些組員直接按姓名排列(假設我記不住這些人),希望能呈現出一個下面這樣的界面來:

<a href="http://blog.51cto.com/attachment/201212/224542995.gif" target="_blank"></a>

圖中點一下人名,就能把這個任務配置設定給這個人。

這個時候千萬别先想技術實作手段,而是先要想一下:到底有幾個業務資訊要處理?其實就4個:

1. 哪個任務?任務的ID

2. 什麼資訊要改?“目前負責人”

3. 可選人員有哪些?“我的組(我負責管理的和我參與的)”

4. 設定好了要做點什麼?“重新整理一下螢幕”(主要是為了讓更新後的内容接受一下Document.Ready裡邊的JS重新刷一下)

是以函數聲明是:

print?public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null)   

public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null) 

item是那個任務

sdcTypeValue是之前定義好的自定義字段的值(不是為了這個AjaxValue定義的,SDC=System Defined Column系統自定義字段)

AjaxValue.ValuesKey是個enum稍微複雜一點。之前總是直接輸入string[]來代表未來的值,現在有可能是User[]……等等,是以幹脆傳遞一個enum由函數内部判斷這次是找什麼,裡邊再詳細處理。

urlFormat哪去了?由于AjaxSDCValue是我們專門用來處理系統自定義字段的,是以urlFormat是統一的,請看函數内部:

{   

    string value = item.ReadSDCValueOf(sdcTypeValue);   

    string urlFormat = "/SFC/Items/AjaxSetSDCValue?&lt;itemID={0}&amp;sdcTypeValue=" + SDCType.USER_CURRENT_OWNER+ "&amp;value={0}";               

 return AjaxValue(item.ID, value, valuesKey, urlFormat, ajaxOnSuccess);        }  

實際上完全使用原來的函數AjaxValue也能實作,但是向使用者暴露了過多的技術細節(尤其是形成urlFormat的式子太複雜),完全是使用者不應該關心的。

/SFC/items/AjaxSetSDCValue會把Item的USER_CURRENT_OWNER字段設定為value的值。

彈出的複雜菜單哪來的?我們總結了一下,發現AjaxValue大緻可能彈出以下幾種東西:

1. 最基本的一串字元串(原來那個string[],可以處理可用的預估值、剩餘時間值、花費時間值……)

2. 我的組的人員(配置設定任務、配置設定Bug、安排值班、安排休假……)

3. ……

urlFormat不同點選後的功能就不同,但上述菜單的結構和外觀每次都相同。

是以就做了一個AjaxController.PopupAjaxValuesMenu,會根據傳進來的AjaxValue.ValuesKey這個enum來判斷彈出哪個菜單(包括準備菜單中的資料),順便把urlFormat也傳進去。

設計和調試這一堆東西還是花了2天時間的,但是從此再也不用見到大約每次都要50行左右的C#/Html代碼了。

當未來的維護者看到這段代碼時:

[email protected](Story, SDCType.USER_CURRENT_OWNER, AjaxValue.ValuesKeys.UsersMyTeams, ajaxOnSuccess: "function() { refreshStoryPad(" + Story.ID + "); }", imageUrl: "/SFC/Users/Details16.png")   

他會想:“哦,這裡要把Story的CurrentOwner調整為……我的Team中的一個人,如果成功了會來重新整理這條故事。而且,前面似乎還顯示了個頭像圖示!”

這比面對50行漫天飛的代碼要舒服多了。

寫到最後,突然想起來10年前總結過的另一句話:

封裝的最高境界,就是用和自然語言一樣多的文字寫完調用代碼,剩下的全部隐匿起來。

本文轉自火星人陳勇 51CTO部落格,原文連結:http://blog.51cto.com/cheny/1101532

繼續閱讀