You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI中WebView2异步脚本对话框无法等待的解决方案问询

解决MAUI WebView2自定义Prompt对话框的异步阻塞问题

针对WebView2无法等待异步弹窗的问题,有两种可行的解决方案,推荐第二种异步交互方式更符合WinUI/MAUI的架构设计:

方案一:通过TaskCompletionSource同步等待异步弹窗结果

利用TaskCompletionSource在同步的ScriptDialogOpening事件中等待异步弹窗的结果,再将结果返回给WebView2:

1. 拦截默认对话框并注册事件

webView.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
webView.CoreWebView2.ScriptDialogOpening += CustomScriptDialogOpening;

2. 实现事件处理函数

private void CustomScriptDialogOpening(object sender, CoreWebView2ScriptDialogOpeningEventArgs e)
{
    e.Handled = true;

    switch (e.Kind)
    {
        case CoreWebView2ScriptDialogKind.Prompt:
            var promptTcs = new TaskCompletionSource<string>();
            
            // 在UI线程调度自定义弹窗
            Application.Current.Dispatcher.Dispatch(async () =>
            {
                var dialog = new CustomPromptDialog(e.Message, e.DefaultText);
                var result = await dialog.ShowAsync();
                
                promptTcs.SetResult(result == ContentDialogResult.Primary ? dialog.InputText : null);
            });

            // 同步等待结果,WebView2会阻塞到这里
            string input = promptTcs.Task.Result;
            if (input != null)
                e.Accept(input);
            else
                e.Cancel();
            break;

        // 同理处理Alert和Confirm
        case CoreWebView2ScriptDialogKind.Alert:
            var alertTcs = new TaskCompletionSource<bool>();
            Application.Current.Dispatcher.Dispatch(async () =>
            {
                var dialog = new CustomAlertDialog(e.Message);
                await dialog.ShowAsync();
                alertTcs.SetResult(true);
            });
            alertTcs.Task.Wait();
            e.Accept();
            break;

        case CoreWebView2ScriptDialogKind.Confirm:
            var confirmTcs = new TaskCompletionSource<bool>();
            Application.Current.Dispatcher.Dispatch(async () =>
            {
                var dialog = new CustomConfirmDialog(e.Message);
                var result = await dialog.ShowAsync();
                confirmTcs.SetResult(result == ContentDialogResult.Primary);
            });
            bool isConfirmed = confirmTcs.Task.Result;
            if (isConfirmed)
                e.Accept();
            else
                e.Cancel();
            break;
    }
}

3. 自定义Prompt对话框示例

public class CustomPromptDialog : ContentDialog
{
    public string InputText { get; private set; }

    public CustomPromptDialog(string message, string defaultText)
    {
        Title = "输入提示";
        Content = new VerticalStackLayout
        {
            Children =
            {
                new Label { Text = message, Margin = new Thickness(0,0,0,10) },
                new Entry { Text = defaultText, Placeholder = "请输入内容" }
            }
        };
        PrimaryButtonText = "确定";
        SecondaryButtonText = "取消";

        PrimaryButtonClick += (s, args) =>
        {
            InputText = ((Entry)((VerticalStackLayout)Content).Children[1]).Text;
        };

        SecondaryButtonClick += (s, args) =>
        {
            InputText = null;
        };
    }
}

方案二:JS Promise + WebMessage异步交互(推荐)

这种方式完全避免线程阻塞,通过替换JS的prompt函数为返回Promise的自定义函数,再通过WebView2的消息机制和C#端交互:

1. 注入自定义JS代码替换prompt

在WebView2初始化完成后执行:

await webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(@"
    window.prompt = function(msg, defaultVal) {
        return new Promise(resolve => {
            // 发送消息到C#端
            window.chrome.webview.postMessage({
                type: 'prompt',
                message: msg,
                defaultValue: defaultVal || ''
            });
            // 保存resolve回调供C#调用
            window.__promptResolve = resolve;
        });
    };
");

2. 注册WebMessageReceived事件处理请求

webView.CoreWebView2.WebMessageReceived += async (sender, e) =>
{
    var msg = System.Text.Json.JsonSerializer.Deserialize<PromptRequest>(e.WebMessageAsJson);
    if (msg.Type != "prompt") return;

    // 显示自定义弹窗
    var dialog = new CustomPromptDialog(msg.Message, msg.DefaultValue);
    var result = await dialog.ShowAsync();
    string input = result == ContentDialogResult.Primary ? dialog.InputText : null;

    // 调用JS的resolve返回结果
    await webView.CoreWebView2.ExecuteScriptAsync($"window.__promptResolve('{input}');");
};

// 定义消息模型
public class PromptRequest
{
    public string Type { get; set; }
    public string Message { get; set; }
    public string DefaultValue { get; set; }
}

方案对比

  • 方案一:实现简单,直接拦截原生事件,但会短暂阻塞WebView2的事件线程,复杂场景下可能导致UI卡顿
  • 方案二:完全异步,符合MAUI/WinUI的异步设计,无线程阻塞问题,扩展性更强,推荐使用

内容的提问来源于stack exchange,提问作者OMendoza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 12:37:43