2017-02-15 61 views
0

請參閱以下更新彙總...多選DropDownListFor將不設置默認值,但多選的DropDownList確實

據我所知,使用「爲」優先HTML輔助,但我在與DropDownListFor問題使用時作爲多選。

這個例子(DropDownList的)完美的作品:

@Html.DropDownList(
    "ProtocolDisciplines", 
    new MultiSelectList(Model.Disciplines, "DisciplineId", "Discipline", Model.ProtocolDisciplines.Select(pd => pd.DisciplineId)), 
    new { @class = "form-control", multiple = "multiple", size = "8" } 
) 

這個例子(DropDownListFor)完美的作品,除了默認值(一個或多個)不被設置:基於

@Html.DropDownListFor(
    model => model.ProtocolDisciplines, 
    new MultiSelectList(Model.Disciplines, "DisciplineId", "Discipline", Model.ProtocolDisciplines.Select(pd => pd.DisciplineId)), 
    new { @class = "form-control", multiple = "multiple", size = "8" } 
) 

最新通報 什麼我正在學習我已經從原文中更新了。這是仍然不起作用的代碼。清楚的是,它正在完美地完成所有的事情,除非它在渲染時沒有選擇默認值。在我正在使用的示例中,只有一個默認值。

@Html.ListBoxFor(
    model => model.ProtocolDisciplines, 
    new MultiSelectList(Model.Disciplines, "DisciplineId", "Discipline", Model.ProtocolDisciplines), 
    new { @class = "form-control", size = "8" } 
) 

我已經取得了一定的那個學科和ProtocolDisciplines(屬於協議學科名單)(在數據庫中的所有16個學科名單)是同一類型(DisciplineViewModel)。此外,該類(見下文)僅包含2個屬性(DisciplineId和Discipline)。

我有一個斷點,模型返回到視圖,並且我已驗證Disciplines和ProtocolDisciplines具有預期的值,所以我目前主要關注視圖和ListBoxFor幫助程序。作爲一個說明,我也嘗試使用具有相同行爲的DropDownListFor助手完全相同的代碼)。

我懷疑問題在於創建MultiSelectList。正如你所看到的,我使用重載(IEnumerable ListItems,字符串DataValue,字符串DataText,IEnumerable SelectedValues)。似乎SelectedValues根本就沒有在ListValues中的任何東西上找到匹配,但我無法弄清楚原因。在兩者中使用的類型相同,DataValue和DataTypes名稱與類型的成員相匹配(爲了安全起見)。我知道ListItems是正確的,因爲列表正確地呈現它們。

我不知所措。

參考:

public partial class DisciplineViewModel 
{ 
    public Guid DisciplineId { get; set; } 

    public string Discipline { get; set; } 
} 

下面是該模型:

public partial class ProtocolViewModelEdit 
{ 
    [Key] 
    public Guid ProtocolId { get; set; } 

    [Display(Name = "Name")] 
    public string Protocol { get; set; } 

    public string ProtocolType { get; set; } 

    [Display(Name = "Type")] 
    public Guid ProtocolTypeId { get; set; } 

    [Display(Name = "Status")] 
    public Guid ProtocolStatusId { get; set; } 

    public virtual ICollection<ProtocolTypeViewModel> ProtocolTypes { get; set; } 

    public virtual ICollection<ProtocolStatusViewModel> ProtocolStatuses { get; set; } 

    public virtual ICollection<DisciplineViewModel> ProtocolDisciplines { get; set; } 

    public virtual ICollection<ProtocolXProgramViewModel> ProtocolPrograms { get; set; } 

    public virtual ICollection<DisciplineViewModel> Disciplines { get; set; } 

    public virtual ICollection<ProgramViewModel> Programs { get; set; } 
} 
+0

對多選使用'ListBoxFor'。 –

+0

感謝您的迴應,但是會產生相同的問題。除了未選擇默認值(s)外,一切看起來都很棒。 –

+0

我剛剛從4年前發現這篇文章,但它有最近的反應,這是否有可能這仍然是一個錯誤? [鏈接] https://social.msdn.microsoft.com/Forums/vstudio/en-US/05ee3b35-f3d3-48b4-83f5-ca3d9073624e/mvc-htmlhelper-listboxfor-and-listbox-multiselectlist-bug?forum=netfxbcl –

回答

1

您稱爲一個post on the MSDN forums其中OP描述了以下內容:

1)selectedValues參數必須僅填充關鍵值的集合。它不能是所選對象的集合,因爲HtmlHelper不會將dataValueField應用於此集合。

2)如果使用ListBox,則不能將name參數設置爲與Model屬性相同。此外,您無法命名將包含與Model屬性相同的項目集合的ViewBag屬性。

