MVC 用扩展方法执行自定义视图,替代 UIHint
MVC 用扩展方法执行自定义视图,替代 UIHint
项目中用了 Bootstrap , 这样就不用写太多的CSS了,省去很多事情。
但是这个业务系统需要输入的地方很多,每个表都有100多个字段,每个页面需要大量的表单。
把这些表单按 bootstrap 的格式写出来,也是件头痛的事情。
我想到模板,EditorTemplates UIHint, 但是 UIHint 需要用 Metadata 标注,一个一个的加,也是不现实的。
还有别外一种办法,就是扩展 HtmlHelper。
要用HtmlHelper ,大家可能就想到了 TagBuilder 了,TagBuilder 基本全是 Hard code 了,不方便调整显示格式。
最终我用了另外一种办法:
在 HtmlHelper 扩展里,取自定义的视图,视图可以随时改,又不用每个字段去加 UIHint.

1 public static MvcHtmlString EditorBlockAFor<TModel, TProperty>(this HtmlHelper<TModel> helper, string template, Expression<Func<TModel, TProperty>> property, bool withLabel, string containerClass = "col-xs-4", object htmlAttributes = null) {
2
3 var body = (MemberExpression)property.Body;
4 if (body == null)
5 throw new ArgumentException();
6
7 var ctx = helper.ViewContext.Controller.ControllerContext;
8 var result = ViewEngines.Engines.FindPartialView(helper.ViewContext.Controller.ControllerContext, template);
9 if (result.View != null) {
10
11 var metadata = ModelMetadata.FromLambdaExpression(property, helper.ViewData);
12 //var model = metadata.Model;
13
14 var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
15
16 using (var writer = new StringWriter(CultureInfo.CurrentCulture)) {
17 var vctx = new ViewContext(ctx, result.View, helper.ViewData, helper.ViewContext.TempData, writer);
18 vctx.ViewBag.PropertyName = string.Join(".", body.ToString().Split(new char[] { '.' }).Skip(1));//body.Member.Name;
19
20 vctx.ViewBag.ContainerClass = containerClass;
21 vctx.ViewBag.IsRequired = metadata.IsRequired;
22 vctx.ViewBag.HtmlAttributes = attrs;
23 vctx.ViewBag.WithLabel = withLabel;
24
25 vctx.ViewBag.DisplayName = metadata.DisplayName ?? metadata.PropertyName;
26
27 result.View.Render(vctx, writer);
28
29 return MvcHtmlString.Create(writer.ToString());
30 }
31 } else {
32 throw new InvalidOperationException(string.Format("particle view {0} not found", template));
33 }
34 }

这个是核心,其它的扩展都是调用这个方法,比如:
1 public static MvcHtmlString TextBlockFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> property, string containerClass = "col-lg-4 col-md-4 col-xs-4", object htmlAttributes = null, bool withLabel = true) {
2 return EditorBlockAFor(helper, "TextBlock", property, withLabel, containerClass, htmlAttributes);
3 }
在 EditorBlockAFor 这个方法里,有一句:
var result = ViewEngines.Engines.FindPartialView(helper.ViewContext.Controller.ControllerContext, template);
这个是去查找指定的自定义视图,它就是整个思路的关键。
自定义视图 TextBlock.cshtml

@model object
@{
this.Layout = null;
string propertyExpression = ViewBag.PropertyName;
string containerClass = ViewBag.ContainerClass; RouteValueDictionary htmlAttributes = SharedTemplatesHelper.MargeClass(ViewBag);
} <div class="@containerClass">
@SharedTemplatesHelper.Label(ViewBag)
@Html.TextBox(propertyExpression, null, htmlAttributes)
</div>

注意,模型是object , 因为不确定模型的类型。
这个文件需要放到 Shared 下,这个是简单的结构。
调用:
@Html.TextBlockFor(m => m.VesselInfo.VESSEL_NAME_CN, "col-lg-2 col-md-2")
最终会生成这样一段HTML:

<div class="col-lg-2 col-md-2">
<span class="help-block">中文名称 <span class="red">*</span>
</span>
<input class="form-control input-sm" data-val="true" data-val-length="The field 中文名称 must be a string with a maximum length of 500." data-val-length-max="500" data-val-required="The 中文名称 field is required." id="VesselInfo_VESSEL_NAME_CN" name="VesselInfo.VESSEL_NAME_CN" type="text" value="" />
</div>

长这个样子:

