C#+ASP.NET 2.0定製複合組件之進階篇

來源:互聯網
上載者:User
asp.net|進階    一、 增加EnhancedListBox的用戶端功能

  為了使用用戶端代碼實現EnhancedListBox中項的重排序,你必須使用JavaScript指令碼,並且要把它們依附到EnhancedListBox的兩個按鈕上。為此,我建議你使用“往後考慮”的方法。就象編寫一個老式的ASP以前的Web頁面,首先編寫一些產生HTML檔案的JavaScript。為此,最好的方法是運行該控制項,然後觀察其源碼並把它的HTML代碼複製到一個編輯器,再添加JavaScript。列表2(見下載原始碼)展示了你需要添加到你的控制項中的JavaScript的原始形式。然後,藉助於StringBuilder/StringWriter技術(參考源碼列表3),該控制項構建這部分代碼。該JavaScript代碼由兩部分功能組成:接收一個HTML控制項(在本例中是一個<select>控制項);使用選擇索引並且在列表中上下移動它(基本上與我在本文開始我使用伺服器代碼向你展示的一樣)。現在,你要理解,你把該JavaScript代碼添加到Web控制項的何處。為了實現在一個Web表單上有多個EnhancedListBox控制項的情況下,該JavaScript代碼不會被重複復制,你需要使用Page.ClientScript對象的RegisterClientScriptBlock方法輸出它。

  要使這個方法起作用,你必須在重載的OnInit事件中調用它(見源碼中列表4)。

  最後,為使按鈕正確工作,你需要把添加的用戶端方法依附到其上。在列表1中的代碼中,你會看到引用了一個方法RenderButtons。儘管我沒有把該代碼在此列出(請參考本文相應源碼),但是它能夠使用我在以前文章中介紹的技術產生按鈕。當時,在產生實際HTML標籤的之前,標籤屬性是使用AddAttribute方法以棧式存放的。在此,你使用一樣的技術把用戶端方法依附到你的按鈕。

string s_MoveUp = "MoveItemUp(document.all." +this.ClientID + ");
output.AddAttribute(HtmlTextWriterAttribute.OnClick,s_MoveUp);
  記住,MoveItemUp是你已經編寫成功的JavaScript函數之一。在產生用於排序的按鈕之前,該代碼將以堆棧存放這些JavaScript命令。對於向下(down)按鈕,你使用一樣的技術。注意,我使用ClientId代表該產生後的控制項的ID;但是,在這個控制項位於一個複合控制項內部時,這個屬性要考慮使用父控制項的名字。

  現在,你可以成功地把該控制項應用於一個Web表單中。你可以使用與你操作一個標準ListBox控制項一樣的方式在其上添加一些項。

  事實上,這完全是一個投放位置預留位置(或ASP.NET ListBox控制項)。當你使用重排序按鈕時,你將看到列表中的項相應地改變順序。現在讓我們先記下這個問題。如果你把一個按鈕拖動到一個Web表單上(不需要為之添加代碼)並執行一個回寄,你猜會發生什麼呢?完全與我以前描述的一樣;任何你使用重排序按鈕作的重排序改變都將恢複到在最近一次回寄之前該控制項看上去的狀態。因此,讓我們修改一下這個問題。

  首先,我再添加一些JavaScript(源碼列表5)。注意,這部分代碼被添加到重載的OnInit方法中並且使用StringBuilder/StringWriter技術進行構建;而且,這個JavaScript方法的名字是BuildItemList。這個函數負責構建列表框完整內容的一個字串描述並且把該串放到要傳遞到該函數的一個HTML元素的value屬性中。你可以把這看作是列表內容的一種序列化。該序列化的輸出風格會根據你自己的設計的不同而有所不同。調用這個JavaScript函數需要依附到該按鈕上的其它代碼。

string s_MoveUp = "MoveItemUp(document.all." + this.ClientID + "); ";
string s_BuildItemList ="BuildItemList(document.all." + this.ClientID +
",document.all.__" + this.ClientID + "); ";
output.AddAttribute(HtmlTextWriterAttribute.Onclick,MoveUp + " " + BuildItemList);
  現在,讓我們來分析一下你發送到BuildItemList函數的兩個參數。第一個參數相應於產生的控制項(<select>標籤)的ID。第二個參數是另外一個ID,與前一個命名一致,但是前面有一個"__"。這是一個你仍然需要添加到你的Web控制項的隱藏的文字框,它將作為一個“序列化”項列表的預留位置。我要在OnPreRender事件中註冊這個隱藏的文本域。

