2010-03-17 3 views
1

примечание: Я изначально задал вопрос, похожий на этот here, но я решил отправить сообщение, потому что я преодолел исходную проблему и в процессе изменил дизайн. Я думал, что это требует новой темы, потому что по мере изменения дизайна вопрос коренным образом изменился. Я просто хочу дать понять, что я не пытаюсь затопить SO тем же вопросом.Как я могу улучшить дизайн этого расширения HtmlHelper?

вопрос: Я работал над небольшим опытом, чтобы узнать, могу ли я создать вспомогательный метод для сериализации любого из моих типов для любого типа тега HTML, который я указываю. Я думал, что поместил его в сообщество, чтобы помочь мне идентифицировать запахи кода или другие недостатки/неэффективность дизайна, чтобы улучшить дизайн.

В принципе, у меня есть этот код, который будет генерировать Выберите коробку (или любой другой элемент HTML) с несколькими вариантами:

// the idea is I can use one method to create any complete tag of any type 
// and put whatever I want in the content area. 
// This makes the generation of all html completely testable 

<% using (Html.GenerateTag<SelectTag>(Model, new { href = Url.Action("ActionName") })) { %> 
    // model is type ShareClass. It contains a list of Funds 
    <%foreach (var fund in Model.Funds) {%> 
     <% using (Html.GenerateTag<OptionTag>(fund)) { %> 
      <%= fund.Name %> 
     <% } %> 
    <% } %> 
<% } %> 

, который будет производить следующий HTML вывод:

<select shareclassname="MyShareClass" 
     shareclasstype="ShareClass_A" 
     href="/Ctrlr/ActionName"> 
    <option selected="selected" id="1" name="MyFund_1">MyFund_1</option> 
    <option id="2" name="MyFund_2">MyFund_2</option> 
    <option id="3" name="MyFund_3">MyFund_3</option> 
    <option id="N" name="MyFund_N">MyFund_N</option> 
</select> 

Этот помощник Html.GenerateTag определяется как:

public static MMTag GenerateTag<T>(this HtmlHelper htmlHelper, object elementData, object attributes) where T : MMTag 
{ 
    return (T)Activator.CreateInstance(typeof(T), htmlHelper.ViewContext, elementData, attributes); 
} 

в зависимости от типа T это будет создать один из типов, определяемые ниже:

public abstract class HtmlTypeBase : MMTag 
{   
    public HtmlTypeBase(ViewContext viewContext, params object[] elementData) 
    { 
     _tag = this.GetTag(); 
     base._viewContext = viewContext; 
     base.MergeDataToTag(viewContext, elementData); 
    } 

    public abstract TagBuilder GetTag(); 
} 

public class SelectTag : HtmlTypeBase 
{ 
    public SelectTag(ViewContext viewContext, params object[] elementData) 
     : base(viewContext, elementData) 
    { 
     base._tag = new TagBuilder("select"); 
    } 

    public override TagBuilder GetTag() 
    { 
     return new TagBuilder("select"); 
    } 
} 

public class OptionTag : HtmlTypeBase 
{ 
    public OptionTag(ViewContext viewContext, params object[] elementData) 
     : base(viewContext, elementData) 
    { 
     base._tag = new TagBuilder("option"); 
    } 

    public override TagBuilder GetTag() 
    { 
     return new TagBuilder("option"); 
    } 
} 

public class AnchorTag : HtmlTypeBase 
{ 
    public AnchorTag(ViewContext viewContext, params object[] elementData) 
     : base(viewContext, elementData) 
    { 
     base._tag = new TagBuilder("a"); 
    } 

    public override TagBuilder GetTag() 
    { 
     return new TagBuilder("a"); 
    } 
} 

и это определение MMTag:

public class MMTag : IDisposable 
{ 
    internal bool _disposed; 
    internal ViewContext _viewContext; 
    internal TextWriter _writer; 
    internal TagBuilder _tag; 

    public MMTag() {} 

    public MMTag(ViewContext viewContext, params object[] elementData){ } 

