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

如何从自定义Xamarin.Forms视图调用返回数据的原生异步API?

处理Xamarin.Forms中异步返回数据的原生API调用

Great question! The standard "Requested" event pattern works perfectly for fire-and-forget native operations (like ListView's ScrollTo), but when you need to call an async native API that returns data—such as UWP WebView's InvokeScriptAsync—you'll need a slight twist to handle the async result flow.

Here's a robust, recommended approach that maintains Xamarin.Forms' separation of concerns while supporting async return values:

Step 1: Define a Custom View with Async Method & Event in Shared Code

First, create a custom view (extending your target control, e.g., WebView) in your shared project. We'll add an async public method for Forms code to call, and an event that carries a TaskCompletionSource—this is how we'll pass the result back from native code.

using System;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace YourApp.Shared.Controls
{
    public class CustomWebView : WebView
    {
        // Event triggered when Forms code requests a script invocation
        public event EventHandler<InvokeScriptRequestedEventArgs> InvokeScriptRequested;

        // Async method exposed to Forms layer
        public async Task<string> InvokeScriptAsync(string scriptName, params string[] arguments)
        {
            var tcs = new TaskCompletionSource<string>();
            // Trigger the event to notify native renderer
            InvokeScriptRequested?.Invoke(this, new InvokeScriptRequestedEventArgs(tcs, scriptName, arguments));
            // Wait for native code to set the result on the TaskCompletionSource
            return await tcs.Task;
        }
    }

    // Custom event args to pass necessary data to the renderer
    public class InvokeScriptRequestedEventArgs : EventArgs
    {
        public TaskCompletionSource<string> TaskCompletionSource { get; }
        public string ScriptName { get; }
        public string[] Arguments { get; }

        public InvokeScriptRequestedEventArgs(TaskCompletionSource<string> tcs, string scriptName, string[] arguments)
        {
            TaskCompletionSource = tcs;
            ScriptName = scriptName;
            Arguments = arguments;
        }
    }
}

Step 2: Implement the Native Renderer (UWP Example)

In your UWP project, create a renderer for the CustomWebView that subscribes to the InvokeScriptRequested event. When the event fires, call the native InvokeScriptAsync method, then use the TaskCompletionSource to pass the result (or exception) back to the Forms layer.

using System;
using YourApp.Shared.Controls;
using YourApp.UWP.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace YourApp.UWP.Renderers
{
    public class CustomWebViewRenderer : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);

            // Subscribe to the event when the element is attached
            if (e.NewElement != null)
            {
                var customWebView = (CustomWebView)e.NewElement;
                customWebView.InvokeScriptRequested += OnInvokeScriptRequested;
            }

            // Unsubscribe to avoid memory leaks when the element is detached
            if (e.OldElement != null)
            {
                var oldCustomWebView = (CustomWebView)e.OldElement;
                oldCustomWebView.InvokeScriptRequested -= OnInvokeScriptRequested;
            }
        }

        private async void OnInvokeScriptRequested(object sender, InvokeScriptRequestedEventArgs e)
        {
            try
            {
                // Call the native UWP WebView's async method
                var scriptResult = await Control.InvokeScriptAsync(e.ScriptName, e.Arguments);
                // Pass the result back to Forms layer
                e.TaskCompletionSource.SetResult(scriptResult);
            }
            catch (Exception ex)
            {
                // Propagate any exceptions back to the caller in Forms
                e.TaskCompletionSource.SetException(ex);
            }
        }
    }
}

Key Benefits of This Approach

  • Maintains Separation of Concerns: Forms code doesn't directly reference native APIs, keeping your shared project clean.
  • Full Async Support: Properly handles async native methods like InvokeScriptAsync and returns the result to the caller.
  • Error Handling: Exceptions from native code are propagated back to the Forms layer, so you can handle them gracefully.
  • Follows Xamarin Patterns: Builds on the familiar "Requested" event pattern but extends it for async return values.

To use this in your Forms code, just instantiate CustomWebView and call its InvokeScriptAsync method like any other async method:

var result = await myCustomWebView.InvokeScriptAsync("myJavaScriptFunction", "arg1", "arg2");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:08:39