字段(Field)和部件(Part)的区别在于,字段是不用单独去创建表来存储数据的,而是通过Orchard框架的功能将其以xml的形式存储到Orchard_Framework_ContentItemRecord表的Data字段中,而Part则需要创建相应的表去存储这些记录。这种存储上的差异就决定了,字段形式适用于简单场景,如只是单纯显示一个单一实体;而部件方式适用于后续有较复杂的逻辑处理的场景,如存在一对多、多的多关系等。更进一步的来说,一个部件可以理解为一个完整功能的业务实体,而一个字段只是业务实体上的一个属性。下面我们就通过一个示例来介绍在Orchard中如何定义字段类型。
目标
创建模块
建立字段模型

using Orchard.ContentManagement;
using Orchard.ContentManagement.FieldStorage;
namespace MyCompany.LocaleField.Fields
{
/// <summary>
/// 自定义的字段需要继承ContentField类
/// Storage相当于是在ContentField中获取或设置值
/// </summary>
public class LocaleField : ContentField
{
public string Locale
{
get
{
var value = Storage.Get<string>();
return value;
}
set
{
Storage.Set(value == null ? String.Empty : value);
}
}
}
建立视图模型

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyCompany.LocaleField.ViewModels
{
public class LocaleFieldViewModel
{
public string Name { get; set; }
public string City { get; set; }
public string Address { get; set; }
/// <summary>
/// 作为示例这里就只简单构造一些城市的数据
/// 如果要做成产品,当然这部分需要处理的更好些
/// </summary>
public SelectList CityList
{
get
{
var lst = new List<SelectListItem>();
lst.Add(new SelectListItem { Text = "北京", Value = "北京" });
lst.Add(new SelectListItem { Text = "上海", Value = "上海" });
lst.Add(new SelectListItem { Text = "天津", Value = "天津" });
lst.Add(new SelectListItem { Text = "重庆", Value = "重庆" });
lst.Add(new SelectListItem { Text = "武汉", Value = "武汉" });
return new SelectList(lst, "Value", "Text", this.City);
}
}
}
写驱动器

using JetBrains.Annotations;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using MyCompany.LocaleField.ViewModels;
using Orchard.Localization;
namespace MyCompany.LocaleField.Drivers
{
public class LocaleFieldDriver : ContentFieldDriver<Fields.LocaleField>
{
/// <summary>
/// 定义模板查找规则,在Views目录下按照Fields/MyCompany.Locale规则查找模板
/// 比如显示画面就直接查找Fields目录下MyCompany.Locale.cshtml文件。(注:aspx模板也是支持的)
/// 编辑画面会查找EditorTemplates目录下的Fields\MyCompany.Locale.cshtml文件
/// </summary>
private const string TemplateName = "Fields/MyCompany.Locale"; // EditorTemplates/Fields/MyCompany.Locale.cshtml
/// <summary>
/// 获取字段前缀
/// 可以使编辑画面中各字段的表单控件都有唯一的名称,不至于出现同名冲突。
/// </summary>
/// <param name="field"></param>
/// <param name="part"></param>
/// <returns></returns>
private static string GetPrefix(ContentField field, ContentPart part)
{
return part.PartDefinition.Name + "." + field.Name;
}
/// <summary>
/// 显示画面
/// </summary>
/// <param name="part"></param>
/// <param name="field"></param>
/// <param name="displayType"></param>
/// <param name="shapeHelper"></param>
/// <returns></returns>
protected override DriverResult Display(ContentPart part, Fields.LocaleField field, string displayType, dynamic shapeHelper)
{
var value = field.Locale;
var viewModel = new LocaleFieldViewModel
{
Name = field.Name,
//地点数据由城市和地址构成,城市和地址用逗号分隔,如:上海,浦东新区张杨路XXX号
City = string.IsNullOrEmpty(value) ? "" : value.Substring(0, value.IndexOf(',')),
Address = string.IsNullOrEmpty(value) ? "" : value.Substring(value.IndexOf(',') + 1)
};
return ContentShape("Fields_MyCompany_Locale", // this is just a key in the Shape Table
() =>
shapeHelper.Fields_MyCompany_Locale( // Fields_MyCompany_Locale是一个动态类型,是视图Fields/MyCompany.Locale.cshtml的module,其中的Model属性,是LocaleFieldViewModel
Model: viewModel)
);
}
/// <summary>
/// 编辑画面显示
/// </summary>
/// <param name="part"></param>
/// <param name="field"></param>
/// <param name="shapeHelper"></param>
/// <returns></returns>
protected override DriverResult Editor(ContentPart part, Fields.LocaleField field, dynamic shapeHelper)
{
var value = field.Locale;
var viewModel = new LocaleFieldViewModel
{
Name = field.Name,
City = string.IsNullOrEmpty(value) ? "" : value.Substring(0, value.IndexOf(',')),
Address = string.IsNullOrEmpty(value) ? "" : value.Substring(value.IndexOf(',') + 1)
};
return ContentShape("Fields_MyCompany_Locale_Edit", // this is just a key in the Shape Table
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: viewModel, Prefix: GetPrefix(field, part)));
}
/// <summary>
/// 编辑画面Post
/// </summary>
/// <param name="part"></param>
/// <param name="field"></param>
/// <param name="updater"></param>
/// <param name="shapeHelper"></param>
/// <returns></returns>
protected override DriverResult Editor(ContentPart part, Fields.LocaleField field, IUpdateModel updater, dynamic shapeHelper)
{
var viewModel = new LocaleFieldViewModel();
if(updater.TryUpdateModel(viewModel, GetPrefix(field, part), null, null))
{
field.Locale = viewModel.City + "," + viewModel.Address;
}
return Editor(part, field, shapeHelper);
}
}
写模板

<!--这个动态类型的其中一个属性Model是一个LocaleFieldViewModel类型的-->
<p class="text-field">
<span class="name">@Model.Model.Name:</span>
@Model.Model.City
@Model.Model.Address
</p
接着,我们还需要在Views下面创建一个EditorTemplates目录,并在其下面创建一个Fields目录,然后再在里面在创建一个MyCompany.Locale.cshtml文件作为该自定义字段的编辑画面,代码如下:

<fieldset>
<label for="@Html.FieldIdFor(m => Model.City)">@Model.Name</label>
City:
@Html.DropDownListFor(m => m.City, this.Model.CityList)
Address:
@Html.TextBoxFor(m => m.Address)
</fieldset
最后我们别忘了添加一个Placement.info文件,没有这个文件我们刚刚定义的模版就无法在页面中显示,关于Placement.info文件的介绍可以查看《Understanding the placement.info File》,这里我们输入以下代码:

<Place Fields_MyCompany_Locale_Edit="Content:2.5"/>
<Match DisplayType="SummaryAdmin">
<Place Fields_MyCompany_Locale="Content"/>
</Match>
<Place Fields_MyCompany_Locale="Content:after"/>
</Placement
使用自定义字段


编辑画面
管理活动列表画面

活动详情画面
功能扩展
总结
参考文档
示例代码
点击下载 (在Orchard后台Modules菜单的Installed选项卡中安装压缩包里的nupkg文件 )
==========================================
作者:二十四画生
转载请注明来源于博客园——二十四画生的Blog,并保留有原文链接。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决
2005-07-28 [DNN模块开发]分类链接模块终于大功告成了