protected override void OnPreRender(EventArgs e)
{
  base.OnPreRender(e);
 if(Page != null)
 {
  Page.ClientScript.RegisterHiddenField("__" + this.ID, "");
 }
}

  注意,我已經使用我們的控制項的ID來標識隱藏的文本域。

  到目前為止,你已經擁有了一個完整功能的Web控制項;其中,用戶端JavaScript被綁定到其中的兩個按鈕上。該JavaScript成功地實現在ListBox中的項的重排序並且把其內容序列化為一個字串;然後,該字串被儲存在一個隱藏的文本域中。所有這些都發生在用戶端。如果一個回寄發生,不會發生重排序,因為當重排序時控制項的Item伺服器屬性還沒有收到你對它作的任何改變的訊息;但是幸運的是,位於隱藏的文本域中的表單的一個序列化快照中發生了這一變化。現在,你有了可以與Item屬性一起使用的內容了。那麼,接下來,你該如何?呢?

  二、 同步

  為了在第一次回寄和所有隨後的回寄中實現同步,ASP.NET在IPostBackDataHandler介面的實現中提供了一個LoadPostData方法。在每一次回寄時都要調用這個LoadPostData方法;因此,你需要在此做一些工作。

  值得一提的是,ASP.NET 2.0修整了一個在1.1版本中被忽視的小地方,然而這一修改能夠使你的工作容易許多。ASP.NET ListBox控制項已經在兩個版本(1.1和2.0)中實現了IPostBackDataHandler介面。但是在2.0版本中,微軟使這個介面的方法定義虛擬化(virtual,在VB中稱作Overridable)。這意味著,你不必在EnhancedListBox控制項中重新實現這個介面;而是,你僅需重載LoadPostData方法。

  更重要的是,這也意味著,你可以存取基類實現而不必建立所有已經存在於你的擴充控制項中的功能。什麼功能呢?這包括微軟加於其中的一切:用於處理Item集合,SelectedIndex,SelectedValue和SelectedItem屬性,及其它許多執行ListBox控制項功能的代碼。在ASP.NET 1.1中,你必須在你的派生控制項中實現這個介面並且要提供你自己對這兩個方法的定義代碼,不僅包括你自己的加入的代碼而且還要重複微軟已經在其控制中所實現的一切。

  我猜測,微軟有人已經發現了他們的實現中的錯誤,並且把方法變為virtual的,這樣開發人員能夠存取基類的代碼。因此,在源碼列表6中向你展示如何?重載的LoadPostData方法。在這個重載中,你將首先調用基類實現代碼;然後,加上你需要的代碼以與Item集合約步。

  另外,你還可以利用ListBox控制項—通過把它編寫成一個複合控制項。此時,你需要把ListBox中的每一個屬性對應到你的EnhancedListBox以便使它成為ListBox控制項的一個投放位置點位符。無論使用哪一種方法,或者通過LoadPostData方法的重新建立,你都仍然需要寫很多代碼。如果我專門為ASP.NET1.1編寫這個控制項,那麼我很可能採取最直接的方案:複合控制項方案。

  LoadPostData方法使你能夠存取寄送到伺服器的每一個域,包括你的隱藏文本域(儲存在要傳遞到這個方法的postCollection參數中)。你可以問:為什麼需要該隱藏文本域,而不是使用這個參數來存取被回寄的<select>元素呢?現在,我作一下解釋。首先,回顧一下典型的ASP時代,當時你使用Request.Form屬性來存取頁面域。在回寄時,你能夠存取一個<select>元素的唯一的部分是選擇的項。在該方案中,你需要完整的列表內容(因此,包括隱藏的文本域)。列表6向你展示如何分析該隱藏的文本域的內容並且把Item重新添加到Item集合中。注意,你是怎樣調用基類實現的。

  最後,在你第一次產生控制項時,你必須構建這個隱藏的文本域,以防在任何重排序前發生頁面回寄。Render方法的最後一行是:

output.Write("<script language='javascript'>BuildItemList(document.all." + this.ClientID +",document.all.__" + this.ClientID +");</script>");
  你可以在列表1的最後看到這一點。

  現在,你可以使用EnhancedListBox控制項來重排序一些項,回寄,並且確保在重建頁面前,控制項的伺服器儲存與在用戶端被改變的用戶端儲存完全同步。因此,現在讓我們使用相同的技術來構建一個複合控制項ListMover。

  三、 構建複合控制項—ListMover

  這個ListMover控制項包含兩個EnhancedListBox控制項,還有一些按鈕用於在兩個列表之間來回移動項。藉助於這些複合控制項構建技術,你可以學習如何建立子控制項並且使用一些HTML產生它們,最終的控制項看上去如圖2所示。對於這個控制項,你要注意的是某些事情必鬚髮生的位置。


圖2.這個ListMover控制項提供了一種標準方式讓使用者在兩個列表間移動項。


  首先,藉助於與在以前的控制項中相同的技術,你必須把在這個控制項中需要的JavaScript代碼添加到OnInit事件的重載版本中。列表7顯示了你需要的JavaScript代碼。如你在上一個控制項中所做的一樣,你也是使用JavaScript存取一個ListBox(<select>元素)中的元素。而且,我已經編製了函數分別實現把項添加到一個列表,從一個列表中刪除項,以及從一個列表中添加或刪除所有項。

  我已經進行了功能的分離,而不是建立單個“move”方法;這樣以來,我可以實現基於屬性設定而使得從一個列表中刪除項成為可選的。毫無疑問,這可以使最終的控制項更為強壯些,但是我在本文中不再分析這些代碼。還應該注意,就象在前面控制項中一樣,我也添加了一個BuildItemList方法。

  現在,你需要把這一用戶端代碼依附到複合控制項的按鈕中。你可以在CreateChildControls方法的最後完成這一點;並且,在此時,完成子控制項的初始化和構建控制項集合。在此,我僅向你展示相應於一個按鈕的代碼(另外的按鈕代碼與此類似,省略)。

string s_AddToLeft = "AddSelectedItemToList(document.all." +
this.lstItemsOnRight.ClientID + ", document.all."
+ this.lstItemsOnLeft.ClientID + ", " +
(this.AllowDuplicatesOnLeft ? "true" : "false") + "); ";
string s_RemoveFromRight = "RemoveSelectedItemFromList(document.all." +
this.lstItemsOnRight.ClientID + "); ";
string s_BuildItemList = "BuildItemList(document.all." +
this.lstItemsOnRight.ClientID + ", document.all.__" + lstItemsOnRight.ClientID + "); " + "BuildItemList(document.all." + this.lstItemsOnLeft.ClientID + ", document.all.__"
+ lstItemsOnLeft.ClientID + "); ";
this.btnAdd.Attributes.Add("onclick", s_AddToLeft
+ " " + s_RemoveFromRight + " " + s_BuildItemList
+ " return false");
  注意,我實現了在以前的控制項中同樣的工作。我把JavaScript函數調用構建成一字串並且把它們依附到一個按鈕上。主要區別在於,既然這是一個包含其它控制項的複合控制項,那麼你可能使用把代碼添加到onclick事件的Attributes.Add方法,這與在一個產生控制項中把它放到一個棧上的方法形成對照。還要注意,我把多個功能放到onclick屬性中;而且,函數調用的最後返回false以便取消按鈕將執行的任何回寄。

  最後,代碼將在一個對Render重載的方法中初始化對用戶端函數BuildItemList的調用。這看起來很象我在EnhancedListBox控制項中向你介紹的那個,在此不再重複。注意,在這個控制項中,我註冊了兩個隱藏的文本域,每一個相應於一個ListBox。

protected override void OnPreRender(EventArgs e)
{
 base.OnPreRender(e);
 if(Page != null)
 {
  Page.ClientScript.RegisterHiddenField("__" + this.lstItemsOnRight.ClientID,
"");
  Page.ClientScript.RegisterHiddenField("__" + this.lstItemsOnLeft.ClientID,
"");
  Page.RegisterRequiresPostBack(this);
 }
}
  現在,你已經構建成功該複合控制項,能夠提供一些用戶端JavaScript,並且把它綁定到按鈕上。與以前一樣,你可以把它放到一個表單上並且使用它;但是,在你添加同步代碼之前,它仍將會遇到你在第一個控制項中所遇到的問題—你可以前後移動項,但是一旦你初始化一個回寄(通過表單上的任何其它控制項),該控制項就會恢複到它回寄之前的狀態。

  為了修改這個問題,你要實現你在第一個控制項中所做的同樣的工作。然而,既然你在開發一個複合控制項,而不是擴充一個已經現有的控制項,那麼你需要實現IPostBackDataHandler介面並且提供LoadPostData和RaisePostDataChangedEvent方法的實現代碼。這些實現(見列表7)與前面的控制項基本一致,除了你要實現兩個EnhancedListBox控制項中的項集合的同步而不是只考慮一個控制項外。並且與以前一樣,你需要確保你儲存你的SelectedIndex位置;這樣以來,在你完成項集合的同步後你就可以把它們設定回去。還要注意,在第一個控制項中,你重載了基控制項的LoadPostData方法,因此在某處調用了它的基類。現在,既然你要從頭編寫一個複合控制項,那麼就沒有基類可調用,而僅需提供你自己的方法實現。

  這個控制項的最後版本包含若干新的屬性:包括用來決定是否添加到一個列表中的項能夠被從另一個列表中刪除的屬性(如果一個列表將允許出現重複項的話);它還包含可擴充的風格化以實現最大化重用的目的,等等。

  就這些。你已經使用了可用於用戶端指令碼中的隱藏的文本域來儲存列表框的狀態。在回寄期間,你使用隱藏文本域的內容來與伺服器端項集合重新同步。最終結果是一個漂亮的複合控制項—允許你在沒有伺服器回寄的情況下實現各清單項目間的來回移動,而當一個回寄真正發生時仍能夠保持這種變化。

  四、 取二者最優

  前面我沒有提及的一個細節是為什麼我在本文中混合了兩個控制項。文章一開始,我首先向你展示了一個標準ListBox控制項的增強版本,然後把這個增強控制項的兩個執行個體應用於ListMover控制項,而沒有使用兩個標準ListBox控制項來構建這個ListMover。在本文中,我沒有涉及的是ListMover控制項的屬性部分,它們將負責映射添加到EnhancedListBox控制項上的屬性。通過這種方式,我就能夠從包含兩個EnhancedListBox控制項的ListMover控制項中控制兩個EnhancedListBox控制項的增強功能。因此,你可以看到,你擁有結合了兩個控制項的最好的功能—你有了一個ListMover控制項,它允許你在兩個列表或單個列表的各項之間進行項的移動與重排序。

  其實,這裡真正關鍵的地方在於面向Web控制項的ASP.NET開發—完全封裝。本文中的EnhancedListBox控制項包含實現其目標(對它的項進行重排序)的所有代碼。當我把兩個這種控制項包括在一個ListMover控制項中時,我可以使用所有伴隨著它們的智能性作為新控制項的額外功能,包括每一個控制項含有的用戶端指令碼以及在EnhancedListBox控制項中的用戶端到伺服器的同步功能。因此,這個ListMover控制項只需注意其自己的功能。圖3展示了和EnhancedListBox控制項在一起的ListMover控制項,其中重排序按鈕處於開狀態。


圖3.通過組合ListMover和EnchancedListBox控制項,你提供給使用者的是具有兩個列表的一個完整的控制項,這樣你可以實現在一個列表中或兩個列表的各項間進行移動。


  最後,我強烈鼓勵你下載本文完整的源碼進行研究。既然我編寫這些控制項,那麼我已經多次使用過ListMover,而且EnhancedListBox控制項在我的所有工程開發中幾乎可以替換標準的ASP.NET ListBox控制項。

   五、 小結

  我在本文中向你展示了如何在一個Web控制項內保持在伺服器和用戶端之間的同步,而且這裡所使用的技術可以適用於許多類似的情況。儘管ASP.NET 2.0中引入了一個直觀的介面用以處理指令碼回調,但是,使功能完全位於用戶端則是一種最快的解決方案而且能夠與ASP.NET 1.1保持相容。說實在的,我更對ASP.NET 2.0中內建的回調能力感興趣,請你也一起研究吧。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.