3)如果使用ListBox,它會變得更加笨拙。您應該命名ViewBag屬性,該屬性將包含與Model屬性名稱相同的項目集合。現在,當您在視圖中使用ListBoxFor時,您必須使用不存在的ViewBag屬性(這很重要!)。HtmlHelper.ListBoxFor將自動查找具有指定Model屬性名稱的ViewBag屬性。

這些都不是實際問題。 A SelectList最終必須翻譯成HTML select元素,該元素只能用於簡單類型(字符串,整型等)。實際上,一切都是字符串,只是模型聯編程序的工作將發佈的值轉換爲更具體的類型,如int。因此,很明顯你爲什麼不能綁定一個對象列表。

另外兩個提到的問題是ModelState的結果。作爲最後的手段,綁定形式字段的值由ModelState中的內容決定,其由Request,ViewData/ViewBag,最後Model的值組成。如果在ViewBag中設置SelectListModel上的屬性名稱相同,則ModelState中該密鑰的值將爲SelectList而不是實際選定的值,因此您選擇的項目將沒有選定項目,因爲沒有選項值當然會與SelectList實例匹配。再說一次,這只是標準的行爲,如果你不知道事情是如何工作的,並且沒有意識到你正在做什麼的含義,那只是一個「錯誤」。

這裏的問題恰恰是第一個問題。您將對象列表作爲選定值傳遞,並且根本無法將其正確綁定到HTML select元素。然而,如果你甚至不打算創建自己的MultiSelectList,事情就會容易得多。所有幫手的需求是IEnumerable<SelectListItem>。 Razor將負責創建SelectList/MultiSelectList並設置適當的選定值。只要做到:

@Html.ListBoxFor(
    m => m.ProtocolDisciplines, 
    Model.Disciplines.Select(d => new SelectListItem { Value = d.DisciplineId.ToString(), Text = d.Discipline }), 
    new { @class = "form-control", size = 8 } 
) 

UPDATE

爲了回答您的問題有關剃刀如何「知道」,就像我說我的回答,對信息來源於ModelState。但是,正如Stephen在下面的評論中指出的那樣,你將它綁定到的屬性是對象的集合。這永遠不會起作用。同樣,來自HTML select元素的發佈值將始終是簡單類型,而不是對象。因此,您需要一個屬性,使模型聯編程序可以將發佈的數據綁定到該屬性,然後您需要使用該信息來查找您需要的實際對象,然後才能最終設置類似ProtocolDisciplines屬性的內容。換句話說:

public List<int> SelectedProtocolDisciplines { get; set; } 

public IEnumerable<SelectListItem> DisciplineOptions { get; set; } 

由於您使用視圖模型,它的更好,如果你包括在該視圖模型選擇列表中的項目,所以我加了屬性這一點。在你的行動(GET和POST),則需要設置該屬性:

model.DisciplineOptions = model.Disciplines..Select(d => new SelectListItem { 
    Value = d.DisciplineId.ToString(), 
    Text = d.Discipline 
}); 

因爲你需要調用的GET和POST操作都,你可能要因素出來成你的控制器上的私人方法都可以調用。然後,在你的看法:

@Html.ListBoxFor(m => m.SelectedProtocolDisciplines, Model.DisciplineOptions, new { @class = "form-control" }) 

最後,在POST操作:

var protocolDisciplines = db.Disciplines.Where(m => model.SelectedProtocolDisciplines.Contains(m.DisciplineId)); 

然後,如果這是一個「創造」的方法,你可以簡單地設置相應的屬性上與你的實體。如果您正在編輯現有的實體,你需要做更多一點的工作:

// Remove deselected disciplines 
entity.ProtocolDisciplines 
    .Where(m => !model.SelectedProtocolDisciplines.Contains(m.DisciplineId)) 
    .ToList() 
    .ForEach(m => entity.ProtocolDisciplines.Remove(m)); 

// Add new selected disciplines 
var addedDisciplineIds = model.SelectedProtocolDisciplines.Except(entity.ProtocolDisciplines.Select(m => m.DisciplineId)); 
db.Disciplines 
    .Where(m => addedDisciplineIds.Contains(m.DisciplineId)) 
    .ToList() 
    .ForEach(m => entity.ProtocolDisciplines.Add(m)); 

這額外的步法要保持現有的,不變的M2M關係。

+0

與您的答案無關,但我建議使用[The Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms#the-select- tag-helper)而不是'@ Html.ListBoxFor' –

+0

我已經實現了你的代碼,就像我試過的所有其他東西一樣,控件完全呈現了我期望的除了所選值之外的其他東西。他們沒有被選中。在將模型返回到視圖之前,我已經查看了調試器,並且一切都如我所期望的那樣;我看到了16個總學科,並且我看到應該是默認(選定)值的單個學科。您聲明Razor將負責設置適當的選定值,但我不知道它是如何知道的(根據您的代碼)。 –

+0

Chris,你現在可能想要更新這個,OP已經根據上面的討論添加了額外的信息(特別是'ProtocolDisciplines'屬性是一個複雜對象的集合) –