2010-09-12 38 views
31

Tôi có ứng dụng C# WinForms có điều khiển WebBrowser bên trong nó. Tôi muốn thực hiện giao tiếp hai chiều giữa biểu mẫu C# của tôi và JavaScript trong điều khiển trình duyệt web được nhúng.Gọi mã C# từ JavaScript trong Tài liệu trong WebBrowser

Tôi biết tôi có thể gọi hàm JavaScript với InvokeScript, nhưng làm cách nào tôi có thể gọi mã C# từ JavaScript trong Document? Tôi đoán nó sẽ không dễ dàng do an ninh, nhưng liệu có thể, bằng cách nào đó, dù sao đi nữa? Các hàm JavaScript này được cho là chức năng của người dùng, giống như các macro, điều đó sẽ cho WebBrowser biết chính xác những gì cần làm với sự trợ giúp của toàn bộ thư viện C# được viết bởi chính tôi. Và vì đây là một trình duyệt web, JavaScript là ngôn ngữ hoàn hảo cho các macro này vì nó được tạo ra khá nhiều để truy cập các phần tử trong một tài liệu HTML.

+0

Tại sao tôi nhận được điểm trừ cho câu hỏi này? – Juan

+0

Bạn có hỏi cách gọi phương thức phía máy chủ từ mã phía máy khách không? Bạn sẽ cần phải sử dụng một cuộc gọi AJAX cho điều đó. Cụ thể, bạn đang cố gắng thực hiện điều gì? – David

+0

Tôi đang tạo một trình duyệt web và tôi muốn đọc một tệp trong máy tính của mình từ một JavaScript có trong tài liệu html. Trên thực tế, tôi muốn gọi một loạt các phương thức trong chương trình của tôi (cùng một chương trình có chứa WebBrowser và Document), nhưng hãy nói rằng tôi chỉ muốn đọc một tệp. – Juan

Trả lời

47

Những gì bạn cần làm là đặt thuộc tính ObjectForScripting trên điều khiển trình duyệt web cho đối tượng chứa phương thức C# bạn muốn gọi từ JavaScript. Sau đó, bạn có thể truy cập đối tượng đó từ JavaScript bằng cách sử dụng window.external. Điều duy nhất cần chú ý là đối tượng phải có thuộc tính [ComVisibleAttribute(true)]. Tôi đã sử dụng thành công này trong vài năm.

Dưới đây là một trang với documenation và một ví dụ đơn giản: http://msdn.microsoft.com/en-us/library/a0746166.aspx

Dưới đây là ví dụ từ liên kết (tôi chưa thử mã này):

using System; 
using System.Windows.Forms; 
using System.Security.Permissions; 

[PermissionSet(SecurityAction.Demand, Name="FullTrust")] 
[System.Runtime.InteropServices.ComVisibleAttribute(true)] 
public class Form1 : Form 
{ 
    private WebBrowser webBrowser1 = new WebBrowser(); 
    private Button button1 = new Button(); 

    [STAThread] 
    public static void Main() 
    { 
     Application.EnableVisualStyles(); 
     Application.Run(new Form1()); 
    } 

    public Form1() 
    { 
     button1.Text = "call script code from client code"; 
     button1.Dock = DockStyle.Top; 
     button1.Click += new EventHandler(button1_Click); 
     webBrowser1.Dock = DockStyle.Fill; 
     Controls.Add(webBrowser1); 
     Controls.Add(button1); 
     Load += new EventHandler(Form1_Load); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     webBrowser1.AllowWebBrowserDrop = false; 
     webBrowser1.IsWebBrowserContextMenuEnabled = false; 
     webBrowser1.WebBrowserShortcutsEnabled = false; 
     webBrowser1.ObjectForScripting = this; 
     // Uncomment the following line when you are finished debugging. 
     //webBrowser1.ScriptErrorsSuppressed = true; 

     webBrowser1.DocumentText = 
      "<html><head><script>" + 
      "function test(message) { alert(message); }" + 
      "</script></head><body><button " + 
      "onclick=\"window.external.Test('called from script code')\">" + 
      "call client code from script code</button>" + 
      "</body></html>"; 
    } 

