獨立使用Asp.net Core 的razor模板 (一):Razor引擎的一些細節
由於最近需要寫一些介面稍微好看點的Winform程式,如果用原生控制元件,,想要達到好看的程度,需要花費比較大的功夫,因為之前使用過CefSharp,因此發覺如果是使用CEF+Html的方式,介面可以相對容易做的精緻一點(其實就是設計完之後,找個前端人員切切圖),但是,使用CEF+Html有個弊端就是,正常的軟體,Header跟Footer大體是通用的,包括一些通用的js/css的引用以及選單欄等等,,如果直接用html,有個問題就在於,,每個介面都要複製一遍,如果萬一發生修改,每個頁面又要來一次,或許都這裡有朋友會說:"那可以使用vue或者ng的模板啊",,實際情況是,,會用的人不多,但是會用jq的人大把.
由於伺服器端有Razor模板,可以很方便的使用Layout以及各種自己封裝的View,但實際情況下,如果單獨把Razor拿出來,實際上是隻有將模板string+model解析成新的string的功能而已,因此,想要獨立的使用Razor就需要為獨立的Razor引擎補充一些功能,
首先需要補充的就是Layout功能
開始動工之前,我們先來了解一下一些功能對應到Razor中,是怎麼個實現方式的:
1.先來看一段簡單的cshtml檔案以及生成後的類:
_Layout.cshtml
1 <html> 2 <head> 3<title></title> 4 </head> 5 <body> 6 7 8 @RenderBody() 9 10 @RenderSection("test",false) 11 12 </body> 13 </html> View Code
Index.cshtml
1 @{ 2Layout = "_Layout.cshtml"; 3 } 4 5 <p>sdfsdfsdfs</p> 6 7 8 @section test{ 9<p>ddddddddd</p> 10 } View Code
Index.cshtml生成後的程式碼:
1 #pragma warning disable 1591 2 namespace TEst 3 { 4 #line hidden 5using System; 6using System.Threading.Tasks; 7public class TextFile1 : WindowsFormsApp2.RazorViewBase<WindowsFormsApp2.Model> 8{ 9 10 #pragma warning disable 1998 11public async override global::System.Threading.Tasks.Task ExecuteAsync() 12{ 13WriteLiteral("\r\n\r\n"); 14WriteLiteral("\r\n"); 15 #line 5 "TextFile1.cshtml" 16 17Layout = "sdfsdfsdfsf"; 18 19 #line default 20 #line hidden 21WriteLiteral("\r\n<html>\r\n<head>\r\n<title></title>\r\n</head>\r\n<body>\r\n"); 22DefineSection("ui", async () => 23{ 24WriteLiteral("\r\n"); 25 #line 15 "TextFile1.cshtml" 26Write(Model.A1); 27 28 #line default 29 #line hidden 30WriteLiteral(";\r\n"); 31 #line 16 "TextFile1.cshtml" 32Write(Model.A1?.StartsWith("sfdsfdf")); 33 34 #line default 35 #line hidden 36WriteLiteral("\r\n<p></p>\r\n"); 37} 38); 39WriteLiteral("</body>\r\n</html>"); 40} 41 #pragma warning restore 1998 42} 43 } 44 #pragma warning restore 1591 View Code
1.關於基類,Razor引擎可以設定本次生成的類的基類,並且,要求基類中需要實現幾個函式,已供生成的子類呼叫
2.@section : 如果使用section關鍵字,編譯後,其實是呼叫基類的DefineSection(string name, Func<Task> act)函式,
如:在Layout 中,使用 Html.RenderSesction 函式輸出
那麼在引用該Layout的頁面中,如Index.csthml中,使用
1 @section header{ 2sdfsdfsdfsdfs 3//TODO:其他需要輸出在頭部的標籤 4 }
對應到實際生成的程式碼,其實是這樣的
1 DefineSection("header", async () => 2{ 3WriteLiteral("\r\n"); 4 #line 15 "TextFile1.cshtml" 5Write(Model.A1); 6 7 #line default 8 #line hidden 9WriteLiteral(";\r\n"); 10 #line 16 "TextFile1.cshtml" 11Write(Model.A1?.StartsWith("sfdsfdf")); 12 13 #line default 14 #line hidden 15WriteLiteral("\r\n<p></p>\r\n"); 16} 17)
由生成的程式碼可以看到 ,@section 段的使用,需要基類實現 DefineSection(string name, Func<Task> act) 函式,並且將傳入的函式存起來,等待Html.RenderSesction 觸發時呼叫
3.RenderBody,該函式其實是直接把Index.cshtml中,非@section的部分直接輸出,由ExecuteAsync函式開始,所有的WriteLiteral的結果總和,因為@section部分已經是通過DefineSection定義了,所以直接輸出其他結果並不會干擾到
4.WriteLiteral和Write: WriteLiteral 直接輸原始資料,Write除非是輸出HtmlString,否則需要轉碼
5.ExecuteAsync函式:Razor其實上是把cshtml轉成對ExecuteAsync函式的內容
6.VS 的IDE支援,,由於.net core 3.0還未出正式版.所以建立的專案為.net 4.5的,而引用的又是asp.net core 的Razor,所以在IDE支援上會有一點點的小區別:
因此為了省的IDE報太多的錯誤,需要在基類中,新增幾個用於糊弄IDE的函式和屬性:
public HttpContextFake Context { set; get; }//返回自己模擬的一個HttpContext的類, protected virtual void DefineSection(string name, Action act)//函式簽名略微不同 public virtual void Execute() //IDE認的就是這個函式,不存在會報錯,但沒有實際用途 [Browsable(false),Obsolete] public class HttpContextFake { public System.Web.HttpApplication ApplicationInstance { get; } }
順帶附上Razor+NaneUI的專案的地址: https://gitee.com/kugar/Kugar.UI.RazorUI
以上是Razor一些小的細節,,下篇文章就開始來說怎麼建立一個支援Layout的獨立Razor了