亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

MVC 用擴展方法執行自定義視圖,替代 UIHint

系統 1632 0

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>
      

?

長這個樣子:

MVC 用擴展方法執行自定義視圖,替代 UIHint

?

?

--------------分隔線內是廢話,有興趣可以了解一下我的崩潰經歷-------------------

一切都按照設想的樣子,直到。。。。
有一天,同事說明明是 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

MVC 用擴展方法執行自定義視圖,替代 UIHint


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美成人性视频在线黑白配 | 奇米狠狠 | 免费一区二区三区免费视频 | 日韩一区精品视频在线看 | 精品一本久久中文字幕 | 曰本女人性配视频 | 香蕉网站男人网站 | 高清毛片免费看 | 日本不卡一区二区三区 | 久久精品亚洲一区二区 | 全部免费毛片 | 黄色一级免费网站 | 天天射天天舔 | 成人高清毛片a | 精品国产区 | 久久99国产综合色 | 亚洲精品久久久久午夜福 | 国产亚洲新品一区二区 | 久久精品伊人波多野结 | 91久久老司机福利精品网 | 亚洲国产天堂久久精品网 | 99热这里只有精品在线观看 | 久久大香香蕉国产免费网vrr | 国产亚洲精品一区999 | 亚洲一区综合 | 国产一区二区在线视频 | 国产精品乱码高清在线观看 | 韩国网站爱久久 | 亚洲午夜一级毛片 | 热热色视频 | 国产在线色视频 | 天天碰免费视频 | 狠狠久久久久久亚洲综合网 | 请看一下欧美一级毛片 | 黄黄网站 | 亚洲国产欧美视频 | 亚洲午夜视频在线 | 夜夜操com| 国产在线精品香蕉综合网一区 | 亚洲一区二区三区免费观看 | 亚洲国产成人久久精品影视 |