使用视图模型是正确的方法。然后,您可以在控制器上创建一个私有方法来SelectList
每次构建。这种方法可以防止您不得不牺牲编译时安全性,如果您使用ViewBag
.
所以首先,视图模型可能看起来像这样:
public class EditPersonViewModel
{
public Person Person { get; set; }
[Display(Name = "State")]
public int StateId { get; set; }
public SelectList States { get; set; }
}
StateId
尽管我们可以通过对象访问该属性,但我在其中拥有该属性的原因Person
是因为在这种情况下,Person
是模型,并且在我看来,我认为在模型上放置数据注释是没有意义的. 除此之外,Display
注释仅允许您控制在使用LabelFor
为您的States
SelectList
.
在开始构建列表之前,让我们定义一个简单的类来保存状态:
public class State
{
public int Id { get; set; }
public string Name { get; set; }
}
接下来,我们需要能够构建SelectList
. 为此,您可以向控制器添加私有方法:
private SelectList GetStates(object selectedValue = null)
{
return new SelectList(this.States, StateListDataValueKey,
StateListDataTextKey, selectedValue);
}
this.States
代表您在哪里获取/存储您的状态数据。所以它可能是这样的db.States.GetAll()
。
StateListDataValueKey
并StateListDataTextKey
分别表示要用作选定值和选定文本的字段的名称。所以在这种情况下,StateListDataValueKey
应该是Id
而且StateListDataTextKey
应该是Name
。我倾向于在我的控制器中定义这些:
public class HomeController : Controller
{
// rest of controller
// These two strings should correspond with
// the properties in your State class
private readonly string StateListDataValueKey = "Id";
private readonly string StateListDataTextKey = "Name";
}
我们现在可以继续创建操作来显示一个人的数据,也可以编辑一个人:
public ActionResult Edit(int id)
{
var model = new EditPersonViewModel();
// Get a person by Id.
model.Person = GetPerson(id);
// Call the utility method to build the list.
model.States = GetStates(model.Person.StateId);
return View(model);
}
[HttpPost]
public ActionResult Edit(EditPersonViewModel model)
{
if (ModelState.IsValid)
{
// Save the edits and then redirect.
return RedirectToActionPermanent("Index");
}
// Build the SelectList again so we can repopulate the view.
model.States = GetStates(model.Person.StateId);
return View(model);
}
既然已经这样了,让我们看一下编辑视图的相关部分:
@model EditPersonViewModel
<div class="editor-label">
@Html.LabelFor(model => model.StateId)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.Person.StateId, Model.States)
@Html.ValidationMessageFor(model => model.States)
</div>
调用LabelFor
使用Display
前面定义的数据注释来正确标记下拉列表。否则它将标有文本“StateId”,这对您的用户没有用处。
更新
我在控制器中指定的两个私有字段有点困扰我,因为这不是最好的做事方式。如果您喜欢它,我已经将这种方法留在那里,但这里有一个更好的实现:
private SelectList BuildSelectList<TSource>(IEnumerable<TSource> source,
Expression<Func<TSource, int>> valueKey, Expression<Func<TSource, string>> textKey,
object selectedValue = null)
{
var selectedValueKey = ((MemberExpression)(MemberExpression)valueKey.Body).Member.Name;
var selectedTextKey = ((MemberExpression)(MemberExpression)textKey.Body).Member.Name;
return new SelectList(source, selectedValueKey, selectedTextKey, selectedValue);
}
请注意我如何使该方法成为通用方法并更改了名称。现在,您可以使用它来构建任何SelectList
,而不仅仅是一个状态。你可以像这样使用它:
model.States = BuildSelectList(this.States, m => m.Id, m => m.Name, model.Person.StateId);
您不再需要在控制器中定义StateListDataValueKey
或StateListDataTextKey
。