列挙型のデフォルトのエディター テンプレートを作成するにはどうすればよいですか?
-
26-09-2019 - |
質問
列挙型のデフォルトのエディター テンプレートを作成するにはどうすればよいですか?つまり、次のことを意味します。次のようなことはできますか:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Enum>" %>
<% -- any code to read the enum and write a dropdown -->
そして、これを EditorTemplates フォルダーに次の名前で置きます Enum.ascx
?
これは私が試した問題の回避策ですが、私が必要とするものではありません。
私の列挙型は次のとおりです。
public enum GenderEnum
{
/// <summary>
/// Male
/// </summary>
[Description("Male Person")]
Male,
/// <summary>
/// Female
/// </summary>
[Description("Female Person")]
Female
}
というテンプレートを作りました GenderEnum.acsx
そしてそれを中に入れてください Shared/EditorTemplates
フォルダ。テンプレートは次のとおりです。
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AlefTech.HumanResource.Core.GenderEnum>" %>
<%@ Import Namespace="AlefTech.HumanResource.WebModule.Classes" %>
<%=Html.DropDownListFor(m => m.GetType().Name, Model.GetType()) %>
もちろん方法は私独自のものです。
public static class HtmlHelperExtension
{
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType)
{
List<SelectListItem> list = new List<SelectListItem>();
Dictionary<string, string> enumItems = enumType.GetDescription();
foreach (KeyValuePair<string, string> pair in enumItems)
list.Add(new SelectListItem() { Value = pair.Key, Text = pair.Value });
return htmlHelper.DropDownListFor(expression, list);
}
/// <summary>
/// return the items of enum paired with its descrtioption.
/// </summary>
/// <param name="enumeration">enumeration type to be processed.</param>
/// <returns></returns>
public static Dictionary<string, string> GetDescription(this Type enumeration)
{
if (!enumeration.IsEnum)
{
throw new ArgumentException("passed type must be of Enum type", "enumerationValue");
}
Dictionary<string, string> descriptions = new Dictionary<string, string>();
var members = enumeration.GetMembers().Where(m => m.MemberType == MemberTypes.Field);
foreach (MemberInfo member in members)
{
var attrs = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs.Count() != 0)
descriptions.Add(member.Name, ((DescriptionAttribute)attrs[0]).Description);
}
return descriptions;
}
}
ただし、これでうまくいきましたが、私が求めているのはそうではありません。代わりに、機能するには次のものが必要です。
のコード Shared\EditorTemplates\Enum.acsx
:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Enum>" %>
<%@ Import Namespace="System.Web.Mvc.Html" %>
<%@ Import Namespace="WhereMyExtentionMethod" %>
<%=Html.DropDownListFor(m => m.GetType().Name, Model.GetType()) %>
これにより、すべての列挙型に対してテンプレートを作成する必要がなくなりました。
解決 2
ご協力いただきありがとうございました
Yngvebn、私は以前にあなたの解決策(最後のコメントで)を試しましたが、私がしなかったことは <dynamic>
, 、代わりに使用しました <Enum>
ジェネリック型で。
最後に解決策は次のとおりです。
Enum.acsx という名前のテンプレートを作成し、 ビュー\共有\エディターテンプレート
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%@ Import Namespace="System.Web.Mvc.Html" %>
<%@ Import Namespace="the extension methods namespace" %>
<% Enum model = (Enum)Model; %>
<%=Html.DropDownList(model.GetType().Name,model.GetType())%>
そしてあなたのエンティティ内で:
public class Person
{
[UIHint("Enum")]
public GenderEnum Gender{get;set;}
}
public Enum GenderEnum
{
[Description("Male Person")]
Male,
[Description("Female Person")]
Female
}
そしてまた拡張メソッドがあります:
public static class HtmlHelperExtension
{
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType)
{
List<SelectListItem> list = new List<SelectListItem>();
Dictionary<string, string> enumItems = enumType.GetDescription();
foreach (KeyValuePair<string, string> pair in enumItems)
list.Add(new SelectListItem() { Value = pair.Key, Text = pair.Value });
return htmlHelper.DropDownListFor(expression, list);
}
/// <summary>
/// return the items of enum paired with its descrtioption.
/// </summary>
/// <param name="enumeration">enumeration type to be processed.</param>
/// <returns></returns>
public static Dictionary<string, string> GetDescription(this Type enumeration)
{
if (!enumeration.IsEnum)
{
throw new ArgumentException("passed type must be of Enum type", "enumerationValue");
}
Dictionary<string, string> descriptions = new Dictionary<string, string>();
var members = enumeration.GetMembers().Where(m => m.MemberType == MemberTypes.Field);
foreach (MemberInfo member in members)
{
var attrs = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs.Count() != 0)
descriptions.Add(member.Name, ((DescriptionAttribute)attrs[0]).Description);
}
return descriptions;
}
}
他のヒント
答えるのが遅れましたが、これが他の人に役立つことを願っています。理想的には、毎回 UIHint を指定するのではなく、規則に従ってすべての列挙型で Enum テンプレートを使用する必要があります。これは、次のようなカスタム モデル メタデータ プロバイダーを作成することで実現できます。
using System;
using System.Collections.Generic;
using System.Web.Mvc;
public class CustomMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) {
var mm = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
if (modelType.IsEnum && mm.TemplateHint == null) {
mm.TemplateHint = "Enum";
}
return mm;
}
}
次に、それを Global.asax.cs の Application_Start メソッドに登録するだけです。
ModelMetadataProviders.Current = new CustomMetadataProvider();
これで、すべての enum プロパティがデフォルトで Enum テンプレートを使用するようになります。
これは私がこのために作成したヘルパーです。ビューでは次のことを簡単に行うことができます。
<%= Html.DropDownForEnum<MyEnum>("some-name-for-dropdown", MyEnum.TheFirstValue) %>
実際のドロップダウン内のテキストについては、列挙型の名前と一致するリソース ファイル内のリソースが検索されます。それ以外の場合は、実際の列挙型テキスト自体を書き込むだけです。
public static MvcHtmlString DropDownForEnum<T>(this HtmlHelper h, string name, T selectedValue)
{
Type enumType = typeof(T);
Tag t = new Tag("select").With("name", name).And("id", name);
foreach (T val in Enum.GetValues(enumType))
{
string enumText = Resources.ResourceManager.GetString(val.ToString());
if (String.IsNullOrEmpty(enumText)) enumText = val.ToString();
Tag option = new Tag("option").With("value", (val).ToString()).AndIf(val.Equals(selectedValue), "selected", "selected").WithText(enumText);
t.Append(option);
}
return MvcHtmlString.Create(t.ToString());
}
書き換えなしで動作させたい場合は、オーバーロードされた Tag クラスも必要になります。
public class Tag : TagBuilder
{
public Tag (string TagName): base(TagName)
{
}
public Tag Append(Tag innerTag)
{
base.InnerHtml += innerTag.ToString();
return this;
}
public Tag WithText(string text)
{
base.InnerHtml += text;
return this;
}
public Tag With(Tag innerTag)
{
base.InnerHtml = innerTag.ToString();
return this;
}
public Tag With(string attributeName, string attributeValue)
{
base.Attributes.Add(attributeName, attributeValue);
return this;
}
public Tag And(string attributeName, string attributeValue)
{
base.Attributes.Add(attributeName, attributeValue);
return this;
}
public Tag AndIf(bool condition, string attributeName, string attributeValue)
{
if(condition)
base.Attributes.Add(attributeName, attributeValue);
return this;
}
}
Nour Sabony さん、リソースによるローカリゼーションもサポートするようにバージョンを修正しました。したがって、DescriptionAttribute を DataAnnotations 名前空間の DisplayAttribute に変更しました。
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType)
{
List<SelectListItem> list = new List<SelectListItem>();
Dictionary<string, string> enumItems = enumType.GetDisplayNames(htmlHelper.ViewContext.HttpContext);
foreach (KeyValuePair<string, string> pair in enumItems)
list.Add(new SelectListItem() { Value = pair.Key, Text = pair.Value });
return htmlHelper.DropDownListFor(expression, list);
}
/// <summary>
/// return the items of enum paired with its DisplayName.
/// </summary>
/// <param name="enumeration">enumeration type to be processed.</param>
/// <returns></returns>
public static Dictionary<string, string> GetDisplayNames(this Type enumeration, HttpContextBase httpContext)
{
if (!enumeration.IsEnum)
{
throw new ArgumentException("passed type must be of Enum type", "enumerationValue");
}
Dictionary<string, string> displayNames = new Dictionary<string, string>();
var members = enumeration.GetMembers().Where(m => m.MemberType == MemberTypes.Field);
foreach (MemberInfo member in members)
{
var attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attrs.Count() != 0)
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
displayNames.Add(member.Name, ((DisplayAttribute)attrs[0]).GetName(););
}
else
{
displayNames.Add(member.Name, ((DisplayAttribute)attrs[0]).Name);
}
}
return displayNames;
}
enum の定義は次のようになります。
public enum Gender
{
[Display(Name = "Male", ResourceType = typeof(mynamespace.App_LocalResources.Shared))]
Male = 1,
[Display(Name = "Female", ResourceType = typeof(mynamespace.App_LocalResources.Shared))]
Female = 2,
}
同じようにビューでも使用できます。(かみそり):
@Html.DropDownListFor(model => model.Gender, typeof(Gender))
これが誰かの役に立てば幸いです!
私は、dropdownlistfor メソッドをもう少し簡単にし、これで selectedValue を指定できるようにしました。
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType)
{
return DropDownListFor(htmlHelper, expression, enumType, null);
}
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Type enumType, object selectedValue)
{
Dictionary<string, string> enumItems = enumType.GetDisplayNames(htmlHelper.ViewContext.HttpContext);
return htmlHelper.DropDownListFor(expression, new SelectList(enumItems, "Key", "Value", selectedValue));
}
ビューで次のように使用します。
@Html.DropDownListFor(m => m.Gender, typeof(Gender), Model.Gender)
モデルは私の MVC モデルであり、そのプロパティ Gender には DropDownListFor の selectedValue が含まれています。
状況に応じて異なる動作が必要になる可能性があるため、すべての列挙型に対してエディターを定義するデフォルトの方法はないと思います。たとえば、[Flags] 列挙型があり、複数選択が必要な場合、ドロップダウンリストが必要な場合、またはラジオ ボタンが必要な場合があります。
さらに、一般に、変数名の制限内で達成できる以上に、何らかの意味のある表示文字列が必要になります。
確かに 割り当てる enum 型のプロパティへの追加はそのままで機能しますが、その値を取得する方法はユーザー次第です。
はい
これは箱から出してすぐに機能することはほぼ確実です。
テンプレートに列挙型と同じ名前を付けてみてください。