ASP.NET MVC Html.DropDownList SelectedValue


103

私はこれをRC1にしてからRC2にアップグレードしてみましたが、問題は解決しませんでした。

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

結果:SelectedValueプロパティがオブジェクトに設定されます

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>

結果:予想されるすべてのオプションがクライアントにレンダリングされますが、選択された属性は設定されていません。SelectedValueの項目はリスト内に存在しますが、リストの最初の項目は常にデフォルトで選択されています。

どうすればいいですか?

更新 John Feminellaの返信のおかげで、問題が何であるかがわかりました。「UserId」は、ビューが強く型付けされているモデルのプロパティです。Html.DropDownList( "UserId"を "UserId"以外の名前に変更すると、選択した値が正しくレンダリングされます。

これにより、値がモデルにバインドされなくなります。


値がリストにあることを確信していますか?
John Sheehan

問題はASP.NET MVCのリリース1にもまだ存在します
Mathias F

selectedUserIdとは!?
fulvio

入力の名前がプロパティと同じでない場合、更新時に値をどのようにバインドすると思いますか?
リューディツェ

回答:


68

これは私がこの問題を修正した方法です:

私は次のことをしました:

コントローラ:

ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;

見る

<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>

以下によって変更:

見る

<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>

DropDownの名前がViewDataの名前と同じであってはならないようです:S奇妙ですが、うまくいきました。


19
「DropDownにViewDataの名前と同じ名前を付けることはできません」の+1感謝
Daniel Dyson

ありがとうございました!!!なぜこれがうまくいかないのか何時間も疑問に思って私の問題を解決しました:)
Jen

1
これは選択した値に対して機能しますが、DropDownListがモデルに存在しない "DealerTypesDD"にバインドされているため、dbの "DealerTypes"を更新しようとすると問題が発生します。回避策は、DropDownListに非表示フィールドとhtmlAttributesを追加することです:<input type = "hidden" name = "DealerTypes" id = "DealerTypes" value = "" /> <%= Html.DropDownList( "DealerTypesDD"、ViewData [ "DealerTypes"] as SelectList、new {@onchange = "DealerTypes.value = this.value"})%>
Zim

3
ビューデータ名に「DS」を追加するほうがよいので、モデルのバインドを台無しにしないでください...
noocyte

1
なんてこったい!これはMVC5で修正されていません??? @Paul Hatcherが言及した別のトリックとして、選択したIDをViewData ["DealerTypes"]にコピーします。
jsgoupil 2014年

51

これを試して:

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

その後:

var list = new[] {   
    new Person { Id = 1, Name = "Name1" }, 
    new Person { Id = 2, Name = "Name2" }, 
    new Person { Id = 3, Name = "Name3" } 
};

var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;

Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])

MVC RC2を使用すると、次のようになります。

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>

これは原因を突き止めるのに役立ちました。追加を反映するために質問を更新しました。
ブルー

モデルの最初ですか?コントローラの2番目ですか?そして3番目のビューは明確ですが、最初の2つは.. ???
rr

良い答えですが、ViewModelを使用する方が良いのではないですか?
Jess

@ジェス:5年以上前の2009年3月にこの回答を書きました。時が変わった!自由に更新してください。:)
John Feminella、2014

5

ドロップダウンに「UserId」という名前を付けても、モデルバインディングが正しく機能します。

これが機能するための唯一の要件は、SelectListを含むViewDataキーが、バインドするModelプロパティと同じ名前を持たないことです。あなたの特定のケースでは、これは次のようになります:

// in my controller
ViewData["Users"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>

これにより、モデルのUserIdプロパティと同じ名前を持つUserIdという名前の選択要素が生成されます。したがって、モデルバインダーは、Html.DropDownListヘルパーによって生成されたHTMLの選択要素で選択された値を設定します。

プロパティ名と等しいキーでViewDataに選択リストを配置したときに、その特定のHtml.DropDownListコンストラクターがSelectListで指定された値を選択しない理由がわかりません。DropDownListヘルパーが他のシナリオでどのように使用されるかと関係があると思います。慣例では、モデルのプロパティと同じ名前のSelectDataがViewDataにあります。これは正しく動作します:

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId")%>

多くの仲間に感謝します。何よりも、これが私にとってうまく
いきました。

最後に!何時間か試してみて、ようやく「唯一の要件」が見つかりました。それはそれでした。
SteveCav 2016

2

これがチームが修正すべきバグだと思わない場合は、少なくともMSDNでドキュメントを改善する必要があります。紛らわしいのは、この貧弱な文書からです。でMSDN、それはパラメータの説明 名前をとして、

Type: System.String
The name of the form field to return.

これは、生成される最終的なhtmlがそのパラメーターを選択入力の名前として使用することを意味します。しかし、それは実際にはそれ以上のものを意味します。

デザイナーは、ユーザーがビューモデルを使用してドロップダウンリストを表示し、同じビューモデルへのポストバックを使用すると想定していると思います。しかし、多くの場合、私たちは実際にその仮定を守っていません。

上記の例を使用して、

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

仮定に従う場合、このドロップダウンリスト関連ビューのビューモデルを定義する必要があります

public class PersonsSelectViewModel{
    public string SelectedPersonId,
    public List<SelectListItem> Persons;
}

ポストバックすると、選択した値のみがポストバックされるため、モデルのプロパティSelectedPersonIdにポストバックする必要があると想定します。つまり、Html.DropDownListの最初のパラメーターは「SelectedPersonId」である必要があります。したがって、デザイナーは、ビューにモデルビューを表示するとき、モデルのプロパティSelectedPersonIdがそのドロップダウンリストのデフォルト値を保持する必要があると考えます。List <SelectListItem> Personsが既にSelectedフラグを設定してどのフラグが選択/デフォルトであるかを示していたとしても、tml.DropDownListはそれを実際に無視して、独自のIEnumerable <SelectListItem>を再構築し、名前に基づいてデフォルト/選択されたアイテムを設定します。

これがasp.net mvcのコードです

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
            string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
            IDictionary<string, object> htmlAttributes)
{
    ...

    bool usedViewData = false;

    // If we got a null selectList, try to use ViewData to get the list of items.
    if (selectList == null)
    {
        selectList = htmlHelper.GetSelectData(name);
        usedViewData = true;
    }

    object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));

    // If we haven't already used ViewData to get the entire list of items then we need to
    // use the ViewData-supplied value before using the parameter-supplied value.
    if (defaultValue == null && !String.IsNullOrEmpty(name))
    {
        if (!usedViewData)
        {
            defaultValue = htmlHelper.ViewData.Eval(name);
        }
        else if (metadata != null)
        {
            defaultValue = metadata.Model;
        }
    }

    if (defaultValue != null)
    {
        selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
    }

    ...

    return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}

そのため、コードは実際にさらに進んで、モデル内だけでなくビューデータ内でも名前を検索しようとします。名前を見つけるとすぐに、selectListを再構築し、元のSelectedを無視します。

問題は、多くの場合、実際にはそのように使用しないことです。1つまたは複数の項目が選択されたtrueが設定されたselectListをスローするだけです。

もちろん、解決策は簡単です。モデルにもビューデータにもない名前を使用してください。一致が見つからない場合は、元のselectListが使用され、元のSelectedが有効になります。

しかし、私はまだMVCがもう1つの条件を追加することでそれを改善すべきだと思います

if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
    selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}

元のselectListにすでに1つのSelectedがある場合、なぜそれを無視するのですか?

ただ私の考え。


これは私がこれまで読んだ中で本当に最高の説明です。頭痛の種をたくさん救いました。私がしなければならなかったのは、viewmodelプロパティの値を設定することだけでした。ありがとう。
モーゼスマチュア2016

2

以前のMVC 3ポストのコードは機能しませんが、良いスタートです。私はそれを修正します。私はこのコードをテストしましたが、MVC 3 Razor C#で動作しますList<SelectListItem>。このコードはViewModelパターンを使用して、を返すプロパティを設定します。

モデルクラス

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

ViewModelクラス

using System.Web.Mvc;

public class ProductListviewModel
{
    public List<SelectListItem> Products { get; set; }
}

コントローラメソッド

public ViewResult List()
{
    var productList = new List<SelectListItem>();

    foreach (Product p in Products)
    {
        productList.Add(new SelectListItem
        {
            Value = p.ProductId.ToString(),
            Text = "Product: " + p.Name + " " + p.Price.ToString(),
            // To set the selected item use the following code 
            // Note: you should not set every item to selected
            Selected = true
        });
    }

    ProductListViewModel productListVM = new ProductListViewModeld();

    productListVM.Products = productList;

    return View(productListVM);
}

