Asp.net mvc 有条件地禁用Html.DropDownList

Asp.net mvc 有条件地禁用Html.DropDownList,asp.net-mvc,Asp.net Mvc,如何更改此DropDownList声明,以便有条件地启用/禁用disabled属性 <%= Html.DropDownList("Quantity", new SelectList(...), new{@disabled="disabled"} %> 非工作示例: <%= Html.DropDownList("Quantity", new SelectList(...), new{@disabled=Model.CanEdit?"false":"disabled"} %&

如何更改此DropDownList声明,以便有条件地启用/禁用disabled属性

<%= Html.DropDownList("Quantity", new SelectList(...), new{@disabled="disabled"} %>

非工作示例:

<%= Html.DropDownList("Quantity", new SelectList(...), new{@disabled=Model.CanEdit?"false":"disabled"} %>

p、 在整个语句周围添加if条件不是理想的方法:)

编辑:根据另一个问题的扩展方法,我提出了以下扩展:

public static IDictionary<string, object> Disabled (this object obj, bool disabled)
{
  return disabled ? obj.AddProperty ("disabled", "disabled") : obj.ToDictionary ();
}
公共静态IDictionary已禁用(此对象obj,bool已禁用)
{
返回disabled?obj.AddProperty(“disabled”、“disabled”):obj.ToDictionary();
}
然后可以用作

<%= Html.DropDownList("Quantity", new SelectList(...), new{id="quantity"}.Disabled(Model.CanEdit) %>

我不知道ASP.NET是否提供了更简洁的特例方法,但您大概可以这样做:

<%= Html.DropDownList("Quantity", new SelectList(...), Model.CanEdit? new{@class="quantity"} : new{@class="quantity", @disabled:"disabled"}) %>

请不要写意大利面代码。Html帮助程序用于此目的:

public static MvcHtmlString DropDownList(this HtmlHelper html, string name, SelectList values, bool canEdit)
{
    if (canEdit)
    {
        return html.DropDownList(name, values);
    }
    return html.DropDownList(name, values, new { disabled = "disabled" });
}
然后:

<%= Html.DropDownList("Quantity", new SelectList(...), Model.CanEdit) %>

或者,您可以提出更好的方案(如果模型包含选项):



您还可以获得更多的单元可测试代码的好处。

一个选项是创建一个自定义版本的Html.DropDownList,它接受一个额外的参数并执行您想要的操作。。。但是,您必须为每种类型的帮助器创建一个新的帮助器-TextBoxFor、TextAreaFor、CheckBoxFor等。。。你还得想办法让它发挥作用

相反,我选择创建一个Html助手来替换普通的匿名HtmlAttributes对象,因为这样它就可以与所有使用HtmlAttributes的助手兼容,而无需任何特殊工作。此解决方案还允许您传递其他属性,如类、名称或任何您想要的属性。它不会将您锁定为仅禁用

我创建了以下帮助器-它接受一个布尔值和一个匿名对象。如果disabled为true,则将disabled属性添加到值为“disabled”的匿名对象(实际上是一个字典),否则根本不添加属性

public static RouteValueDictionary ConditionalDisable(
   bool disabled, 
   object htmlAttributes = null)
{
   var dictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

   if (disabled)
      dictionary.Add("disabled", "disabled");

   return dictionary;
}

这方面的一个例子是:

@Html.TextBoxFor(m => m.SomeProperty,    
   HtmlHelpers.ConditionalDisable(true, new { @class = "someClass"))

对我来说,这种方法的一个巨大优势是,它几乎适用于所有MVC HTMLHelper,因为它们都有接受RouteValueDictionary而不是匿名对象的重载

注意事项
匿名对象htmlAttributes()使用一些奇特的代码忍者工作来完成任务。我不完全确定它的性能如何。。。但这已经足够我用它做什么了。您的里程可能会有所不同

我并不特别喜欢它的名字,但我想不出更好的名字了。重命名很容易


我也不喜欢使用语法,但我还是想不出更好的方法。这应该不难改变。对
对象
的扩展方法是一种思想。。。您将以
new{@class=“someClass”}.ConditionalDisable(true)
结束,但是如果您只想要disable属性,并且没有任何附加内容,那么您将以类似
new{}.ConditionalDisable(true)的东西结束并且您还将得到一个扩展方法,该方法将显示所有对象。。。这可能是不可取的。

无需添加帮助器方法,您只需使用

<%= Html.DropDownList("Quantity", new SelectList(...), IsEditable == true ? new { @disabled = "disabled" } as object : new {} as object %>

如果要将
作为对象
项删除,这将不起作用,因为默认情况下
new{}
是在运行时编译的动态对象,因此这两个可能的对象必须具有相同的属性。但是Html attributes参数实际上只是一个对象,因此这些动态可以转换为对象来解决这个问题


此解决方案甚至允许您使用多个HTML属性,其中一个是可选的,另一个不是可选的,即
class='whatever'
不是可选的,但
disabled
是禁用的,因此您可以将
class='whatever'
放在两个对象中,但可选的仅放在第一个对象中。Dimitrov的答案不支持除禁用之外的任何自定义属性。

强类型verison:

 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel>    html,
                                                                   Expression<Func<TModel, TProperty>> expression,
                                                                   IEnumerable<SelectListItem> selectList,
                                                                   string optionText, bool canEdit)
    {
        if (canEdit)
        {
            return html.DropDownListFor(expression, selectList, optionText);
        }
        return html.DropDownListFor(expression, selectList, optionText, new { disabled = "disabled" });
    }
public static MvcHtmlString DropDownListFor(此HtmlHelper html,
表情表情,
IEnumerable selectList,
字符串optionText,bool canEdit)
{
如果(canEdit)
{
返回html.DropDownListFor(表达式、selectList、optionText);
}
返回html.DropDownListFor(expression,selectList,optionText,new{disabled=“disabled”});
}

为了完整起见,这里有一个保留所有参数的选项,它会将select值发布到服务器:

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes, bool enabled)
{
  if (enabled)
  {
    return SelectExtensions.DropDownListFor<TModel, TProperty>(html, expression, selectList, htmlAttributes);
  }

  var htmlAttributesAsDict = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
  htmlAttributesAsDict.Add("disabled", "disabled");
  string selectClientId = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression));
  htmlAttributesAsDict.Add("id", selectClientId + "_disabled");

  var hiddenFieldMarkup = html.HiddenFor<TModel, TProperty>(expression);
  var selectMarkup = SelectExtensions.DropDownListFor<TModel, TProperty>(html, expression, selectList, htmlAttributesAsDict);
  return MvcHtmlString.Create(selectMarkup.ToString() + Environment.NewLine + hiddenFieldMarkup.ToString());
}
你可以做:

var dropDownEditDisable = new { disabled = "disabled" };
var dropDownEditEnable = new { };

object enableOrDisable = Model.CanEdit ? 
           (object)dropDownEditEnable : (object)dropDownEditDisable;

@Html.DropDownList("Quantity", new SelectList(...), enableOrDisable)

Html.DropDownListFor()可能很长,因此不需要重复它。

由于WebForms引擎的性质,您无法对视图进行单元测试。您可以测试扩展方法。这样,您的视图将不再包含值得进行单元测试的条件逻辑。通过测试扩展方法,您可以确保使用它的所有视图的行为都符合您的预期,并且根据模型上的
CanEdit
属性具有准确的标记。如果我需要具有此签名的帮助器方法,那么此代码将是什么样子
publicstaticstringdropdownlist(这个HTMLHelperHTML,字符串名,SelectList值,objecthtmlattributes,boolcanedit)
@Pavel Surmenok,你可以使用
DropDownList
重载,它使用
RouteValueDictionary
将html属性合并在一起。不,你不能这样做。您会得到一个编译时错误:“无法确定条件表达式的类型,因为‘AnonymousType#1’和‘AnonymousType#2’之间没有隐式转换。您需要将强制转换添加到anonymousTypes‘as object’中,否则它会起作用。嗨,我想仅基于该值禁用/启用特定页面的下拉列表,我正在通过模型传递它。我试着将真/假传递给di
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes, bool enabled)
{
  if (enabled)
  {
    return SelectExtensions.DropDownListFor<TModel, TProperty>(html, expression, selectList, htmlAttributes);
  }

  var htmlAttributesAsDict = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
  htmlAttributesAsDict.Add("disabled", "disabled");
  string selectClientId = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression));
  htmlAttributesAsDict.Add("id", selectClientId + "_disabled");

  var hiddenFieldMarkup = html.HiddenFor<TModel, TProperty>(expression);
  var selectMarkup = SelectExtensions.DropDownListFor<TModel, TProperty>(html, expression, selectList, htmlAttributesAsDict);
  return MvcHtmlString.Create(selectMarkup.ToString() + Environment.NewLine + hiddenFieldMarkup.ToString());
}
@Html.DropDownListFor(m => m.SomeValue, Model.SomeList, new { @class = "some-class" }, Model.SomeList > 1)
var dropDownEditDisable = new { disabled = "disabled" };
var dropDownEditEnable = new { };

object enableOrDisable = Model.CanEdit ? 
           (object)dropDownEditEnable : (object)dropDownEditDisable;

@Html.DropDownList("Quantity", new SelectList(...), enableOrDisable)