6

在花了最后几天搜索之后,我正式陷入困境。我正在将一个对象绑定到 Telerik MVC 3 Grid,但问题是它需要动态创建列(不是自动生成的)。其中三列是已知的,其他列是未知的,这是棘手的部分。基本上,它可以像这些例子:

已知列1 | 已知列2 | 未知列1 | 已知列 3 已知列 1 | 已知列2 | 未知列1 | 未知列2 | 未知列3 | 已知列 3 等

因为我将未知列放在列表中(我也尝试过字典,所以我可以获取列名),这对我来说在绑定时很复杂。我的代码如下:

模型(可以有0到数百行,但是这个模型在List类型的视图模型中,也可以有0到20加上动态添加的列)

public class VendorPaymentsGLAccount
{
    public string GeneralLedgerAccountNumber { get; set; }
    public string GeneralLedgerAccountName { get; set; }
    public string DisplayName { get { return string.Format("{0} - {1}", GeneralLedgerAccountNumber, GeneralLedgerAccountName); } }
    public Dictionary<string, double> MonthAmount { get; set; }
    public double Total { get { return MonthAmount.Sum(x => x.Value); } }
    public List<string> Columns { get; set; }
    public List<double> Amounts { get; set; }

    public VendorPaymentsGLAccount() { }
}

查看(被注释掉的部分是试图使用字典)

<fieldset>
    <legend>General Ledger Account Spend History</legend>
    @if (Model.VendorPaymentsGLAccounts != null)
    {

            <br />
            @(Html.Telerik().Grid(Model.VendorPaymentsGLAccounts)
                    .Name("Grid")
                    .Columns(columns =>
                    {
                        columns.Bound(gl => gl.DisplayName).Title("General Ledger Account").Width(200).Filterable(false).Sortable(false);

                        //foreach (var month in Model.VendorPaymentsGLAccounts[0].MonthAmount)
                        //{
                        //    //columns.Bound(gl => gl.MonthAmount[month.Key.ToString()].ToString()).Title(month.Key.ToString()).Width(100).Filterable(false).Sortable(false);
                        //    //columns.Template(v => Html.ActionLink(v.VoucherID, "VoucherSummary", new { id = v.VoucherID, bu = v.BusinessUnitID, dtt = v.InvoiceDate.Ticks })).Title("Voucher").Width(100);
                        //    columns.Template(gl => Html.ActionLink(gl.MonthAmount[month.Key.ToString()].ToString(), "VoucherSummary")).Title(month.Key.ToString()).Width(100);
                        //}

                        for (int i = 1; i <= (Model.VendorPaymentsGLAccounts[0].Columns.Count() - 1); i++)
                        {
                            string colTemp = Model.VendorPaymentsGLAccounts[0].Columns[i - 1];
                            columns.Template(gl => gl.Amounts[i - 1]).Title(colTemp).Width(100);
                        }

                        columns.Template(gl => String.Format("{0:C}", gl.Total)).Title("Total");
                    })
                    .Sortable()
                    .Pageable()
                    .Filterable()
                    .Footer(true))
    }
    else
    {
        <br />
        @:There are no records that match your selected criteria.
    }
</fieldset>

使用字典方法,我能够使用正确的标题文本正确生成列,但列的值(在我的测试中只有 2 列)是相同的。有人能帮忙吗?这似乎是一个奇怪的问题。只是想弄清楚如何正确地做到这一点。

更新:这是使用显示问题的字典方法的屏幕截图。列标题正确,但两个动态列的值相同。

问题截图

4

5 回答 5

7

在 Telerik 网格控件中使用动态定义的列可能会很棘手。但在你的情况下,这主要是一个典型的闭包陷阱。

在以下循环中,编译器会将 的每个实例绑定gl => gl.Amounts[i - 1]到变量i并稍后对其进行评估:

for (int i = 1; i <= (Model.VendorPaymentsGLAccounts[0].Columns.Count() - 1); i++)
{
  string colTemp = Model.VendorPaymentsGLAccounts[0].Columns[i - 1];
  columns.Template(gl => gl.Amounts[i - 1]).Title(colTemp).Width(100);
}

事实上,它是在循环完成后评估的。所以i总会有导致循环完成的值。

解决方法是使用临时变量:

for (int i = 1; i <= (Model.VendorPaymentsGLAccounts[0].Columns.Count() - 1); i++)
{
  string colTemp = Model.VendorPaymentsGLAccounts[0].Columns[i - 1];
  int columnIndex = i - 1;
  columns.Template(gl => gl.Amounts[columnIndex]).Title(colTemp).Width(100);
}
于 2012-07-13T17:10:59.403 回答
2

我在运行时使用反射动态绑定列:

@model IEnumerable<object>
@using System.Collections
@using System.Collections.Generic
@using System.Reflection;

@(Html.Telerik().Grid(Model)
    .Name("Grid")
    .Columns(columns =>                    
        {
            Type t = Model.GetType().GetGenericArguments()[0];
            foreach (var prop in t.GetProperties())
            {
                if (IsCoreType(prop.PropertyType))
                {
                    columns.Bound(prop.PropertyType, prop.Name);
                }
            }
        })    
        .DataBinding(binding => binding.Ajax()                                        
            .Select("SelectMethod", "SomeController")                                    
        )    
    .Sortable()    
    .Pageable()
    .Filterable()
    .Groupable()
)

@functions{
    public bool IsCoreType(Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            return IsCoreType(type.GetGenericArguments()[0]);
        }
        return !(type != typeof(object) && Type.GetTypeCode(type) == TypeCode.Object);
    }
}
于 2012-11-21T19:02:58.920 回答
2

我遇到了同样的问题,并且在谷歌上搜索了很多小时,并且从各种援助热线进行了很多尝试。但即便如此,解决起来也不是那么简单!

出于这个原因,并且在这里还有另一个工作示例,我还将提供我的解决方案!

信息:它只适用于我所在的IList模型。其他收藏也有问题!

@model IList<CBS.Web.Models.Equipment.EquipmentViewModel>
@(Html.Telerik().Grid(Model)
    .Name("Grid")

    .DataKeys(keys =>
    {
        keys.Add(m => m.ID);
    })

    .DataBinding(dataBinding =>
    {
        dataBinding.Ajax()
            // renders the grid initially
            .Select("EquipmentGrid", "Equipment");
    })                    

    .Columns(columns =>
    {
        // Equipment IDs
        columns.Bound(m => m.ID).Hidden(true);
        columns.Bound(m => m.Name).Title("Equipments").Width(200);

        // Every item (EquipmentViewModel) of the Model has the same Count of Fields
        for (int i = 0; i < (Model[0].Fields.Count()); i++)
        {
            // Name of the column is everytime same as in Model[0]
            string columnName = Model[0].Fields.ElementAt(i).FieldDefinition.Name;
            // Constructs i-counted columns, dynamically on how much
            // Fields are owned by an Equipment. But note, that all Equipment-items
            // in the Model must have the same Count and disposal of Fields! 
            columns.Template(m => m.Fields
                                    .Where(f => f.FieldDefinition.Name == columnName)
                                    .Where(f => f.EquipmentId == m.ID).First().Value)
                                    .Title(columnName)
                                    .Width(columnName.Length * 8); // * 8 was the optimal lenght per character
        }
    })

    .ClientEvents(events => events.OnRowSelect("onRowSelected"))

    .Selectable()
    .Resizable(resizing => resizing.Columns(true))
    .Pageable()
    .Scrollable()
    .Groupable()
    .Filterable()
)

控制器:

public ActionResult EquipmentGrid(Guid id)
{            
    var belongingEquipments = _equipmentRepository.GetNotDeleted()
                                .OrderBy(e => e.Name).ToList()
                                .Where(e => e.RevisionId == id);

    List<EquipmentViewModel> equVMList = new List<EquipmentViewModel>();

    for (int i = 0; i < belongingEquipments.Count(); i++)
    {
        var equVM = new EquipmentViewModel
        {
            ID = belongingEquipments.ElementAt(i).ID,
            Name = belongingEquipments.ElementAt(i).Name,
            RevisionId = belongingEquipments.ElementAt(i).RevisionId,
            EquipmentTypeId = belongingEquipments.ElementAt(i).EquipmentTypeId,

            Fields = SortFields(belongingEquipments.ElementAt(i).Fields.ToList())
        };
        equVMList.Add(equVM);
    }

    return PartialView("EquipmentGrid", equVMList);
}

楷模:

namespace CBS.Web.Models.Equipment
{
    public class EquipmentViewModel
    {
        public Guid ID { get; set; }
        public string Name { get; set; }

        public Guid RevisionId { get; set; }                            
        public Guid EquipmentTypeId { get; set; }

        public virtual ICollection<FieldEntity> Fields { get; set; }
    }
}

字段定义

