WPF中嵌入瀏覽器:http://www.tuicool.com/articles/y6JZBn


CefSharp初識--把網頁移到桌面的神器

在開發中我們可曾有過這樣的需求,將某個網頁嵌入到.Net應用中來,但Winform自帶的web browser不怎么理想。CefSharp可以讓我們在.Net應用中嵌入一個Chromium。它提供了WPF和Winform版的web browser 控件,能很好的渲染出HTML5效果而且和宿主程序有很強的交互能力。  git地址: https://github.com/cefsharp/CefSharp 。 

在WPF中使用

在Nugget中輸入CefSharp,找到CefSharp.WPF 並按照到工程中。

cefsharp不支持anycup,還需要設置一下目標平台為x86或x64. 具體請移步: http://www.cnblogs.com/yuefei/p/4123597.html

渲染效果

加入一個css3的動畫: 轉動的風車 。 元素結構還是很清晰,但動畫效果還是沒有瀏覽器流暢。

      

交互方法

cefsharp支持JavaScript和C#方法相互調用。首先需要注冊一個綁定對象:

  private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { var wb = new ChromiumWebBrowser { Address = @"file:///D:/VS2012/Support/Main/Portal/Presentation/Portal.Client/Resources/BindingTest.html" }; wb.RegisterJsObject("bound", new BoundObject()); WBGrid.Children.Add(wb); } 