    protected void MergeDataToTag(ViewContext viewContext, object[] elementData) 
    { 
     MergeTypeDataToTag(elementData[0]); 
     MergeAttributeDataToTag(elementData[1]); 

     this._viewContext = viewContext; 

     this._viewContext.Writer.Write(_tag.ToString(TagRenderMode.StartTag)); 
    } 

    private void MergeAttributeDataToTag(object attributeData) 
    { 
     var dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 
     var attributes = attributeData; 
     if (attributes != null) 
     { 
      foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(attributes)) 
      { 
       object value = descriptor.GetValue(attributes); 
       dic.Add(descriptor.Name, value); 
      } 
     } 

     _tag.MergeAttributes<string, object>(dic); 
    } 

    private void MergeTypeDataToTag(object typeData) 
    { 
     Type elementDataType = typeData.GetType(); 
     foreach (PropertyInfo prop in elementDataType.GetProperties()) 
     { 
      if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(Decimal) || prop.PropertyType == typeof(String)) 
      { 
       object propValue = prop.GetValue(typeData, null); 
       string stringValue = propValue != null ? propValue.ToString() : String.Empty; 
       _tag.Attributes.Add(prop.Name, stringValue); 
      } 
     } 
    } 

    #region IDisposable 
    public void Dispose() 
    { 
     Dispose(true /* disposing */); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!_disposed) 
     { 
      _disposed = true; 
      if (disposing) 
      { 
       _writer = _viewContext.Writer; 
       _writer.Write(_tag.ToString(TagRenderMode.EndTag)); 
      } 
     } 
    } 
    #endregion 
} 

Я хотел бы также добавить, что Фонд & ShareClass определяются как:

public class Fund 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 

    public Fund() 
    { 
     this.ID = 123; 
     this.Name = "MyFund"; 
    } 

    public Fund(int id, string name) 
    { 
     this.ID = id; 
     this.Name = name; 
    } 
} 

public class ShareClass 
{ 
    public string ShareClassName { get; set; } 
    public string ShareClassType { get; set; } 
    public IEnumerable<Fund> Funds { get; set; } 

    public ShareClass(string name, string shareClassType) 
    { 
     this.ShareClassName = name; 
     this.ShareClassType = shareClassType; 
    } 
} 

ответ

1

Have вы считали использование конвенции для создания своих тегов? Похоже, вы требуете много повторяющегося кода в своих представлениях. Каждый раз, когда вы хотите выпадающего списка, вам придется копировать полдюжины строк.

Использование уместных конструкторов ввода существенно упростит ваше представление, но требует немного настройки с вашей стороны. По моему опыту, это настроение - это ХОРОШЕЕ ВРЕМЯ, когда вы сберегаете дорогу!

Идея конструктора ввода заключается в том, что вы указываете, что для свойства на вашей модели представления есть элемент ввода (или отображение или метка и т. Д.). Затем ваша структура конструктора входных данных проверяет свойство для его типа и атрибутов и определяет правильный тип ввода для визуализации.

Вот пример того, как выпадающий построен в моем текущем проекте:

//View Model 
[OptionsFrom("Years")] 
public int ContractYear{ get; set; } 

public IDictionary Years 
{ 
    get 
    { 
     var currentYear = DateTime.Today.Year; 
     return Enumerable.Range(0, 10).ToDictionary(i => currentYear + i, i => (currentYear + i).ToString()); 
    } 
} 

//View 
Html.InputFor(x => x.ContractYear); 

Here является проходными на использование входных строителей MVCContrib в. Я знаю, что в MVC2 есть поддержка ввода-вывода, но я не знаком с ней. По моему мнению, лучшие строители, основанные на соглашениях, доступны от FubuMVC. У меня есть сообщение here о том, как использовать их с ASP.NET MVC. У меня пока нет сообщений о том, как их настроить, хотя я планирую это скоро.

+0

Здесь вы можете найти информацию об «некоторой поддержке конструктора ввода в MVC2»: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction. html :) –

Смежные вопросы