景色

@model MvcApp.ViewModels.ProductListViewModel

@using (Html.BeginForm())
{
    @Html.DropDownList("Products", Model.Products)
}

HTML出力は次のようになります

<select id="Products" name="Products">
    <option value="3">Product: Widget 10.00</option>
    <option value="4">Product: Gadget 5.95</option>
</select>

出力のフォーマット方法によって異なります。これがお役に立てば幸いです。コードは機能します。


1
これはselectedオプションを設定しません。
Jess

SelectListItemを使用して選択した項目を設定するには、Selectedプロパティをtrueに設定します。
wayne.blackmon 14

1

これは、選択されたアイテムのモデルではなくViewDataのみをチェックするため、SelectExtensionsクラスのバグのようです。したがって、トリックは、選択したアイテムをモデルからプロパティの名前でViewDataコレクションにコピーすることです。

これは、MVCフォーラムでの回答から引用したものです。KaziのDropDownList属性を使用したブログ投稿にも、より完全な回答があります ...

与えられたモデル

public class ArticleType
{
   public Guid Id { get; set; }
   public string Description { get; set; }
}

public class Article
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public ArticleType { get; set; }
}

との基本的なビューモデル

public class ArticleModel
{
     public Guid Id { get; set; }
     public string Name { get; set; }

     [UIHint("DropDownList")]
     public Guid ArticleType { get; set; }
}

次に、DropDownListエディターテンプレートを次のように記述します。

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">  
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata;
        if (metaData == null)
        {
            return null;
        }

        var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
        ViewData[metaData.PropertyName] = selected;

        var key = metaData.PropertyName + "List";
        return (IEnumerable<SelectListItem>)ViewData[key];
    }
</script>
<%= Html.DropDownList(null, GetSelectList()) %>

これは、ビューモデルのArticleTypeをSelectListItemに変更した場合にも機能しますが、Kaziのブログに従ってタイプコンバーターを実装し、バインダーに強制的にこれを単純なタイプとして扱うように登録する必要があります。

あなたのコントローラーには、...

public ArticleController 
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne<Article>(id);
          var model = builder.Convert<ArticleModel>(entity);

          var types = repository.FindAll<ArticleTypes>();
          ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);

          return VIew(model);
     }
     ...
}

返信ありがとうございます。これを確認する必要があります。
ブルー10/10/30

0

問題は、ドロップボックスがリストボックスと同じように機能しないことです。少なくともASP.NET MVC2の設計では、ドロップボックスでは複数の値を選択できるため、ドロップボックスで使用できる値は0または1つだけです。したがって、HTMLで厳密であるため、その値は「選択された」フラグとしてオプションリストに含めるのではなく、入力自体に含める必要があります。

次の例を参照してください。

<select id="combo" name="combo" value="id2">
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

<select id="listbox" name="listbox" multiple>
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

コンボにはオプションが選択されていますが、そのvalue属性が設定されています。そのため、ASP.NET MVC2でドロップボックスをレンダリングし、特定の値(デフォルト値など)も選択する場合は、次のように、レンダリングで値を指定する必要があります。

// in my view             
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { @Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>

0

ASP.NET MVC 3では、リストをViewDataに追加するだけです...

var options = new List<SelectListItem>();

options.Add(new SelectListItem { Value = "1", Text = "1" });
options.Add(new SelectListItem { Value = "2", Text = "2" });
options.Add(new SelectListItem { Value = "3", Text = "3", Selected = true });

ViewData["options"] = options;

...そしてカミソリビューで名前で参照します...

@Html.DropDownList("options")

DropDownList呼び出しでリストを手動で「使用」する必要はありません。このようにすると、選択した値も正しく設定されます。

免責事項:

  1. Webフォームビューエンジンでこれを試したことはありませんが、動作するはずです。
  2. これはv1とv2ではテストしていませんが、動作する可能性があります。

0

私はなんとか目的の結果を得ることができましたが、アプローチは少し異なりました。ドロップダウンリストでは、モデルを使用してから参照しました。これがあなたが探していたものかどうかわかりません。

@Html.DropDownList("Example", new SelectList(Model.FeeStructures, "Id", "NameOfFeeStructure", Model.Matters.FeeStructures))

上記のModel.Matters.FeeStructuresは私のIDです。これは、選択する必要があるアイテムの値になる可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.