我在 ASP.NET MVC 4 项目中使用 DevExpress 控件。
我在GridView
ASP.NET MVC 的扩展中使用未绑定的列。在CustomUnboundColumnData
事件处理程序中,e.GetListSourceFieldValue
总是为我返回 null。
然后,我尝试直接从模型中获取值,而不是调用此方法(请参阅该方法调用上方的注释行),但即使这样有效,它也有副作用,我现在不会讨论.
我正在使用带有 Visual Web Developer Express 2010 版的 ASP.NET MVC 4。我正在为 MVC v12.2.10.0 使用 DevExpress 扩展。
我的操作系统是 Windows 7,64 位。但是,我使用的扩展只是 32 位的。
我无法交付我的整个解决方案,因为它被分解为多个项目,其中大部分都有我为我的客户编写的大量 IP 代码。但这是我的代码中的相关部分。
Index.cshtml (Razor 视图引擎) ------------------------------------------ -------------------------
@model List<GlobalizationUI.Presentation.ViewModels.StringTableRow>
@{
ViewBag.Title = "Strings";
}
<div id = "pageCaption">Strings</div>
@Html.Partial("_StringsPartial", Model)
_StringsPartial.cshtml (Razor 视图引擎) ------------------------------------------ -------------------------
@using System.Web.UI.WebControls;
@using System.Data;
@model List<GlobalizationUI.Presentation.ViewModels.StringTableRow>
@Html.DevExpress().GridView(settings =>
{
settings.Name = "gvStrings";
settings.CallbackRouteValues = new { Controller = "Strings", Action = "StringsPartial" };
settings.Width = 1200;
settings.SettingsPager.Position = PagerPosition.TopAndBottom;
settings.SettingsPager.FirstPageButton.Visible = true;
settings.SettingsPager.LastPageButton.Visible = true;
settings.SettingsPager.PageSizeItemSettings.Visible = true;
settings.SettingsPager.PageSizeItemSettings.Items = new string[] { "10", "20", "50", "100", "200" };
settings.SettingsPager.PageSize = 50;
settings.Settings.ShowFilterRow = true;
settings.Settings.ShowFilterRowMenu = true;
settings.CommandColumn.Visible = true;
settings.CommandColumn.ClearFilterButton.Visible = true;
settings.Settings.ShowHeaderFilterButton = true;
settings.KeyFieldName = "ResourceKeyId";
settings.Columns.Add("Key");
var categoryColumn = settings.Columns.Add("CategoryId", "Category");
categoryColumn.ColumnType = MVCxGridViewColumnType.ComboBox;
var categoryColumnEditProperties = categoryColumn.PropertiesEdit as ComboBoxProperties;
categoryColumnEditProperties.DataSource = ViewBag.AllCategories;
categoryColumnEditProperties.TextField = "Name";
categoryColumnEditProperties.ValueField = "Id";
categoryColumnEditProperties.ValueType = typeof(long);
if (Model != null && Model.Count > 0 &&
Model[0] != null && Model[0].StringValues != null && Model[0].StringValues.Count > 0)
{
foreach (var kvp in Model[0].StringValues)
{
settings.Columns.Add(col =>
{
col.FieldName = kvp.CultureShortName;
col.Caption = kvp.CultureShortName;
col.UnboundType = DevExpress.Data.UnboundColumnType.Object;
col.SetDataItemTemplateContent(container => { ViewContext.Writer.Write(DataBinder.Eval(container.DataItem, col.FieldName + ".StringValue")); });
col.SetEditItemTemplateContent(container =>
{
Html.DevExpress().TextBox(s =>
{
s.Name = string.Format("txt{0}", kvp.CultureShortName);
}).Bind(kvp.StringValue).Render();
});
});
}
}
settings.CustomUnboundColumnData = (sender, e) =>
{
var fixedColumns = new List<string> { "ResourceKeyId", "Key", "CategoryId" };
if (!fixedColumns.Contains(e.Column.FieldName))
{
if (e.IsGetData)
{
try
{
// var values = Model[e.ListSourceRowIndex].StringValues;
var values = e.GetListSourceFieldValue(e.ListSourceRowIndex, "StringValues") as IList<GlobalizationUI.Presentation.ViewModels.CultureNameAndStringValue>;
if (values != null)
{
var value = values.FirstOrDefault(pair => pair.CultureShortName == e.Column.FieldName);
var defaultValue = default(GlobalizationUI.Presentation.ViewModels.CultureNameAndStringValue);
e.Value = value.Equals(defaultValue) ? defaultValue : new GlobalizationUI.Presentation.ViewModels.CultureNameAndStringValue();
}
}
catch (Exception ex)
{
System.Diagnostics.Debugger.Break();
System.Diagnostics.Debug.Print(ex.ToString());
}
}
}
};
foreach (GridViewDataColumn column in settings.Columns)
{
column.Settings.HeaderFilterMode = HeaderFilterMode.CheckedList;
}
settings.SettingsEditing.AddNewRowRouteValues = new { Controller = "Strings", Action = "CreateNew" };
settings.SettingsEditing.UpdateRowRouteValues = new { Controller = "Strings", Action = "Edit" };
settings.SettingsEditing.DeleteRowRouteValues = new { Controller = "Strings", Action = "Delete" };
settings.SettingsEditing.Mode = GridViewEditingMode.Inline;
settings.SettingsBehavior.ConfirmDelete = true;
settings.CommandColumn.Visible = true;
settings.CommandColumn.NewButton.Visible = true;
settings.CommandColumn.EditButton.Visible = true;
settings.CommandColumn.UpdateButton.Visible = true;
settings.CommandColumn.DeleteButton.Visible = true;
}).Bind(Model).GetHtml()
字符串控制器------------------------------------------------- ------------------
using System.Data;
using System.Web.Mvc;
using GlobalizationUI.BusinessObjects;
using Resources.BaseServices.Caching;
using System.Collections.Generic;
using System.ComponentModel;
using GlobalizationUI.Presentation.ViewModels;
using Resources.Util;
namespace GlobalizationUI.Presentation.Controllers
{
public class StringsController : Controller
{
private static string CacheKey_StringTable = "CacheKey_StringTable";
private static string CacheKey_AllCategories = "CacheKey_AllCategories";
private static object padLock = new object();
public ActionResult Index()
{
var stringTable = GetStringTable();
ViewBag.AllCategories = GetCategoryList();
return View(stringTable);
}
public ActionResult StringsPartial()
{
var stringTable = GetStringTable();
ViewBag.AllCategories = GetCategoryList();
return PartialView("_StringsPartial", stringTable);
}
[HttpPost]
public ActionResult CreateNew(StringTableRow row)
{
System.Diagnostics.Debugger.Break();
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(row))
{
System.Diagnostics.Debug.Print(prop.Name);
}
return Content("Hello, there!");
}
[HttpPost]
public ActionResult Edit(DataRow row)
{
return new EmptyResult();
}
[HttpPost]
public ActionResult Delete(long resourceKeyId)
{
return new EmptyResult();
}
private IEnumerable<Category> GetCategoryList()
{
lock (padLock)
{
if (CacheManager.Contains(CacheKey_AllCategories))
{
return CacheManager.Get<IEnumerable<Category>>(CacheKey_AllCategories);
}
}
var list = Category.All;
lock (padLock)
{
CacheManager.Add(CacheKey_AllCategories, list);
}
return list;
}
private List<StringTableRow> GetStringTable()
{
List<StringTableRow> stringTable;
lock (padLock)
{
if (CacheManager.Contains(CacheKey_StringTable))
{
return CacheManager.Get<List<StringTableRow>>(CacheKey_StringTable);
}
}
stringTable = new StringTable().ToListOfStringTableRows();
lock (padLock)
{
CacheManager.Add(CacheKey_StringTable, stringTable);
}
return stringTable;
}
}
}
查看模型------------------------------------------------ ------------------
using System.Collections.Generic;
namespace GlobalizationUI.Presentation.ViewModels
{
public class StringTableRow
{
public long ResourceKeyId { get; set; }
public string Key { get; set; }
public long CategoryId { get; set; }
public List<CultureNameAndStringValue> StringValues { get; set; }
}
}
namespace GlobalizationUI.Presentation.ViewModels
{
public class CultureNameAndStringValue
{
public CultureNameAndStringValue() : this(null, null) { }
public CultureNameAndStringValue(string cultureShortName, string stringValue)
{
CultureShortName = cultureShortName;
StringValue = stringValue;
}
public string CultureShortName { get; set; }
public string StringValue { get; set; }
}
}
模型: - - - - - - - - - - - - - - - - - - - - - - - - ------------------
using System.Data;
using GlobalizationUI.Data;
namespace GlobalizationUI.BusinessObjects
{
public class StringTable : DataTable
{
public StringTable()
{
var sql = @"
declare @stmt nvarchar(max)
select @stmt =
isnull(@stmt + ', ', '') +
'max(case when s.CultureId = ' + cast(c.Id as nvarchar(max)) +
' then s.ResourceValue end) as ' + quotename(c.ShortName)
from Culture as c
where c.Supported = 1
select @stmt = '
select
rk.Id AS ResourceKeyId,
rk.Name AS [Key],
c.Id AS CategoryId,
c.Name as CategoryName, ' + @stmt + '
from StringCategory as sc
LEFT OUTER join Category as c on c.Id = sc.CategoryId
RIGHT OUTER JOIN ResourceKey as rk on rk.Id = sc.ResourceKeyId
inner join Strings as s on s.ResourceKeyId = rk.Id
group by rk.Id, rk.Name, c.Id, c.Name
'
exec sp_executesql @stmt = @stmt;";
this.Merge(Database.DefaultDatabase.GetDataTable(sql));
}
}
}
模型到视图模型的转换:-------------------------------------------- ----------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlTypes;
namespace GlobalizationUI.Presentation.ViewModels
{
public static class DataTableExtensions
{
public static List<StringTableRow> ToListOfStringTableRows(this DataTable dataTable)
{
var ret = new List<StringTableRow>();
if (dataTable == null || dataTable.Rows.Count == 0) return ret;
foreach (DataRow row in dataTable.Rows)
{
StringTableRow stringTableRow = new StringTableRow();
foreach (DataColumn column in dataTable.Columns)
{
if (string.Compare(column.ColumnName, "ResourceKeyId", true) == 0)
{
stringTableRow.ResourceKeyId = (long)row[column.ColumnName];
}
else if (string.Compare(column.ColumnName, "Key", true) == 0)
{
stringTableRow.Key = (string)row[column.ColumnName];
}
else if (string.Compare(column.ColumnName, "CategoryId", true) == 0)
{
var categoryId = row[column.ColumnName];
stringTableRow.CategoryId = categoryId == DBNull.Value ? 0 : (long)categoryId;
}
else if (string.Compare(column.ColumnName, "CategoryName", true) == 0)
{
continue;
}
else
{
if (stringTableRow.StringValues == null)
stringTableRow.StringValues = new List<CultureNameAndStringValue>();
stringTableRow.StringValues.Add(new CultureNameAndStringValue(column.ColumnName, (string)row[column.ColumnName]));
}
}
ret.Add(stringTableRow);
}
return ret;
}
}
}
用户界面应该看起来有点像上面显示的图片。SQL 查询返回的列数是可变的,具体取决于特定安装/部署/业务客户端支持的文化数量。因此需要使用未绑定的列。
当我将数据直接绑定到 aSystem.Data.DataTable
并且没有使用任何未绑定的列时,我不是通过这段代码而是通过旧版本的代码拍摄这张照片的。这一切都很好,直到我不得不在 MVC/服务器端编辑操作中的数据。因此,我从使用 a 切换DataTable
到 POCO,并对所有文化使用未绑定的列。
请帮忙。