目前,我正在创建自定义MVC Html助手,我将通过流畅的API使用。为了给出一个示例来理解它,我将使用以下帮助程序(或者应该在不久的将来)生成一个网格:
@(Html.GridFor(Model)
.WithName("MyName")
.WithColumns(model =>
{
model.Bind(x => x.Name);
model.Bind(x => x.DateCreated);
model.Bind(x => x.DateUpdated);
}).Render());
现在,一切都是用起始点构造的。IGridBuilder。
/// <summary>
/// When implemented by a class, it defines the class as an object that can construct a grid by using a fluent API.
/// </summary>
public interface IGridBuilder<TModel> : IHtmlHelper, IDataSource<TModel>
{
#region Properties
/// <summary>
/// Gets the name of the <see cref="IGridBuilder{TModel}" />.
/// </summary>
string Name { get; }
#endregion
#region Methods
/// <summary>
/// Sets the name of the <see cref="IGridBuilder{TModel}" />. This name will be used as an id on the outer element that
/// holds the entire grid.
/// </summary>
/// <param name="name">The name that the <see cref="IGridBuilder{TModel}" /> should have.</param>
/// <returns>An <see cref="IGridBuilder{TModel}" /> that can be used to construct the grid through a fluent API.</returns>
IGridBuilder<TModel> WithName(string name);
/// <summary>
/// Set the columns of the model that should be bound to grid.
/// </summary>
/// <param name="bindAllColumns">The action that will bind all the columns.</param>
/// <returns>An <see cref="IGridBuilder{TModel}" /> that is used to construct the grid.</returns>
IGridBuilder<TModel> WithColumns(Action<IColumnBinder<TModel>> bindAllColumns);
/// <summary>
/// Renders the grid with all the set properties.
/// </summary>
/// <returns>A <see cref="MvcHtmlString" /> that contains the HTML representation of the grid.</returns>
MvcHtmlString Render();
#endregion
}
和绑定命令,我使用IColumnBinder接口:
/// <summary>
/// When implemented by a class, this class is marked as being an builder that can construct a column through a fluent API.
/// </summary>
/// <typeparam name="TModel"></typeparam>
public interface IColumnBinder<TModel> : IHtmlHelper, IDataSource<TModel>
{
#region Methods
/// <summary>
/// Binds an column to the grid.
/// </summary>
/// <typeparam name="TItem">The type of the column on which to bind the items.</typeparam>
/// <param name="propertySelector">The functional that will bind the control to the grid.</param>
void Bind<TItem>(Expression<Func<TModel, TItem>> propertySelector);
/// <summary>
/// Apply a specific css class on an element.
/// </summary>
/// <param name="className">The name of the css class that should be placed on the element.</param>
/// <returns>As <see cref="IColumnBinder{TModel}"/> that is used to construct this column through a fluent API.</returns>
IColumnBinder<TModel> WithCss(string className);
#endregion
}
现在,链接IColumnBuilder到IGridBuilder的最佳方法是什么?
为了使它非常简短,我在下面挣扎:
IColumnBuilder设置了特定的属性,但是渲染发生在IGridBuilder接口中。
主要问题在于下面的代码:
/// <summary>
/// Set the columns of the model that should be bound to grid.
/// </summary>
/// <param name="bindAllColumns">The action that will bind all the columns.</param>
/// <returns>An <see cref="IGridBuilder{TModel}" /> that is used to construct the grid.</returns>
public IGridBuilder<TModel> WithColumns(Action<IColumnBinder<TModel>> bindAllColumns)
{
bindAllColumns(new ColumnBinder<TModel>());
return this;
}
这里我执行了绑定列的操作:
model.Bind(x => x.Name)
但是我如何保持IGridBuilder和IColumnBuilder之间的引用,以便在战后以适当的方式构建它?
还是有其他解决方案?
Ok,
经过几个小时的搜索,我找到了一个解决方案,因此我正在回答我自己的问题。但是,如果有用户有另一种方法来解决同样的问题,请告诉我,以便我可以调整我的代码。
这段代码将主要使用类来传输对象,因为类是引用类型,所以它可以传递给另一个对象,并且该对象可以操作该对象。
因此,我编写了一个自定义HTML帮助器,其工作方式如下:@(Html.GridFor(Model)
.WithName("MyName")
.WithColumns(model =>
{
model.Bind(x => x.Name).WithCss("row first");
model.Bind(x => x.DateCreated);
model.Bind(x => x.DateUpdated);
}).Render());
我确实有一个可枚举的模型,我将把它传递给网格。网格采用模型的3列并呈现网格。
对于这段代码,我有两个接口在整个过程中帮助我:
一个HTML Helper接口(只是保存一个对象到HtmlHelper):
/// <summary> /// Provides a way to extend the <see cref="HtmlHelper" /> to construct objects of various kinds. /// </summary> public static class HtmlHelperExtensions { #region Grid /// <summary> /// Constructs a grid for a property that holds a collection. /// </summary> /// <typeparam name="TModel">The type of the model on which this grid is being build.</typeparam> /// <typeparam name="TEntity">The type of a single item in the collection. </typeparam> /// <param name="htmlHelper">The helper on which this method is executed. </param> /// <param name="dataSource">The datasource on which the items are bound. </param> /// <returns>An <see cref="IGridBuilder{TEntity}" /> that is used to construct the grid.</returns> public static IGridBuilder<TEntity> GridFor<TModel, TEntity>(this HtmlHelper<TModel> htmlHelper, IEnumerable<TEntity> dataSource) { return new GridBuilder<TEntity>(htmlHelper, dataSource); } #endregion }
一个数据源接口(只是保存一个到数据源的接口):
public interface IDataSource<out TModel> { #region Properties /// <summary> /// Gets the source that will be bound to the implemented object. /// </summary> IEnumerable<TModel> DataSource { get; } #endregion }
然后是所有其他代码。
HTML Helper扩展类是第一个:
/// <summary> /// Provides a way to extend the <see cref="HtmlHelper" /> to construct objects of various kinds. /// </summary> public static class HtmlHelperExtensions { #region Grid /// <summary> /// Constructs a grid for a property that holds a collection. /// </summary> /// <typeparam name="TModel">The type of the model on which this grid is being build.</typeparam> /// <typeparam name="TEntity">The type of a single item in the collection.</typeparam> /// <param name="htmlHelper">The helper on which this method is executed.</param> /// <param name="dataSource">The datasource on which the items are bound.</param> /// <returns>An <see cref="IGridBuilder{TEntity}" /> that is used to construct the grid.</returns> public static IGridBuilder<TEntity> GridFor<TModel, TEntity>(this HtmlHelper<TModel> htmlHelper, IEnumerable<TEntity> dataSource) { return new GridBuilder<TEntity>(htmlHelper, dataSource); } #endregion }
下一个是IGridBuilder接口的实现:
/// <summary> /// Provides an implemention of the <see cref="IGridBuilder{TModel}" /> that is used to construct the grid through a /// fluent API. /// </summary> /// <typeparam name="TModel">The type of the model that the grid will hold.</typeparam> public class GridBuilder<TModel> : IGridBuilder<TModel> { #region Constructors /// <summary> /// Creates a new instance of the <see cref="GridBuilder{TModel}" />. /// </summary> /// <param name="helper">The <see cref="HtmlHelper" /> that is used to construct the grid.</param> /// <param name="dataSource">The collection of objects that will be bound to the grid.</param> public GridBuilder(HtmlHelper helper, IEnumerable<TModel> dataSource) { htmlHelper = helper; DataSource = dataSource; Constructor = new GridConstructor<TModel>(htmlHelper, DataSource); } #endregion #region IGridBuilder Members /// <summary> /// Gets the name of the <see cref="IGridBuilder{TModel}" />. /// </summary> public string Name { get; private set; } /// <summary> /// Gets the constructor that will be used to construct this <see cref="IGridBuilder{TModel}" />. /// </summary> public IGridContructor<TModel> Constructor { get; set; } /// <summary> /// Gets the source that will be bound to the implemented object. /// </summary> public IEnumerable<TModel> DataSource { get; private set; } /// <summary> /// Gets the <see cref="HtmlHelper" /> object. /// </summary> public HtmlHelper htmlHelper { get; private set; } /// <summary> /// Sets the name of the <see cref="IGridBuilder{TModel}" />. This name will be used as an id on the outer element that /// holds the entire grid. /// </summary> /// <param name="name">The name that the <see cref="IGridBuilder{TModel}" /> should have.</param> /// <returns>An <see cref="IGridBuilder{TModel}" /> that can be used to construct the grid through a fluent API.</returns> public IGridBuilder<TModel> WithName(string name) { Name = name; return this; } /// <summary> /// Set the columns of the model that should be bound to grid. /// </summary> /// <param name="bindAllColumns">The action that will bind all the columns.</param> /// <returns>An <see cref="IGridBuilder{TModel}" /> that is used to construct the grid.</returns> public IGridBuilder<TModel> WithColumns(Action<IColumnBinder<TModel>> bindAllColumns) { var columnBinder = new ColumnBinder<TModel>(Constructor); bindAllColumns(columnBinder); return this; } /// <summary> /// Renders the grid with all the set properties. /// </summary> /// <returns>A <see cref="MvcHtmlString" /> that contains the HTML representation of the grid.</returns> public MvcHtmlString Render() { var outputBuilder = new StringBuilder(); BaseElementBuilder parentElement = DivFactory.DivElement().WithCss("header"); outputBuilder.Append(parentElement.ToString(TagRenderMode.StartTag)); outputBuilder.Append(parentElement.ToString(TagRenderMode.EndTag)); return new MvcHtmlString(outputBuilder.ToString()); } #endregion }
然后是IGridColumnBinder的实现:
/// <summary> /// Provides an implementation of the <see cref="IColumnBinder{TModel}" /> that can be used to construct a column /// through a fluent API. /// </summary> /// <typeparam name="TModel">The type of the datasource that's bound to the grid.</typeparam> public class ColumnBinder<TModel> : IColumnBinder<TModel> { #region Constructors /// <summary> /// Creates a new instance of the <see cref="ColumnBinder{TModel}" />. /// </summary> /// <param name="constructor">An <see cref="IGridContructor{TModel}" /> that contains the builder to construct the grid.</param> public ColumnBinder(IGridContructor<TModel> constructor) { Constructor = constructor; } #endregion #region IColumnBinder Members /// <summary> /// Gets the values that are bound to this <see cref="IColumnBinder{TModel}" />. /// </summary> public IGridContructor<TModel> Constructor { get; private set; } /// <summary> /// Gets the css class of the <see cref="IColumnBinder{TModel}" />. /// </summary> public string CssClass { get; private set; } /// <summary> /// Gets the values that are bound to this <see cref="IColumnBinder{TModel}" />. /// </summary> public IList<object> Values { get; set; } /// <summary> /// Binds an column to the grid. /// </summary> /// <typeparam name="TItem">The type of the column on which to bind the items.</typeparam> /// <param name="propertySelector">The functional that will bind the control to the grid.</param> /// <returns>As <see cref="IColumnBinder{TModel}" /> that is used to construct this column through a fluent API.</returns> public IColumnBinder<TModel> Bind<TItem>(Expression<Func<TModel, TItem>> propertySelector) { string name = ExpressionHelper.GetExpressionText(propertySelector); name = Constructor.htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(() => default(TModel), typeof (TModel), name); // Get's the name to display on the column in grid. The Display attribute is used if present, otherwise the name of the property is used. string displayName = string.IsNullOrEmpty(metadata.DisplayName) ? metadata.PropertyName : metadata.DisplayName; Values = Constructor.DataSource.Select(myVar => propertySelector.Compile()(myVar)) .Select(dummy => (object) dummy) .ToList(); Constructor.builderProperties.Add(displayName, this); return this; } /// <summary> /// Apply a specific css class on an element. /// </summary> /// <param name="className">The name of the css class that should be placed on the element.</param> /// <returns>As <see cref="IColumnBinder{TModel}" /> that is used to construct this column through a fluent API.</returns> public IColumnBinder<TModel> WithCss(string className) { CssClass = className; return this; } #endregion }
最后是IGridConstructor的实现。
/// <summary> /// Provides an implemention of the <see cref="IGridContructor{TModel}" /> that is used to construct the grid through a /// fluent API. /// </summary> /// <typeparam name="TModel">The type of the model that the grid will hold.</typeparam> public class GridConstructor<TModel> : IGridContructor<TModel> { #region Constructors /// <summary> /// Creates a new instance of the <see cref="GridConstructor{TModel}" />. /// </summary> /// <param name="helper">The <see cref="HtmlHelper" /> that is used to built the model.</param> /// <param name="source">The model that is bound to the grid.</param> public GridConstructor(HtmlHelper helper, IEnumerable<TModel> source) { htmlHelper = helper; DataSource = source; builderProperties = new Dictionary<string, IColumnBinder<TModel>>(); } #endregion #region Properties /// <summary> /// Provides a dictionary that contains all the properties for the builder. /// </summary> public IDictionary<string, IColumnBinder<TModel>> builderProperties { get; set; } /// <summary> /// Gets the source that will be bound to the implemented object. /// </summary> public IEnumerable<TModel> DataSource { get; private set; } /// <summary> /// Gets the <see cref="HtmlHelper" /> object. /// </summary> public HtmlHelper htmlHelper { get; private set; } #endregion }
现在,这到底是如何工作的呢?
HtmlHelper返回一个实现了GridBuilder的成员,所以在上面的例子中,它返回一个GridBuilder。
在GridBuilder上,有几个你可以调用的元素,一个更重要的是WithColumns方法,它接受一个IColumnBinder动作,这是一个技巧。IColumnBinder的实现接受一个IGridConstructor的引用。这个构造器将被完全构建。
因此,我们需要知道的所有事情,包括给定列的每个css类,都通过GridBuilder公开。GridContructor
所以,一个很长的帖子,但我希望它能帮助一些人。