    public void Test(String message) 
    { 
     MessageBox.Show(message, "client code"); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     webBrowser1.Document.InvokeScript("test", 
      new String[] { "called from client code" }); 
    } 
} 
+0

BTW, tôi cũng đã sử dụng phương pháp này để liên lạc giữa C# trong mã Silverlight trong điều khiển web nhúng vào biểu mẫu C# lưu trữ nó. – Gabe

+3

Bạn cũng nên chú ý rằng phương thức C# được gọi từ JavaScript được khai báo là công khai. –

+0

Bạn không nên sử dụng đối tượng Form hoặc bất kỳ đối tượng hiện có nào khác nhưng một số đối tượng chuyên dụng cho công việc đó, bởi vì nếu không bạn có thể vô tình cho phép javascript thực thi một số phương thức khác trên đối tượng. – NineBerry

4

lẽ Bạn đang tìm kiếm http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.objectforscripting.aspx

WebBrowser.ObjectForScripting cho phép bạn hiển thị một thể hiện của một lớp học [ComVisible] .net thành mã javascript chạy bên trong trình duyệt web được lưu trữ. Nó được tiếp xúc trong javascript như window.external

Tuyệt vời bài báo từ Microsoft: How to: Implement Two-Way Communication Between DHTML Code and Client Application Code

+0

Bài viết đó rất tuyệt vời. Ví dụ, tôi thực sự muốn biết liệu tôi có thể trả về một giá trị vào mã javascript hay không, nhưng điều đó không rõ ràng. –

1

Dưới đây là số phương pháp mở rộng mà tôi đã viết để giúp đỡ với hai cách giao tiếp/gọi giữa các đối tượng WebBrowser và C# mã:

using System; 
using System.Threading; 
using FluentSharp.Web35; 
using FluentSharp.WinForms; 
using FluentSharp.CoreLib; 
using FluentSharp.CoreLib.API; 

namespace FluentSharp.Watin 
{ 
    public static class WatiN_IE_ExtensionMethods_Javascript 
    { 

     public static object invokeScript(this WatiN_IE ie, string functionName) 
     { 
      return ie.invokeScript(functionName, null); 
     } 

     public static object invokeScript(this WatiN_IE ie, string functionName, params object[] parameters) 
     { 
      //"[WatiN_IE] invokeScript '{0}' with parameters:{1}".info(functionName ,parameters.size()); 
      return ie.invokeScript(true, functionName, parameters); 
     } 

     public static object invokeScript(this WatiN_IE ie, bool waitForExecutionComplete, string functionName, params object[] parameters) 
     { 
      var sync = new AutoResetEvent(false); 
      object responseValue = null; 
      ie.WebBrowser.invokeOnThread(
       ()=>{ 
         var document = ie.WebBrowser.Document; 
         if (parameters.isNull()) 
          responseValue = document.InvokeScript(functionName); 
         else 
          responseValue = document.InvokeScript(functionName, parameters); 
         sync.Set(); 
       }); 
      if (waitForExecutionComplete) 
       sync.WaitOne(); 
      return responseValue; 
     } 

     public static object invokeEval(this WatiN_IE ie, string evalScript) 
     { 
      var evalParam = "(function() { " + evalScript + "})();"; 
      //"[WatiN_IE] invokeEval evalParam: {0}".debug(evalParam); 
      return ie.invokeScript("eval", evalParam); 
     } 
     public static WatiN_IE.ToCSharp injectJavascriptFunctions(this WatiN_IE ie) 
     { 
      return ie.injectJavascriptFunctions(false); 
     } 