BoundObject:

 public class BoundObject { public int MyProperty { get; set; } public string MyReadOnlyProperty { get; internal set; } public Type MyUnconvertibleProperty { get; set; } public SubBoundObject SubObject { get; set; } public ExceptionTestBoundObject ExceptionTestObject { get; set; } public uint[] MyUintArray { get { return new uint[] { 7, 8 }; } } public int[] MyIntArray { get { return new[] { 1, 2, 3, 4, 5, 6, 7, 8 }; } } public Array MyArray { get { return new short[] { 1, 2, 3 }; } } public byte[] MyBytes { get { return new byte[] { 3, 4, 5 }; } } public BoundObject() { MyProperty = 42; MyReadOnlyProperty = "I'm immutable!"; IgnoredProperty = "I am an Ignored Property"; MyUnconvertibleProperty = GetType(); SubObject = new SubBoundObject(); ExceptionTestObject = new ExceptionTestBoundObject(); } public void TestCallback(IJavascriptCallback javascriptCallback) { const int taskDelay = 1500; Task.Run(async () => { await Task.Delay(taskDelay); await javascriptCallback.ExecuteAsync("This callback from C# was delayed " + taskDelay + "ms"); }); } public int EchoMyProperty() { return MyProperty; } public string Repeat(string str, int n) { string result = String.Empty; for (int i = 0; i < n; i++) { result += str; } return result; } public string EchoParamOrDefault(string param = "This is the default value") { return param; } public void EchoVoid() { } public Boolean EchoBoolean(Boolean arg0) { return arg0; } public Boolean? EchoNullableBoolean(Boolean? arg0) { return arg0; } public SByte EchoSByte(SByte arg0) { return arg0; } public SByte? EchoNullableSByte(SByte? arg0) { return arg0; } public Int16 EchoInt16(Int16 arg0) { return arg0; } public Int16? EchoNullableInt16(Int16? arg0) { return arg0; } public Int32 EchoInt32(Int32 arg0) { return arg0; } public Int32? EchoNullableInt32(Int32? arg0) { return arg0; } public Int64 EchoInt64(Int64 arg0) { return arg0; } public Int64? EchoNullableInt64(Int64? arg0) { return arg0; } public Byte EchoByte(Byte arg0) { return arg0; } public Byte? EchoNullableByte(Byte? arg0) { return arg0; } public UInt16 EchoUInt16(UInt16 arg0) { return arg0; } public UInt16? EchoNullableUInt16(UInt16? arg0) { return arg0; } public UInt32 EchoUInt32(UInt32 arg0) { return arg0; } public UInt32? EchoNullableUInt32(UInt32? arg0) { return arg0; } public UInt64 EchoUInt64(UInt64 arg0) { return arg0; } public UInt64? EchoNullableUInt64(UInt64? arg0) { return arg0; } public Single EchoSingle(Single arg0) { return arg0; } public Single? EchoNullableSingle(Single? arg0) { return arg0; } public Double EchoDouble(Double arg0) { return arg0; } public Double? EchoNullableDouble(Double? arg0) { return arg0; } public Char EchoChar(Char arg0) { return arg0; } public Char? EchoNullableChar(Char? arg0) { return arg0; } public DateTime EchoDateTime(DateTime arg0) { return arg0; } public DateTime? EchoNullableDateTime(DateTime? arg0) { return arg0; } public Decimal EchoDecimal(Decimal arg0) { return arg0; } public Decimal? EchoNullableDecimal(Decimal? arg0) { return arg0; } public String EchoString(String arg0) { return arg0; } // TODO: This will currently not work, as it causes a collision w/ the EchoString() method. We need to find a way around that I guess. //public String echoString(String arg) //{ // return "Lowercase echo: " + arg; //} public String lowercaseMethod() { return "lowercase"; } public string ReturnJsonEmployeeList() { return "{\"employees\":[{\"firstName\":\"John\", \"lastName\":\"Doe\"},{\"firstName\":\"Anna\", \"lastName\":\"Smith\"},{\"firstName\":\"Peter\", \"lastName\":\"Jones\"}]}"; } [JavascriptIgnore] public string IgnoredProperty { get; set; } [JavascriptIgnore] public string IgnoredMethod() { return "I am an Ignored Method"; } public string ComplexParamObject(object param) { if (param == null) { return "param is null"; } return "The param type is:" + param.GetType(); } public SubBoundObject GetSubObject() { return SubObject; } } View Code

JavaScript調用C#方法並執行回調函數:

  <p> Javscript Callback Test <br /> <script type="text/javascript"> function callback(s) { var result = document.getElementById('cbresult'); result.innerText += "Callback: " + s+ "" + Date(); } function testCallback() { bound.testCallback(callback); var result = document.getElementById('cbresult'); result.innerText = "The function has returned: " + Date() + "\n"; } </script> <button onclick="testCallback()">Test Callback</button> <br /> <span id="cbresult"></span> </p> 

這里的bound就是我們注冊的C#對象。其中包含一個TestCallback的方法。調用的時候不區分大小寫。

  public void TestCallback(IJavascriptCallback javascriptCallback) { const int taskDelay = 1500; Task.Run(async () => { await Task.Delay(taskDelay); using (javascriptCallback) { await javascriptCallback.ExecuteAsync("This callback from C# was delayed " + taskDelay + "ms"); } }); } 

執行結果:

先執行了testCallback方法,然后執行了callback,返回了后台傳遞過來的參數。但如果再執行JavaScript之后頁面跳轉了,是不會再執行C#里面的回調函數的。

 function testDisposedCallback() { bound.testCallback(callback); //這里的方法不會執行了。 var result = document.getElementById('disposedcbresult'); result.innerText = "The function has returned: " + Date() + "\n"; window.location.assign("http://www.baidu.com"); } 

JavaScript執行有參數的C#方法

BoundObject有一個Repeat方法

   public string Repeat(string str, int n) { string result = String.Empty; for (int i = 0; i < n; i++) { result += str; } return result; } 

JavaScript調用:

 <p> Result of calling bound.repeat("hi ", 5) = <script type="text/javascript"> var result = bound.repeat("hi ", 5); document.write('"' + result + '"'); if (result === "hi hi hi hi hi ") { document.write(" SUCCESS"); } else { document.write(" FAIL!"); } </script> </p> 

執行結果:

委托C#方法

將綁定對象的方法作為參數傳遞給JavaScript方法。

  <script type="text/javascript"> function myFunction(functionParam) { return functionParam(); } document.write("委托輸出屬性結果: " + myFunction(bound.echoMyProperty)); </script> 
echoMyProperty方法:
   public int EchoMyProperty() { return MyProperty;//初始化為42 } 

返回C#對象

BoundObject含有一個子對象 SubBoundObject。通過GetObject返回。

 public SubBoundObject GetSubObject()
  {
     return SubObject; } 

SubBoundObject:

public class SubBoundObject { public string SimpleProperty { get; set; } public SubBoundObject() { SimpleProperty = "這是子對象屬性"; } public string GetMyType() { return "My Type is " + GetType(); } public string EchoSimpleProperty() { return SimpleProperty; } } View Code

JavaScript調用:

 <script type="text/javascript"> document.write("bound.getSubObject().simpleProperty result: " + bound.getSubObject().simpleProperty); </script> 

執行結果:

獲取bound對象的所有方法和屬性

        'bound的'所有方法:<br /> <ul> <script type="text/javascript"> for (var name in bound) { if (bound[name].constructor.name != 'Function') continue; document.write("<li>" + name + "</li>"); } </script> </ul> 'bound的'所有屬性:<br /> <ul> <script type="text/javascript"> for (var name in bound) { if (bound[name].constructor.name === 'Function') continue; document.write("<li>" + name + "</li>"); if (typeof bound[name] == "object" && bound[name] !== null) { //展示子對象屬性 for (var sub in bound[name]) { var type = bound[name][sub].constructor.name === 'Function' ? "Function" : "Property"; document.write("<li>" + name + "." + sub + "(" + type + ")" + "</li>"); } } } </script> </ul> 

可以在C#對象中忽略掉屬性和方法,這樣就不會顯示出來。

   [JavascriptIgnore]
   public string IgnoredProperty { get; set; } [JavascriptIgnore] public string IgnoredMethod() { return "I am an Ignored Method"; } 

整個測試頁面:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html> <head> <title>Binding Test</title> </head> <body> <p> Async Binding Test <span id="asyncresult"></span> <script type="text/javascript"> var asResult = document.getElementById('asyncresult'); function writeAsyncResult(call, end) { var p = document.createElement('p'); var br = document.createElement('br'); var br2 = document.createElement('br'); var title = document.createTextNode('Async Call: '); var callText = document.createTextNode(call); var endText = document.createTextNode(end); p.appendChild(title); p.appendChild(br); p.appendChild(callText); p.appendChild(br2); p.appendChild(endText); asResult.appendChild(p); } function asyncError() { var call = "Async call (Throw Exception): " + Date(); boundAsync.error().catch(function (e) { var end = "Error: " + e + "(" + Date() + ")"; writeAsyncResult(call, end); }); } function asyncDivOk() { var call = "Async call (Divide 16 / 2): " + Date(); boundAsync.div(16, 2).then(function (res) { var end = "Result: " + res + "(" + Date() + ")"; writeAsyncResult(call, end); }); } function asyncDivFail() { var call = "Async call (Divide 16 /0): " + Date(); boundAsync.div(16, 0).then(function (res) { var end = "Result: " + res + "(" + Date() + ")"; writeAsyncResult(call, end); }, function (e) { var end = "Error: " + e + "(" + Date() + ")"; writeAsyncResult(call, end); }); } function asyncHello() { var call = "Async call (Hello): " + Date(); boundAsync.hello('CefSharp').then(function (res) { var end = "Result: " + res + "(" + Date() + ")"; writeAsyncResult(call, end); }); } function asyncDoSomething() { var call = "Async call (Long Running Task): " + Date(); boundAsync.doSomething().then(function (res) { var end = "Result: " + res + "(" + Date() + ")"; writeAsyncResult(call, end); }); } asyncError(); asyncDivOk(); asyncDivFail(); asyncDoSomething(); </script> </p> <p> Javscript Callback Test <br /> <script type="text/javascript"> function callback(s) { var result = document.getElementById('cbresult'); result.innerText += "Callback: " + s + "" + Date(); } function testCallback() { bound.testCallback(callback); var result = document.getElementById('cbresult'); result.innerText = "The function has returned: " + Date() + "\n"; } </script> <button onclick="testCallback()">Test Callback</button> <br /> <span id="cbresult"></span> </p> <p> Disposed Javscript Callback Test (navigates to www.google.com before callback fires) <br /> <script type="text/javascript"> function disposedCallback(s) { // This callback should be disposed and should not be called window.alert("This callback should not have been called"); var result = document.getElementById('disposedcbresult'); result.innerText += "Callback: " + s + "" + Date(); } function testDisposedCallback() { bound.testCallback(callback); var result = document.getElementById('disposedcbresult'); result.innerText = "The function has returned: " + Date() + "\n"; window.location.assign("http://www.baidu.com"); } </script> <button onclick="testDisposedCallback()">Test Disposed Callback</button> <br /> <span id="disposedcbresult"></span> </p> <p> Result of calling bound.repeat("hi ", 5) = <script type="text/javascript"> var result = bound.repeat("hi ", 5); document.write('"' + result + '"'); if (result === "hi hi hi hi hi ") { document.write(" SUCCESS"); } else { document.write(" FAIL!"); } </script> </p> <p> 委托c# 方法 <br /> <script type="text/javascript"> function myFunction(functionParam) { return functionParam(); } document.write("委托輸出屬性結果: " + myFunction(bound.echoMyProperty)); </script> </p> <p> Function returning complex type <br /> <script type="text/javascript"> document.write("bound.getSubObject().simpleProperty result: " + bound.getSubObject().simpleProperty); </script> </p> <p> Stress Test <br /> <script type="text/javascript"> var stressTestCallCount = 1000; for (var i = 1; i <= stressTestCallCount; i++) { bound.repeat("hi ", 5); } document.write("Stress Test done with : " + stressTestCallCount + " call to bound.repeat(\"hi \", 5)"); </script> </p> <p> JSON Serializer Test <br /> <script type="text/javascript"> var json = bound.returnJsonEmployeeList(); var jsonObj = JSON.parse(json); document.write("Employee Count : " + jsonObj.employees.length + "<br/>"); for (var i = 0; i < jsonObj.employees.length; i++) { var employee = jsonObj.employees[i]; document.write("Employee : " + employee.firstName + " " + employee.lastName + "<br/>"); } </script> </p> 'bound的'所有方法:<br /> <ul> <script type="text/javascript"> for (var name in bound) { if (bound[name].constructor.name != 'Function') continue; document.write("<li>" + name + "</li>"); } </script> </ul> 'bound的'所有屬性:<br /> <ul> <script type="text/javascript"> for (var name in bound) { if (bound[name].constructor.name === 'Function') continue; document.write("<li>" + name + "</li>"); if (typeof bound[name] == "object" && bound[name] !== null) { //展示子對象屬性 for (var sub in bound[name]) { var type = bound[name][sub].constructor.name === 'Function' ? "Function" : "Property"; document.write("<li>" + name + "." + sub + "(" + type + ")" + "</li>"); } } } </script> </ul> </body> </html> View Code

WebGL的渲染效果

WebGL是一種3D繪圖標准,這種繪圖技術標准允許把JavaScript和OpenGL ES 2.0結合在一起,通過增加OpenGL ES 2.0的一個JavaScript綁定,WebGL可以為HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就可以借助系統顯卡來在瀏覽器里更流暢地展示3D場景和模型了,還能創建復雜的導航和數據視覺化。

測試頁面: http://webglsamples.org/aquarium/aquarium.html

這個效果還是不錯的。

小結:以上只是簡單的測試程序,CEFSharp對html5和JavaScript的支持確實不錯。后續有機會做更多分享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM