UWP WebView中能否实现confirm()与prompt()等基础JavaScript API?
完全懂你的无奈——手里堆着一大堆现成的HTML/JS代码,就因为UWP WebView不支持这些基础弹窗API就要重写,换谁都得崩溃!别担心,真的有办法搞定,而且不需要动你的核心代码,只需要加一层「桥接」逻辑就行。
先给你说清楚根源:UWP里的传统WebView控件(不是后来的WebView2)出于安全和系统体验一致性的考虑,确实没有实现原生的prompt()、confirm(),而且JS线程本身也没法像浏览器那样被弹窗直接阻塞。但我们可以用JS注入+UWP原生对话框+双向通信的方式完美模拟这个行为。
方案一:针对传统WebView的桥接实现
1. 注入自定义JS覆盖原生API
在WebView加载完成后,注入一段JS代码,把原生的弹窗API替换成我们自己的函数——这些函数会把弹窗请求发送给UWP端,然后通过Promise等待结果返回。
// 覆盖confirm函数 window.confirm = function(message) { const callbackId = 'confirm_' + Date.now(); // 向UWP端发送弹窗请求,用JSON格式区分类型和参数 window.external.notify(JSON.stringify({ type: 'confirm', message: message, callbackId: callbackId })); // 返回Promise等待结果 return new Promise((resolve) => { window['__confirm_callback_' + callbackId] = resolve; }); }; // 覆盖prompt函数 window.prompt = function(message, defaultValue = '') { const callbackId = 'prompt_' + Date.now(); window.external.notify(JSON.stringify({ type: 'prompt', message: message, defaultValue: defaultValue, callbackId: callbackId })); return new Promise((resolve) => { window['__prompt_callback_' + callbackId] = resolve; }); };
2. 在UWP端处理请求并返回结果
在你的UWP代码(比如C#)里,给WebView注册ScriptNotify事件,收到JS的请求后弹出原生的MessageDialog,再把用户的选择/输入结果传回JS。
private async void MyWebView_ScriptNotify(object sender, NotifyEventArgs e) { var webView = sender as WebView; if (webView == null) return; try { // 解析JS发来的请求 var request = JsonConvert.DeserializeObject<DialogRequest>(e.Value); switch (request.Type) { case "confirm": // 创建确认对话框 var confirmDialog = new MessageDialog(request.Message, "确认"); confirmDialog.Commands.Add(new UICommand("确定", null, true)); confirmDialog.Commands.Add(new UICommand("取消", null, false)); confirmDialog.DefaultCommandIndex = 0; confirmDialog.CancelCommandIndex = 1; var confirmResult = await confirmDialog.ShowAsync(); // 调用JS的回调函数返回结果 await webView.InvokeScriptAsync("eval", new[] { $"window['__confirm_callback_{request.CallbackId}']({(bool)confirmResult.Id});" }); break; case "prompt": // 创建带输入框的提示对话框 var promptDialog = new MessageDialog(request.Message, "输入"); var inputBox = new TextBox { Text = request.DefaultValue }; promptDialog.Content = inputBox; promptDialog.Commands.Add(new UICommand("确定", null, inputBox)); promptDialog.Commands.Add(new UICommand("取消", null, null)); promptDialog.DefaultCommandIndex = 0; promptDialog.CancelCommandIndex = 1; var promptResult = await promptDialog.ShowAsync(); var inputValue = promptResult.Id as TextBox != null ? promptResult.Id.Text : null; // 返回输入结果 await webView.InvokeScriptAsync("eval", new[] { $"window['__prompt_callback_{request.CallbackId}']({(inputValue != null ? $"\"{inputValue}\"" : "null")});" }); break; } } catch (Exception ex) { // 处理异常,避免崩溃 System.Diagnostics.Debug.WriteLine($"处理弹窗请求出错:{ex.Message}"); } } // 辅助类用于序列化请求 public class DialogRequest { public string Type { get; set; } public string Message { get; set; } public string DefaultValue { get; set; } public string CallbackId { get; set; } }
⚠️ 别忘了在XAML里给WebView配置AllowedScriptNotifyUris,或者在Package.appxmanifest中添加对应的规则,确保JS能通过window.external.notify发送消息。
3. 适配原有同步代码
如果你的旧JS代码是同步调用弹窗的,比如:
if (confirm("确定要删除吗?")) { // 执行删除操作 }
只需要改成async/await的写法即可,这是最小的改动:
async function handleDelete() { if (await confirm("确定要删除吗?")) { // 执行删除操作 } }
方案二:升级到WebView2(更简单!)
如果你的应用还在用旧的WebView,强烈建议升级到WebView2——它基于Edge Chromium内核,完全支持原生的prompt()、confirm(),不需要任何额外的桥接逻辑,直接就能用你的现有代码,一步到位解决问题。
内容的提问来源于stack exchange,提问作者j_6582