     public static WatiN_IE.ToCSharp injectJavascriptFunctions(this WatiN_IE ie, bool resetHooks) 
     { 
      if (ie.WebBrowser.isNull()) 
       "in InjectJavascriptFunctions, ie.WebBrowser was null".error(); 
      else 
      { 
       if (ie.WebBrowser.ObjectForScripting.isNull() || resetHooks) 
       { 
        ie.WebBrowser.ObjectForScripting = new WatiN_IE.ToCSharp(); 

        "Injecting Javascript Hooks * Functions for page: {0}".debug(ie.url()); 
        ie.eval("var o2Log = function(message) { window.external.write(message) };"); 
        ie.invokeScript("o2Log","Test from Javascript (via toCSharp(message))"); 
        ie.eval("$o2 = window.external"); 
        "Injection complete (use o2Log(...) or $o2.write(...) to talk back to O2".info(); 
        return (ie.WebBrowser.ObjectForScripting as WatiN_IE.ToCSharp); 
       } 
       else 
       { 
        if((ie.WebBrowser.ObjectForScripting is WatiN_IE.ToCSharp)) 
         return (ie.WebBrowser.ObjectForScripting as WatiN_IE.ToCSharp); 
        else 
         "in WatiN_IE injectJavascriptFunctions, unexpected type in ie.WebBrowser.ObjectForScripting: {0}".error(ie.WebBrowser.ObjectForScripting.typeName());     
       } 

      } 
      return null; 
     } 

     public static object downloadAndExecJavascriptFile(this WatiN_IE ie, string url) 
     { 
      "[WatiN_IE] downloadAndExecJavascriptFile: {0}".info(url); 
      var javascriptCode = url.uri().getHtml(); 
      if (javascriptCode.valid()) 
       ie.eval(javascriptCode); 
      return ie; 
     } 

     public static WatiN_IE injectJavascriptFunctions_onNavigate(this WatiN_IE ie) 
     { 

      ie.onNavigate((url)=> ie.injectJavascriptFunctions()); 
      return ie; 
     } 

     public static WatiN_IE setOnAjaxLog(this WatiN_IE ie, Action<string, string,string,string> onAjaxLog) 
     { 
      (ie.WebBrowser.ObjectForScripting as WatiN_IE.ToCSharp).OnAjaxLog = onAjaxLog; 
      return ie; 
     } 

     public static WatiN_IE eval_ASync(this WatiN_IE ie, string script) 
     { 
      O2Thread.mtaThread(()=> ie.eval(script)); 
      return ie; 
     } 

     public static WatiN_IE eval(this WatiN_IE ie, string script) 
     { 
      return ie.eval(script, true); 
     } 

     public static WatiN_IE eval(this WatiN_IE ie, string script, bool waitForExecutionComplete) 
     { 
      var executionThread = O2Thread.staThread(()=> ie.IE.RunScript(script));   
      if (waitForExecutionComplete) 
       executionThread.Join(); 
      return ie; 
     } 

     public static WatiN_IE alert(this WatiN_IE ie, string alertScript) 
     { 
      return ie.eval("alert({0});".format(alertScript)); 
     } 

     public static object getJsObject(this WatiN_IE ie) 
     { 
      var toCSharpProxy = ie.injectJavascriptFunctions(); 
      if (toCSharpProxy.notNull()) 
       return toCSharpProxy.getJsObject(); 
      return null;   
     } 

     public static T getJsObject<T>(this WatiN_IE ie, string jsCommand) 
     { 
      var jsObject = ie.getJsObject(jsCommand); 
      if (jsObject is T) 
       return (T)jsObject; 
      return default(T); 
     } 

     public static bool doesJsObjectExists(this WatiN_IE ie, string jsCommand) 
     { 
      var toCSharpProxy = ie.injectJavascriptFunctions(); 
      if (toCSharpProxy.notNull()) 
      { 
       var command = "window.external.setJsObject(typeof({0}))".format(jsCommand); 
       ie.invokeEval(command); 
       ie.remapInternalJsObject();    
       return toCSharpProxy.getJsObject().str()!="undefined"; 
      } 
      return false; 
     } 