--------------分隔线内是废话,有兴趣可以了解一下我的崩溃经历-------------------
一切都按照设想的样子,直到。。。。
有一天,同事说明明是 Required 的,为什么没有执行验证?
我简单的看了一下,是因为没有生成 data-xxx 这样的验证属性。看了一下 Action ,就是 Return View(); 没有传递 model 到视图。
声明了一个 model 传给视图 (return View(XXX);) 后,一切正常。
我做MVC3的时候,不给 Model 都会输出验证属性,当时赶时间,没有去深入研究为什么,还以为 MVC 5 的新特性呢。
国庆过7天猪一样的生活,项目也进行的七七八八了,终于有时间回头看看了。
下了MVC的最新源码:
http://aspnetwebstack.codeplex.com/SourceControl/latest
版本号是 5.2.3.0, 我们项目中用的是 5.2.0.0 ,差别不大。
新建了一个测试项目,
编译了一份MVC的DLL,连同PDB一起放到项目的引用目录下,改了一下MVC的配置、引用,在 InputExtensions 下加了断点,但是却无法断点。
按照网上的搜到的调试 MVC 源码的方法去做,很不幸,没有一个适用的。
搞到晚上8点多,还是没有办法调试进源码。真是崩溃至极了。
又找到了篇 pdb 符号服务器的博文:
http://weblogs.asp.net/gunnarpeipman/stepping-into-asp-net-mvc-source-code-with-visual-studio-debugger
但是需要下载这些符号,电脑没关,回去了。
今天按照博文的说明去做,仍然不能调试进源码。
很有幸,找到另外一篇文:
http://blogs.msdn.com/b/micl/archive/2014/06/07/how-to-debug-your-code-with-mvc-fresh-source-code.aspx
Before each version of MVC launch, the contribute team always strong name each MVC related assembly by a specified keyfile 35MSSharedLib1024.snk which is located in tools folder to prevent assembly tamper. But the snk file that you get doesn't contain private key, that you can only delay signed all assemblies if you compile directly. Unfortunately, delay signed assembly doesn't support debug feature.
简单的翻译一下:
因为我们下载到的 MVC 源码是经过签名的,源码里提供的密钥不包含私钥,只能是延迟签名。很不幸,延迟答名是不能DEBUG 的。
(这方面我没有经验,不懂)
按照博文的做法,将相关的项目都去掉了签名:
System.Web.Helpers
System.Web.Mvc
System.Web.Razor
System.web.WebPages
等
然后将 System.Web.WebPages 下的
AssemblyInfo.cs (在 Properties 下) 中的 InternalsVisibleTo 参数换成不带版本号的。
[assembly: InternalsVisibleTo("System.Web.Mvc")]
[assembly: InternalsVisibleTo("System.Web.Helpers")]
编译,修改测试项目的的 web.config ,将 System.Web.Mvc 的版本改成编译的版本号,删除原来的相关引用,添加为刚刚编译过的相关DLL
加断点,运行,调试进去了。
--------------分隔线-------------------
上面都是废话。
调了一圈,发现在 ModelMetadata.cs 第 382 行:
ModelMetadata propertyMetadata = viewData.ModelMetadata.Properties.Where(p => p.PropertyName == expression).FirstOrDefault();
匹配不到 property, propertyMetadata 的结果是 null.
属性明明是有的,就死活就是没有属性的 Metadata.
当快速监视 viewData.ModelMetadata 后,这个值又有了,说明问题出在这个 viewData.ModelMetadata 上。
在 ViewDataDictionary.cs ,定义的是 ViewDataDictionary , 第82行,
if (_modelMetadata == null && _model != null)
_model 无疑是传到视图里的 model
当 _modelMetadata = null 且 _model 不为 null 的时候,会去取模型的Metadata ,这就解释了为什么当传 Model 到视图的时候,会输出验证属性了。
当是不传 Model 到视图,就返回 null 了。
进入 ViewDataDictionaryOfModel.cs
定义的是 ViewDataDictionary<TModel> ,继承自 ViewDataDictionary
在 第35行,取父类的 ModelMetadata, 因为返回的是 null, 又去按 模型(Model) 的类型去取 Metadata.
接上面的自定义视图,TextBlock.cshtml
@model object
...
...
指定的模型类型是 object , 这是因为它是 Shared 的,不确定模型的确切类型,所以我用了 object.
好了,问题来了,挖掘机哪家强?按 object 去取 Metadata 当然是取不到的!
转了一转,又把我逼到当初处理这个东西的原点上。
想到快速监视后,是可以取到想要的结果的,我把上面的扩展方法改加一句:
。。。
var vctx = new ViewContext(ctx, result.View, helper.ViewData, helper.ViewContext.TempData, writer);
vctx.ViewData.ModelMetadata = helper.ViewData.ModelMetadata;//////必须的,调了很长时间,定位问题在这个 ModelMetadata 上
。。。
运行,通过!
MVC 用扩展方法执行自定义视图,替代 UIHint的更多相关文章
- MVC页面扩展方法 单例模式
MVC页面扩展方法 单例模式 /// <summary> /// 创建一个Config内容对象 /// </summary> ...
- MVC ---- 如何扩展方法
先定义一个扩展类: public static class StringExtend { //扩展一个string的方法 public static bool IsNullOrEmpty(this s ...
- Asp.net MVC 控制器扩展方法实现jsonp
项目需要,在使用KendoUI,又涉及到jsonp数据格式的处理,网上看到这样一种实现方法,在此小记一下(其实是因为公司里只能上博客园等少数网站,怕自己忘了,好查看一下,哈哈哈) 1. 新建控制器扩展 ...
- ASP.NET MVC扩展自定义视图引擎支持多模板&动态换肤skins机制
ASP.NET mvc的razor视图引擎是一个非常好的.NET MVC框架内置的视图引擎.一般情况我们使用.NET MVC框架为我们提供的这个Razor视图引擎就足够了.但是有时我们想在我们的项目支 ...
- ASP.NET MVC 扩展自定义视图引擎支持多模板&动态换肤skins机制
ASP.NET mvc的razor视图引擎是一个非常好的.NET MVC 框架内置的视图引擎.一般情况我们使用.NET MVC框架为我们提供的这个Razor视图引擎就足够了.但是有时我们想在我们的 ...
- ASP.NET MVC之持久化TempData及扩展方法(十三)
前言 之前在开始该系列之前我们就讲述了在MVC中从控制器到视图传递数据的四种方式,但是还是存在一点问题,本节就这个问题进行讲述同时进行一些练习来看看MVC中的扩展方法. 话题 废话不必多说,我们直接进 ...
- SpringMVC:自定义视图及其执行过程
一:自定义视图 1.自定义一个实现View接口的类,添加@Component注解,将其放入SpringIOC容器 package com.zzj.view; import java.io.PrintW ...
- Unity中自定义扩展方法
问题背景 在使用unity开发过程中,通常会遇到一种情况,比如说给物体重新赋值坐标的问题, Transfrom tran: ,pos_y=,pos_z=; tran.position=new Vect ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
随机推荐
- 关于TCP/IP协议栈(转)
1. TCP/IP协议栈 与OSI参考模型不同,TCP/IP协议栈共有4层,其中网络接口层对应OSI中的物理层和数据链路层,应用层对应OSI中的应用层.表示层和会话层. 在网络接口层的主要协议有:AR ...
- 彩色图像--色彩空间 YIQ 、YUV 、YCbCr 、YC1C2 和I1I2I3
学习DIP文章64天 转载请注明文章出处:http://blog.csdn.net/tonyshengtan .出于尊重文章作者的劳动,转载请标明出处.文章代码已托管,欢迎共同开发:https://g ...
- Angularjs里面跨作用域
Angularjs里面跨作用域的实战! 好久没有来写博客了,最近一直在用Google的AngularJS,后面我自己简称AngularJS就叫AJ吧! 学习AngularJS一路也是深坑颇多啊-- ...
- DFS PKU 1562
简单地DFS Oil Deposits Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 12801 Accepted: 6 ...
- 14、Cocos2dx 3.0三,找一个小游戏开发Scene and Layer:游戏梦想
发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30474393 Scene :场景 了解了Director ...
- SQL Server---触发
今天的第一次SQL Server触发感觉很方便,本文将向您介绍一个简单的SQL Server触发器和简单的使用. 我将确定其.原理.使用细节都是关于. 定义 触发器(trigger)是个特殊的存储过程 ...
- 承诺c指针 (1)指针是地址
(1)是地址 首先明白一个观点:指针就是地址.这是理解指针的起始一步. 直观感受下.变量的地址 int main() { int foo; int *foo_p; foo = 5; foo_p = & ...
- 关于system()的一些用法
C语库函数 函数名: system 功 能: 发出一个DOS命令 用 法: int system(char *command); 它包含头文件<stdlib.h> system ...
- js 正则学习小记之左最长规则
原文:js 正则学习小记之左最长规则 昨天我在判断正则引擎用到的方法是用 /nfa|nfa not/ 去匹配 "nfa not",得到的结果是 'nfa'.其实我们的本意是想得到整 ...
- 移植X264成功
http://blog.csdn.net/mashang123456789/article/details/8673426 http://blog.csdn.net/b5w2p0/article/de ...