首先說些題外話,很久沒有寫博客了,空間里面的大部分文章還是11年寫的。那時候剛畢業就來到這家公司,參與到一個Asp.net MVC的項目開發中,這個項目是一個全新的項目,連項目開發框架都沒有,虧得領導的信任,讓我研究一個MVC開發框架。那時候的我就像打了雞血一樣斗志高昂,努力奮斗了一個月后終於搭建了一個比較粗糙的Asp.net MVC+JQuery+EF4.0+Oracle的開發框架,不得不說MVC和Jquery Ajax簡直就是天生一對。空間中的文章大部分都是這個時候寫的。
隨后的兩年,我投入到基於WPF的邏輯圖項目的研發中,學習到了很多東西,業務上的就不說了;技術上包括:如何利用WPF的圖形渲染優勢開發一套圖形引擎、通用的撤銷回做組件設計、圖形布局算法(花了大量的時間)、鼠標工具、圖形拓撲分析聯動等。由於技術保密原因,這些東西都不能寫成博客公開,因此這兩年我一篇博客都沒寫。但是最近需要開發一個WPF瀏覽器應用程序與外部Flex程序的交互功能,查找了很多資料都沒有找到解決辦法,一般都是通過WebBrowser來中轉實現,這局限性不能滿足要求,所以最終還是采取了JS直接與WPF函數互調用的方式來實現。
言歸正傳,以下是這個問題的解決方案,希望各位不吝指教。
WPF瀏覽器應用程序(xbap)實際並不是一個標准的Web應用程序,它只不是由IE中承載的PresentationHost.exe充當宿主來解析執行,原理與ActiveX類似。JS與ActiveX交互的資料網上很多,把WPF瀏覽器應用程序封裝成ActiveX然后開發與JS的交互函數,這也可以作為我們的一種解決方案。但是我覺得這種辦法太復雜了,所以沒有采用。
“應用程序承載到 HTML 框架中后,您可以與包含 XBAP 的網頁通信。可以通過檢索 BrowserInteropHelper 的 HostScript 屬性來完成此操作。此屬性會返回一個代表該 HTML 窗口的腳本對象。然后,您可以使用常規的點語法訪問 window object(window 對象)的屬性、方法和事件。您還可以訪問腳本方法和全局變量”
上面是msdn上的一段說明,參照上面的做法:我創建了一個html頁面,然后在頁面中添加一個iframe,iframe的src屬性指向目標xbap文件。我們確實可以在WPF的頁面后台代碼中取到外面html的HostScript腳本對象,並且可以通過這個腳本對象獲取html頁面的控件屬性甚至調用它的JS函數。但是這個對象是只讀的,也就是說你別想通過這個對象給外面的Html添加一個函數、事件監聽什么的。
我們的問題卡在這里了,我們已經實現了WPF調用html頁面的JS函數,但是JS函數怎么樣才能調用到WPF的函數呢?
其實思路很簡單,我在外面的html頁面的JS部分定義一個變量wpfObj,並且定義一個設置wpfObj值的函數——SetWpfObj(wpfobj);然后在wpf頁面后台構造函數中通過HostScript調用SetWpfObj函數給wpfObj復制一個C#對象,由這個對象來負責調用WPF的函數;最后JS函數執行的時候通過wpfObj調用C#對象的函數便完成了JS調用WPF函數的過程。
通過這兩種方式的結合,便完成了JS和WPF函數的互調用,而且簡單易復用,當我需要與外部交互的時候,我給外部展示這個html;當我不需要這個接口的時候,我直接展示這個xbap即可。無圖無真相,下面詳細貼出代碼設置和運行結果。
html代碼如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <meta http-equiv="X-UA-Compatible" content="IE=8"/> <!--不加上這一句,IE9貌似沒效果--> <script type="text/javascript"> // 調用WPF的JS函數 function JSInvokeWPF() { if (wpfObj == null) { alert("中間對象為空!"); } else { alert(wpfObj.MyMethod("JS調用WPF后台函數")); } } // 供WPF調用的函數 function WPFInvokeJS(parameter) { alert(parameter); } var wpfObj = null; function SetWpfObj(obj) { wpfObj = obj; } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>交互測試</title> </head> <body> <span> <button title="JS調用WPF函數" style="height: 30px; width: 100%;" onclick=" JSInvokeWPF();return false; ">JS調用WPF函數</button> </span> <iframe id="wpf" src="OPSYS.Web_Schematic.UI.xbap" Style="width: 100%;height: 540px" ></iframe> </body> </html>
WPF后台文件代碼如下:
using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using MessageBox = System.Windows.MessageBox; namespace OPSYS.Web_Schematic.UI { /// <summary> /// ShellView.xaml 的交互邏輯 /// </summary> public partial class ShellView : Page { private dynamic scriptObject = null; public ShellView() { InitializeComponent(); // Retrieve the script object. The XBAP must be hosted in a frame or // the HostScript object will be null. if (!BrowserInteropHelper.IsBrowserHosted) { MessageBox.Show("不滿足與JS調用條件"); return; } scriptObject = BrowserInteropHelper.HostScript; if(scriptObject!=null) { scriptObject.SetWpfObj(new CallbackClass()); } } private void JavaScriptInvoke_Click(object sender, RoutedEventArgs e) { if(scriptObject!=null) scriptObject.WPFInvokeJS("WPF調用JS函數"); } } //記得加上這個特性 [ComVisible(true)] public class CallbackClass { public string MyMethod(string message) { return "來自WPF的中轉函數," + message; } } }
運行結果如下:


如果您發現VS老是報JS函數不存在,請設置如下內容(請運行在IE8模式):