namespace CBS.DataAccess.Entities
{
    public class FieldDefinitionEntity : EntityBase
    {
        [Required]
        public virtual Guid EquipmentTypeId { get; set; }
        public virtual EquipmentTypeEntity EquipmentType { get; set; }

        [Required(AllowEmptyStrings = false)]
        public virtual string Name { get; set; }

        public virtual int Numbering { get; set; }

        [Required]
        public virtual Guid TypeInformationId { get; set; }
        public virtual TypeInformationEntity TypeInformation { get; set; }

        public virtual ICollection<FieldEntity> Fields { get; set; }
    }
}

场地

namespace CBS.DataAccess.Entities
{
    public class FieldEntity : EntityBase
    {
        [Required]
        public virtual Guid EquipmentId { get; set; }
        public virtual EquipmentEntity Equipment { get; set; }

        [Required]
        public virtual Guid FieldDefinitionId { get; set; }
        public virtual FieldDefinitionEntity FieldDefinition { get; set; }

        public virtual string Value { get; set; }
    }
}
于 2012-09-07T22:12:21.413 回答
1

这是解决方法:

@(Html.Telerik().Grid(Model.Users)
    .Name("Grid")
    .Columns(columns => {
        columns.GenerateCustomColumns(columnSettings);          
       }).DataBinding(dataBinding => dataBinding.Ajax().Select("_getusers", "home"))
           .Scrollable(scrolling => scrolling.Enabled(true).Height("auto"))
           .Pageable(paging => paging.Enabled(true)
           .PageSize(10, new int[] { 5, 10, 20, 50, 100, 500 })
           .Position(GridPagerPosition.Both)
           .Total(Model.Users.Count)
           .Style(GridPagerStyles.PageSizeDropDown | GridPagerStyles.NextPreviousAndNumeric)
                           .PageTo(1))
           .Filterable(filtering => filtering.Enabled(true))
           .Reorderable(reordering => reordering.Columns(true))
               .NoRecordsTemplate(" ")
           .EnableCustomBinding(true)

)

// 动态生成列的扩展方法

public static class TelerikMvcGridColumnHelper
{
    public static void GenerateCustomColumns<T>(this GridColumnFactory<T>   columns,List<GridCustomColumnSettings> settings) where T:class
    {
        if (settings != null)
        {
            settings.ForEach(column =>
            {
                var boundedColumn = columns.Bound(column.Member);
                if (column.ClientFooterTemplate != null)
                    boundedColumn.ClientFooterTemplate(column.ClientFooterTemplate);

                if (!string.IsNullOrEmpty(column.Width))
                    boundedColumn.Width(column.Width);

            });
        }

    }
}

// 列设置类

public class GridCustomColumnSettings : GridColumnSettings
 {
    public string ClientFooterTemplate { get; set; }

 }
于 2012-12-18T07:25:49.630 回答
0

我做了这个简单的方法。注意:以下解决方案也适用于 ajax 编辑模式(不仅仅是只读网格):

当 ViewModel 是:

public class PriceSheetEditGridViewModel
{
    public IEnumerable<PriceSheetRowViewModel> Rows { get; set; }
    public IEnumerable<PriceSheetColumnViewModel> Columns { get; set; }
}

公共类 PriceSheetColumnViewModel { public int Id { get; 放; } 公共字符串标题 { 获取;放; } }

public class PriceSheetRowViewModel 
    {

        public int RowNo { get; set; }
        public string Description { get; set; }
        public double?[] Prices { get; set; }

    }

视图可以是这样的(view.cshtml 文件的一部分...):

    ....
@model PriceSheetEditGridViewModel
...
                columns.Bound(o => o.Description ).Width(150);

                int i = 0;
                foreach (var col in Model.Columns)


{
                columns
                    .Bound(model => model.Prices).EditorTemplateName("PriceSheetCellPrice").EditorViewData(new { ColumnId = i })
                    .ClientTemplate("<span><#=Prices ? jsHelper.addCommas(Prices[" + i.ToString() + "]):null#></span>")
                    .Title(col.Title).Width(80);
                i++;
            }    

....

PriceSheetCellPrice.cshtml编辑器模板文件(在 shared\editortemplates 文件夹中):

  @model decimal?
@(Html.Telerik().NumericTextBox()
        .Name(ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)+"["+ViewBag.ColumnId+"]")
                    .InputHtmlAttributes(new { style = "width:100%" })
})
        .EmptyMessage("")
        .DecimalDigits(0)
        .DecimalSeparator(",")
        .MinValue(0)
        .Value((double?) Model)
)
于 2013-03-13T05:59:45.040 回答