您需要將C#的回調委託傳遞給JavaScript。這個委託將在JavaScript端的異步操作完成時被調用。完成的信號自然是特定於腳本邏輯的。它可能是一個AJAX,DOM或Silverlight事件。
由於@StephenCleary在評論中指出的那樣,你可以進一步使用的腳本側Deferred對象方便地包裹在JavaScript中完成事件,以類似的方式,你使用一個Task
(或TaskCompletionSource
)包裹在事件C#。您仍然需要傳遞一個C#回調函數,您可以在延遲解析時調用它。
在下面的代碼中,我使用JavaScript定時器作爲異步操作的示例。爲了簡單起見,代碼不使用延遲。
C#:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
namespace WpfWebBrowser
{
public partial class MainWindow : Window
{
public MainWindow()
{
SetBrowserFeatureControl();
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
async void MainWindow_Loaded(object sender, RoutedEventArgs eventArg)
{
// navigate the browser
System.Windows.Navigation.LoadCompletedEventHandler loadedHandler = null;
var loadedTcs = new TaskCompletionSource<bool>();
loadedHandler = (s, e) =>
{
this.webBrowser.LoadCompleted -= loadedHandler;
loadedTcs.SetResult(true);
};
this.webBrowser.LoadCompleted += loadedHandler;
this.webBrowser.Navigate("http://localhost:81/callback.html");
await loadedTcs.Task;
// call the script method "scriptFuncAsync"
var asyncScriptTcs = new TaskCompletionSource<object>();
var oncompleted = new ScriptEventHandler((ref object returnResult, object[] args) =>
{
// we are here when the script has called us back
asyncScriptTcs.SetResult(args[0]);
});
this.webBrowser.InvokeScript("scriptFuncAsync", oncompleted);
await asyncScriptTcs.Task;
// show the result of the asyc call
dynamic result = asyncScriptTcs.Task.Result;
MessageBox.Show(result.outcome.ToString());
}
/// <summary>
/// ScriptEventHandler - an adaptor to call C# back from JavaScript or DOM event handlers
/// </summary>
[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch)]
public class ScriptEventHandler
{
[ComVisible(false)]
public delegate void Callback(ref object returnResult, params object[] args);
[ComVisible(false)]
private Callback _callback;
[DispId(0)]
public object Method(params object[] args)
{
var returnResult = Type.Missing; // Type.Missing is "undefined" in JavaScript
_callback(ref returnResult, args);
return returnResult;
}
public ScriptEventHandler(Callback callback)
{
_callback = callback;
}
}
/// <summary>
/// WebBrowser version control to enable HTML5
/// http://msdn.microsoft.com/en-us/library/ee330730(v=vs.85).aspx#browser_emulation
/// </summary>
void SetBrowserFeatureControl()
{
// http://msdn.microsoft.com/en-us/library/ee330720(v=vs.85).aspx
// FeatureControl settings are per-process
var fileName = System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
// make the control is not running inside Visual Studio Designer
if (String.Compare(fileName, "devenv.exe", true) == 0 || String.Compare(fileName, "XDesProc.exe", true) == 0)
return;
// Webpages containing standards-based !DOCTYPE directives are displayed in IE9/IE10 Standards mode.
SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", fileName, 9000);
}
void SetBrowserFeatureControlKey(string feature, string appName, uint value)
{
using (var key = Registry.CurrentUser.CreateSubKey(
String.Concat(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\", feature),
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(appName, (UInt32)value, RegistryValueKind.DWord);
}
}
}
}
XAML:
<Window x:Class="WpfWebBrowser.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<WebBrowser Name="webBrowser"/>
</Window>
http://localhost:81/callback.html
:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script>
window.scriptFuncAsync = function (oncompleted) {
// do something async, use timer
setTimeout(function() {
info.innerHTML = "Async operation completed.";
oncompleted({ outcome: 42 });
}, 2000);
}
</script>
</head>
<body>
<span>Async Script Test Page</span><br>
<span id="info"></span>
</body>
</html>
[更新]:
下面是一個更復雜的腳本,說明如何使用jQuery的Deferred/Promise pattern。它執行兩個異步操作(並行),然後在兩個操作完成時調用C#(所以它們的promise已經解決)。
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script>
window.scriptFuncAsync = function (oncompleted) {
// do something async, use timer
var promise1 = (function() {
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve("Async timeout completed.");
}, 3000);
return deferred.promise();
})();
promise1.done(function() {
output("promise1 resolved.");
});
var promise2 = $.ajax({ url: "http://www.html5rocks.com/en/resources" });
promise2.done(function() {
output("promise2 resolved.");
});
$.when(promise1, promise2).then(function(result1, result2) {
output("result1: " + result1);
output("result2: " + result2.toString().substr(0, 100) + "...");
oncompleted({ outcome: 42 });
});
function output(str) {
if (!info.firstChild)
info.appendChild(document.createTextNode(""));
info.firstChild.data += str + "\n";
}
};
</script>
</head>
<body>
<span>Async Script Test Page</span><br>
<pre id="info"></pre>
</body>
</html>
JavaScript沒有像'async'或'Task'這樣的內置任何東西。我建議嘗試使用Promise或Deferred(例如Q),並從'ScriptableMember'返回。 –
這對我來說更新,我該如何做到這一點? –
@JérémyJaniszewski,如果我的回答沒有幫助,請考慮留言。 – Noseratio