2009-10-23 59 views
1

我是MVC的新手,但我已經完成了所有這些工作,閱讀所有文檔和所有問題以及所有可以找到的博客文章,而我所做的所有工作都是獲取完全纏繞在軸上。在MVC中堅持選定的值DropDownLists

我試圖做一個「創建」動作和視圖。我的數據輸入相對簡單,並且很常見:我有一個下拉列表和一個文本框。在我的情況下,我創建了一個用戶聯繫信道,然後在電子郵件和textmsg之間選擇下拉框,然後文本框輸入相關的聯繫信息,包括格式正確的電子郵件地址或移動電話號碼。

下面是一個(略簡化形式)我查看網頁:

<tr> 
    <td><%= Html.DropDownList("ChannelDescription", Model.ChannelDescription, "Select a Channel", new { id = "ChannelDDL", onchange="ChannelDDLChanged()" })%> 
     <br /> 
     <%= Html.ValidationMessage("ChannelDescription", "Please Select a Channel") %> 
     </td> 
     <td> 
      <%= Html.TextBox("SubscriberNotificationAddr") %> <br /> 
      <%= Html.ValidationMessage("SubscriberNotificationAddr", "Please enter a contact address or number") %> 
     </td> 
    </tr> 

我使用的是強類型的ViewData模型,而不是使用的ViewDataDictionary。 ChannelDescription元素是一個SelectList,它通過選擇列表進行初始化,並且沒有選擇。

窗體的初始顯示,窗體中的數據輸入以及控制器從窗體中提取數據都很順利。

我的問題是,如果數據包含錯誤,如錯誤形成的電子郵件地址或手機號碼,我必須返回到視圖,我沒有成功獲取下拉列表選擇重新顯示。 ChannelDescription元素在控制器中重新創建,用戶選擇作爲選定項目。我在該視圖的該行上設置了斷點,並驗證了項目列表的選定元素的Selected屬性設置爲true,但仍顯示默認的「選擇頻道」。

這似乎是一個非常普遍的情況,不應該這麼難。我究竟做錯了什麼?僅供參考,這是在Firefox 3.5.2下運行的MVC 1.0(發行版),Windows 7和VS 2008。

回答

1

我曾與布拉德·威爾遜一些討論,從MVC團隊,他向我解釋說我誤解了應該如何使用DropDownList幫助器方法(從我讀過的內容來看,我認爲這種誤解可能相當普遍)。

基本上,只要在ViewModel的命名參數中給它一個SelectList,讓它根據選擇的適當項目構建下拉列表,或者將SelectList作爲一個單獨的參數給它,並讓命名參數ViewModel只是所選項目的值字符串。如果你給它一個SelectList參數,那麼它期望命名值是一個字符串或字符串列表,而不是一個SelectList。所以,現在你的ViewModel最終有兩個元素用於視圖中的一個概念項目(下拉列表)。因此,你可能有一個具有

string SelectedValue {get; set;} 
SelectList DropDownElements { get; set;} 

然後你就可以預先填充與選擇的DropDownElements的模型,但在你的模型視圖結合,你只需要處理的SelectedValue元素。當我這樣做時,它似乎對我來說工作得很好。

0

是的,我也有太多的問題讓DropDownList尊重我給它的選定項目。

請檢查my answer in this question。據我所知,這是我能夠實現它的唯一途徑。通過ViewData傳遞列表。

僅供參考,我停止使用該HtmlHelper方法。我現在只需簡單地輸出<select><option>標籤,然後通過自行檢查來設置option標籤的selected屬性。

1

查看上面的答案之後,我想查看一下,因爲我看過的所有例子都確實使用了ViewDataDictionary,而不是強類型的ViewDataModel。

所以我做了一些實驗。我構建了一個非常簡單的視圖,它使用一個普通的ViewDataDictionary,並通過命名鍵傳遞值。它堅持選擇的項目就好了。然後我剪切並粘貼了View(和控制器)到另一個,只改變了切換到強類型的ViewData模型所需的內容。瞧,它也堅持選定的項目。

那麼我的簡單測試和我的應用程序有什麼不同?在我的測試中,我只用了「Html.DropDownList(」name「,」optionLabel「)」。但是,在我的應用程序中,我需要添加HTML屬性,並且包含HtmlAttributes的唯一可用重載也包含選擇列表。

事實證明,使用選擇列表參數的DropDownList超載被破壞了!查看下載的MVC源代碼,當僅使用名稱,名稱和optionLabel調用DropDownList時,它最終將從ViewData中檢索目標選擇列表,然後通過以下調用調用私有SelectInternal方法:

return SelectInternal(htmlHelper, optionLabel, name, selectList, true /* usedViewData */, false /* allowMultiple */, (IDictionary<string, object>)null /* htmlAttributes */); 

但是,如果它被稱爲一個選擇列表的參數,它結束了以下內容:

return SelectInternal(htmlHelper, optionLabel, name, selectList, false /* usedViewData */, false /* allowMultiple */, htmlAttributes); 

不同的是,在第一個(這將正常工作)的「usedViewData」參數爲真,而在第二個中,這是錯誤的。這實際上沒問題,但在SelectInternal例程中暴露了一個內部缺陷。

如果usedViewData爲false,則它將從ViewData模型中獲取一個對象變量「defaultValue」。 但是,使用defaultValue就好像它是一個字符串或一個字符串數組,實際上從ViewData返回的是一個SelectList。 (IEnumerable<SelectListItem>)。

如果usedViewData爲true,則defaultValue將爲null或字符串。

那麼,如果默認值不爲空,它最終進入包含此代碼塊:

 foreach (SelectListItem item in selectList) { 
      item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text); 
      newSelectList.Add(item); 

選擇列表的是已通過在原有的SelectList,因此該項目是一個SelectListItem(字符串文本,字符串值和布爾選定)。但selectedValues是從defaultValue派生的,併成爲SelectLists列表,而不是字符串列表。因此,對於每個項目,都是根據selectedValues列表「包含」item.Value來設置Selected標誌。那麼,SelectLists列表永遠不會「包含」一個字符串,所以item.Selected永遠不會被設置。 (更正:實際上,在對調試器進行了更多跟蹤之後,我發現selectedValues是通過「ToString()」調用從defaultValue派生的,所以它實際上是一個字符串列表,而不是包含我們想要的值包含「System.Web.Mvc.SelectList」 - 將「ToString()」應用於複雜對象(如SelectList)的結果。結果仍然相同 - 我們不會找到我們要查找的值列表)。

然後它將新構建的「newSelectList」替換爲原始的「selectList」,並繼續從中構建HTML。

作爲cagdas(我爲你的名字屠殺感到抱歉,但我不知道如何在我的美國鍵盤上製作這些角色),我想我必須建立自己的方法來代替DropDownList HtmlHelper。我猜是因爲這個發行版本1和發行版本2在Beta 2中,除非我們自己做對了,否則我們不能真正期望修復任何錯誤?

順便說一句,如果你追了我這麼遠,這個代碼在src \ SystemWebMvc \的mvc \ HTML \ SelectExtensions.cs,大約線116-136