C# Wpf:webbrowser在javascript中同步调用异步silverlight方法
我有一个问题,在我目前正在进行的项目中,我有一个WPF应用程序。 在这个WPF应用程序中,我们有一个选项卡项,其中有一个WPF webbrowser 此wpf webbrowser承载一个Silverlight网站项目 为了使事情更简单,假设我有一个异步方法,如下所示:C# Wpf:webbrowser在javascript中同步调用异步silverlight方法,c#,javascript,silverlight,asynchronous,webbrowser-control,C#,Javascript,Silverlight,Asynchronous,Webbrowser Control,我有一个问题,在我目前正在进行的项目中,我有一个WPF应用程序。 在这个WPF应用程序中,我们有一个选项卡项,其中有一个WPF webbrowser 此wpf webbrowser承载一个Silverlight网站项目 为了使事情更简单,假设我有一个异步方法,如下所示: // Script to call in WPF to see if technical inspection has changed [ScriptableMember] public async Task<int>
// Script to call in WPF to see if technical inspection has changed
[ScriptableMember]
public async Task<int> VerifyModifiedTechnicalInspection()
{
var rvm = ((MainViewModel)DataContext).TechnicalInspectionViewModel;
if (rvm == null)
return -1;
// Verify if we have some technical inspection to save. If yes, show a dialog.
// Gets the dialog's result
var result = await rvm.SaveTechnicalInspection();
switch(result)
{
case DialogButton.Cancel:
return 2;
case DialogButton.No:
return 1;
case DialogButton.Yes:
return 0;
default:
return -1;
}
}
function VerifyModifiedTechnicalInspection() {
if (slControl != null) {
return slControl.Content.SLMainPage.VerifyModifiedTechnicalInspection();
}
return -1;
}
var result = (int)webBrowser.InvokeScript("VerifyModifiedRelances");
switch(result)
{
}
问题是:
更改选项卡项时,我需要按如下方式运行脚本:
// Script to call in WPF to see if technical inspection has changed
[ScriptableMember]
public async Task<int> VerifyModifiedTechnicalInspection()
{
var rvm = ((MainViewModel)DataContext).TechnicalInspectionViewModel;
if (rvm == null)
return -1;
// Verify if we have some technical inspection to save. If yes, show a dialog.
// Gets the dialog's result
var result = await rvm.SaveTechnicalInspection();
switch(result)
{
case DialogButton.Cancel:
return 2;
case DialogButton.No:
return 1;
case DialogButton.Yes:
return 0;
default:
return -1;
}
}
function VerifyModifiedTechnicalInspection() {
if (slControl != null) {
return slControl.Content.SLMainPage.VerifyModifiedTechnicalInspection();
}
return -1;
}
var result = (int)webBrowser.InvokeScript("VerifyModifiedRelances");
switch(result)
{
}
因此,这里的问题是,脚本是异步调用的,因此结果返回时没有实际值,因此tabitem会更改,我不会这样做
如何同步调用脚本?您需要将回调委托从C#传递到JavaScript。当JavaScript端的异步操作完成时,将回调此委托。完成的信号自然是特定于脚本逻辑的。它可以是AJAX、DOM或Silverlight事件 正如@StephenCleary在评论中指出的,您可以进一步使用脚本端的对象方便地用JavaScript包装完成事件,就像使用
任务
(或任务完成源代码
)用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);
}
}
}
}
<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>
<!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的使用。它执行两个异步操作(并行),然后在两个操作都完成时(因此它们的承诺已经得到解决)回调C
window.scriptFuncAsync=函数(未完成){
//做一些异步的事情,使用定时器
var promise1=(函数(){
var deferred=$.deferred();
setTimeout(函数(){
解析(“异步超时完成”);
}, 3000);
延迟返回。承诺();
})();
承诺1.完成(功能){
输出(“承诺1已解决”);
});
var promise2=$.ajax({url:http://www.html5rocks.com/en/resources" });
承诺2.完成(功能){
输出(“承诺2已解决”);
});
$.when(promise1,promise2)。然后(函数(result1,result2){
输出(“结果1:+结果1”);
输出(“result2:+result2.toString().substr(01100)+”);
未完成({结果:42});
});
函数输出(str){
如果(!info.firstChild)
info.appendChild(document.createTextNode(“”);
info.firstChild.data+=str+“\n”;
}
};
异步脚本测试页
JavaScript没有任何内置功能,如async
或Task
。我建议尝试使用像承诺或DEFRIED(例如,Q)之类的东西,并从你的代码> Script TabMuleBue/Cuff>中返回。这对我来说是新的,我怎么才能做到这一点?@ Je'ReyMyjisiZeWSKY,如果我的答案无济于事,请考虑留下评论。