WPF瀏覽器應用程序與JS的互調用(不用WebBrowser)


首先說些題外話,很久沒有寫博客了,空間里面的大部分文章還是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 的網頁通信。可以通過檢索 BrowserInteropHelperHostScript 屬性來完成此操作。此屬性會返回一個代表該 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>
View Code

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;
        }
    }
}
View Code

運行結果如下:

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

 

 

 

 

 

 

 

 

 


免責聲明!

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



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