2009年12月11日 星期五

GridView 的RowIndex


用GridView好一陣子了,之前都會搭配ObjectDataSource一起使用,所以在使用Update Command的時候,只需要ObjectDataSource中有對應的Update Method就可以很容易執行Update的方法,這中間躲掉了一個如果我想要點選自訂Command的按鈕時,該如何才能輕鬆抓到RodIndex的問題。



試想一下哪些狀況會需要抓取RowIndex呢?主要情形有兩種
第一、當你有對GridView設定DataKeys屬性時,你可以透過這個屬性來取到該Row的鍵值,範例程式碼如下

void ContactsGridView_RowCommand(Object sender, GridViewCommandEventArgs e)
  {
    if(e.CommandName=="Add")
    {
      // Convert the row index stored in the CommandArgument
      // property to an Integer.
      int index = Convert.ToInt32(e.CommandArgument);
      string myKey = ContactsGridView.DataKeys[index].Value;
     }
  }
上述程式碼的e.CommandArgument可以使用的前提是該按鈕設定如下
asp:buttonfield buttontype="Link" 
                  commandname="Add" 
                  text="Add"

第二、是你想要抓取該選取列之其他欄位的值 ,範例程式碼如下

void ContactsGridView_RowCommand(Object sender, GridViewCommandEventArgs e)
  {
    if(e.CommandName=="Select")
    {
      // Convert the row index stored in the CommandArgument
      // property to an Integer.
      int index = Convert.ToInt32(e.CommandArgument);
      string TheValue = ContactsGridView.Rows[index].Cell[2].Text;
     }
  }
 
上述程式碼的e.CommandArgument可以使用的前提是該按鈕設定如下asp:buttonfield buttontype="Link" 
                  commandname="Select" 
                  text="Select" 


上述兩種狀況都在說明都需要使用buttonfield則GridView才會在GridViewCommandEventArgs中的CommandArgument設定該RowIndex,但這樣設定預設也會驅動GridView的Select以及Add事件,如果我們想要使用客製化的按鈕名稱,並不想驅動GridView中預設的事件時,則我們會在GridView中使用樣板的方式,加入自己想要的按鈕然後設定其CommandName,但經過這樣設定,CommandArgument中將不會再有RowIndex的資訊,如下
void ContactsGridView_RowCommand(Object sender, GridViewCommandEventArgs e)
  {
    if(e.CommandName=="MySelect")
    {
      //下面將取不到RowIndex
      //int index = Convert.ToInt32(e.CommandArgument);
      
      GridViewRow Selectedrow =  (GridViewRow)(((LinkButton)e.CommandSource).NamingContainer); 
       string TheValue = Selectedrow.Cell[2].Text;
     }
  }
但是你可以使用CommandSource的方式,先取得該按鈕,然後經由該按鈕的NamingContainer抓到該按鈕所位處的Row。其實透過該方法,還是不會知道RowIndex,但原本想要抓取RowIndex也是想要透過該Index抓取所選取的欄位中的值,所以透過CommandSource的方式也算是能達到我們的需求。不過我個人覺得透過這樣的方式抓取未免太不直覺,而且照理說要解決這樣的方式,應該還是要想辦法把RowIndex的資訊想辦法塞到CommandArgument中。記得兩年前尋找該方法的時候,於MSDN中是採取透過GridView的RowCreated事件,在該事件中將RowIndex塞入我們要客製的按鈕的CommandArgument屬性中,不過這個方法太麻煩了,需要再多寫一個事件,如果我以後想要撰寫類似的程式,使用Copy,Past大法時就很麻煩,需要先複製aspx上面的GridView宣告,然後再複製CodeFile中實做的事件程式碼,而且如果按鈕的ID取名不一樣,那就兩邊都需要同時再做修正。
 
廢話不多說,好久總是沉甕底,今天再次尋找該方法的時候MSND裡出現了另一個解決方案(當然我不知道他什麼時候改的),參考網址GridView..::.RowCommand Event ,主要是在宣告區域中採用底下方法(請注意CommandArgument屬性)                            
              "server" ID="IncreaseButton"
                Text="Increase Price 5%"
                CommandName="Increase"
                CommandArgument="<%# ((GridViewRow) Container).RowIndex %>" />


透過所謂延遲繫結的方式,將RowIndex直接設定給CommandArgument,相信這種方法其實早就該有了,只不過是早想到還是晚想到的問題而已。
這是MDSN中Note:的區段,貼下來留念一下
To determine the index of the row that raised the event, use the CommandArgument property of the event argument that is passed to the event. The ButtonField class automatically populates the CommandArgument property with the appropriate index value. For other command buttons, you must manually set the CommandArgument property of the command button. For example, you can set the CommandArgument to <%# Container.DataItemIndex %> when the GridView control has no paging enabled.
 
結論:
延遲繫結的技巧其實我也算常常用,不過卻很少會使用GridView中預設的屬性,例如範例中的Container,所以對他真的很不熟,下面列出找到的相關參考連結。 GridView..::.RowCommand Event
IDataItemContainer Members
How to: Access Members of a Control's Naming Container
How to: Access Members of a Web Server Control's Naming Container

沒有留言: