因爲最近須要寫一些界面稍微好看點的Winform程序,若是用原生控件,,想要達到好看的程度,須要花費比較大的功夫,由於以前使用過CefSharp,所以發覺若是是使用CEF+Html的方式,界面能夠相對容易作的精緻一點(其實就是設計完以後,找個前端人員切切圖),可是,使用CEF+Html有個弊端就是,正常的軟件,Header跟Footer大致是通用的,包括一些通用的js/css的引用以及菜單欄等等,,若是直接用html,有個問題就在於,,每一個界面都要複製一遍,若是萬一發生修改,每一個頁面又要來一次,或許都這裏有朋友會說:"那可使用vue或者ng的模板啊",,實際狀況是,,會用的人很少,可是會用jq的人大把.css
因爲服務器端有Razor模板,能夠很方便的使用Layout以及各類本身封裝的View,但實際狀況下,若是單獨把Razor拿出來,其實是隻有將模板string+model解析成新的string的功能而已,所以,想要獨立的使用Razor就須要爲獨立的Razor引擎補充一些功能,html
首先須要補充的就是Layout功能前端
開始動工以前,咱們先來了解一下一些功能對應到Razor中,是怎麼個實現方式的:vue
1.先來看一段簡單的cshtml文件以及生成後的類:git
_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>
Index.cshtmlasp.net
1 @{ 2 Layout = "_Layout.cshtml"; 3 } 4 5 <p>sdfsdfsdfs</p> 6 7 8 @section test{ 9 <p>ddddddddd</p> 10 }
Index.cshtml生成後的代碼:async
1 #pragma warning disable 1591 2 namespace TEst 3 { 4 #line hidden 5 using System; 6 using System.Threading.Tasks; 7 public class TextFile1 : WindowsFormsApp2.RazorViewBase<WindowsFormsApp2.Model> 8 { 9 10 #pragma warning disable 1998 11 public async override global::System.Threading.Tasks.Task ExecuteAsync() 12 { 13 WriteLiteral("\r\n\r\n"); 14 WriteLiteral("\r\n"); 15 #line 5 "TextFile1.cshtml" 16 17 Layout = "sdfsdfsdfsf"; 18 19 #line default 20 #line hidden 21 WriteLiteral("\r\n<html>\r\n<head>\r\n <title></title>\r\n</head>\r\n<body>\r\n"); 22 DefineSection("ui", async () => 23 { 24 WriteLiteral("\r\n "); 25 #line 15 "TextFile1.cshtml" 26 Write(Model.A1); 27 28 #line default 29 #line hidden 30 WriteLiteral(";\r\n "); 31 #line 16 "TextFile1.cshtml" 32 Write(Model.A1?.StartsWith("sfdsfdf")); 33 34 #line default 35 #line hidden 36 WriteLiteral("\r\n <p></p>\r\n "); 37 } 38 ); 39 WriteLiteral("</body>\r\n</html>"); 40 } 41 #pragma warning restore 1998 42 } 43 } 44 #pragma warning restore 1591
1.關於基類,Razor引擎能夠設置本次生成的類的基類,而且,要求基類中須要實現幾個函數,已供生成的子類調用ide
2.@section : 若是使用section關鍵字,編譯後,實際上是調用基類的DefineSection(string name, Func<Task> act)函數,函數
如:在Layout 中,使用 Html.RenderSesction 函數輸出
那麼在引用該Layout的頁面中,如Index.csthml中,使用
1 @section header{ 2 sdfsdfsdfsdfs 3 //TODO:其餘須要輸出在頭部的標籤 4 }
對應到實際生成的代碼,實際上是這樣的
1 DefineSection("header", async () => 2 { 3 WriteLiteral("\r\n "); 4 #line 15 "TextFile1.cshtml" 5 Write(Model.A1); 6 7 #line default 8 #line hidden 9 WriteLiteral(";\r\n "); 10 #line 16 "TextFile1.cshtml" 11 Write(Model.A1?.StartsWith("sfdsfdf")); 12 13 #line default 14 #line hidden 15 WriteLiteral("\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了