     public static object getJsVariable(this WatiN_IE ie, string jsCommand) 
     { 
      return ie.getJsObject(jsCommand); 
     } 

     public static object getJsObject(this WatiN_IE ie, string jsCommand) 
     { 
      var toCSharpProxy = ie.injectJavascriptFunctions(); 
      if (toCSharpProxy.notNull()) 
      { 
       var command = "window.external.setJsObject({0})".format(jsCommand); 
       ie.invokeEval(command); 
       ie.remapInternalJsObject();    
       return toCSharpProxy.getJsObject(); 
      } 
      return null; 
     }    

     public static WatiN_IE remapInternalJsObject(this WatiN_IE ie) 
     {  
      //"setting JS _jsObject variable to getJsObject()".info(); 
      ie.invokeEval("_jsObject = window.external.getJsObject()"); // creates JS variable to be used from JS 
      return ie; 
     } 

     public static WatiN_IE setJsObject(this WatiN_IE ie, object jsObject) 
     { 
      var toCSharpProxy = ie.injectJavascriptFunctions(); 
      if (toCSharpProxy.notNull())    
      { 
       toCSharpProxy.setJsObject(jsObject); 
       ie.remapInternalJsObject(); 
      } 
      return ie; 
     } 

     public static object waitForJsObject(this WatiN_IE watinIe) 
     { 
      return watinIe.waitForJsObject(500, 20); 
     } 

     public static object waitForJsObject(this WatiN_IE watinIe, int sleepMiliseconds, int maxSleepTimes) 
     {     
      "[WatiN_IE][waitForJsObject] trying to find jsObject for {0} x {1} ms".info(maxSleepTimes, sleepMiliseconds); 
      watinIe.setJsObject(null); 
      for(var i = 0; i < maxSleepTimes ; i++) 
      { 
       var jsObject = watinIe.getJsObject(); 
       if(jsObject.notNull()) 
       { 
        "[watinIe][waitForJsObject] got value: {0} (n tries)".info(jsObject, i); 
        return jsObject; 
       } 

       watinIe.sleep(500, false); 
      } 
      "[WatiN_IE][waitForJsObject] didn't find jsObject after {0} sleeps of {1} ms".error(maxSleepTimes, sleepMiliseconds); 
      return null; 
     } 

     public static object waitForJsVariable(this WatiN_IE watinIe, string jsCommand) 
     { 
      return watinIe.waitForJsVariable(jsCommand, 500, WatiN_IE_ExtensionMethods.WAITFORJSVARIABLE_MAXSLEEPTIMES); 
     } 

     public static object waitForJsVariable(this WatiN_IE watinIe, string jsCommand, int sleepMiliseconds, int maxSleepTimes) 
     { 
      "[WatiN_IE][waitForJsVariable] trying to find jsObject called '{0}' for {1} x {2} ms".info(jsCommand, maxSleepTimes, sleepMiliseconds);   
      watinIe.setJsObject(null); 
      for(var i = 0; i < maxSleepTimes ; i++) 
      { 
       if (watinIe.doesJsObjectExists(jsCommand)) 
       { 
        var jsObject = watinIe.getJsObject(jsCommand); 
        "[watinIe][waitForJsVariable] got value: {0} ({1} tries)".info(jsObject, i); 
        return jsObject; 
       }     
       watinIe.sleep(500, false); 
      } 
      "[WatiN_IE][waitForJsVariable] didn't find jsObject called '{0}' after {1} sleeps of {2} ms".error(jsCommand, maxSleepTimes, sleepMiliseconds); 
      return null; 
     } 

     public static WatiN_IE deleteJsVariable(this WatiN_IE watinIe, string jsVariable) 
     { 
      var evalString = "try { delete " + jsVariable + " } catch(exception) { }"; 
      watinIe.eval(evalString); 
      return watinIe; 
     } 


    } 
} 
Các vấn đề liên quan