Open a Browser Window with Javascript on Xamarin.Forms

berkbababerkbaba TRMember ✭✭

I have a Xamarin.Forms app. It includes a button like this:

xaml()
{
<Button x:Name="Buy_Button" Text="Satın Al" FontAttributes="Bold" TextColor="#e2e2e2" BackgroundColor="#2A52BE" FontFamily="Segoe UI" Grid.Column="2" Grid.ColumnSpan="1" Grid.RowSpan="1"  CornerRadius="5"  VerticalOptions="Start" HorizontalOptions="Center" FontSize="15.667" Grid.Row="0" Margin="0,10,10,0"  Clicked="Buy_Button_ClickedAsync" CommandParameter="{Binding  Buy_URL}" />`
}

I'm sending a URL link to click event for opening specific web page. Code is:

  private async void Buy_Button_ClickedAsync(object sender, EventArgs e)
    {
        Button btn = (Button)sender; // Coming button from click event handler.
        var buylink = btn.CommandParameter.ToString(); // Get the CommandParameter.
                                                       //  await DisplayAlert("Satın alma linki", buylink, "Anladım"); // Show the link.
        try // Uwp & iOS & Android
        {
            await Browser.OpenAsync(new Uri(buylink), BrowserLaunchMode.SystemPreferred); // Open url in-app browser for iOS & Android- native in UWP
        }
        catch (NotImplementedInReferenceAssemblyException ex) //Wasm falls here because lack of Xamarin.Essentials.
        {
            // await DisplayAlert("Hata", ex.Message, "Anladım"); // Show the info about exception.

            // Jint - nt is a Javascript interpreter for .NET which provides full ECMA 5.1 compliance and can run on any .NET platform. 
            //Because it doesn't generate any .NET bytecode nor use the DLR it runs relatively small scripts faster. 
            //https://github.com/sebastienros/jint

            var engine = new Engine();
            engine.SetValue("log", new Action<object>(Console.WriteLine));

            engine.Execute(@"function openurl() { log('" + buylink + "'); }; openurl(); ");
        }
    }

In UWP, Xamarin.iOS and Xamarin. Android this code is running via Xamarin.Esssentials:

 await Browser.OpenAsync(new Uri(buylink), BrowserLaunchMode.SystemPreferred); // Open url in-app browser for iOS & Android- native in UWP

However, my Xamarin.Forms app projected to WebAssembly code with Uno Platform, so this code block not running. As a result. I install Jint to Xamarin.Forms app. This catch block prints the link to Browser console, but no window.open function track in API reference:

 catch (NotImplementedInReferenceAssemblyException ex) //Wasm falls here because lack of Xamarin.Essentials.
            {
                // await DisplayAlert("Hata", ex.Message, "Anladım"); // Show the info about exception.

                       // Jint - nt is a Javascript interpreter for .NET which provides full ECMA 5.1 compliance and can run on any .NET platform. 
                //Because it doesn't generate any .NET bytecode nor use the DLR it runs relatively small scripts faster. 
                //https://github.com/sebastienros/jint

                var engine = new Engine();
                engine.SetValue("log", new Action<object>(Console.WriteLine));

                engine.Execute(@"function openurl() { log('" + buylink + "'); }; openurl(); ");
            }
        }

How can I open WebBrowser page on WASM via Javascript form Xamarin.Forms C# code? Thanks.

Best Answer

  • berkbababerkbaba TR ✭✭
    Accepted Answer

    Hi.

    I solved it via Customizing via WebView:

    Try to create the webView custom renderer that extends the WebView to
    allow C# code to be invoked from JavaScript.

    The process for WebView Renderer:

    Create the HybridWebView custom control. Consume the HybridWebViewfrom
    Xamarin.Forms. Create the custom renderer for the HybridWebView on
    each platform. When the user clicks an HTML button, a JavaScript
    function will invoke a C# Action.

    1.Create the HybridWebView

    public class HybridWebView : WebView
    {
        Action<string> action;
    
        public static readonly BindableProperty UriProperty = BindableProperty.Create(
            propertyName: "Uri",
            returnType: typeof(string),
            declaringType: typeof(HybridWebView),
            defaultValue: default(string));
    
        public string Uri
        {
            get { return (string)GetValue(UriProperty); }
            set { SetValue(UriProperty, value); }
        }
    
        public void RegisterAction(Action<string> callback)
        {
            action = callback;
        }
    
        public void Cleanup()
        {
            action = null;
        }
    
        public void InvokeAction(string data)
        {
            if (action == null || data == null)
            {
                return;
            }
            action.Invoke(data);
        }
    }
    

    2.Consume the HybridWebView

    <ContentPage
                 xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
                 x:Class="CustomRenderer.HybridWebViewPage"
                 Padding="0,40,0,0">
        <local:HybridWebView x:Name="hybridWebView"
                             Uri="index.html" />
    </ContentPage>
    

    3.Create the custom renderer on UWP

    [assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
    namespace CustomRenderer.UWP
    {
        public class HybridWebViewRenderer : WebViewRenderer
        {
            const string JavaScriptFunction = "function invokeCSharpAction(data){window.external.notify(data);}";
    
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
            {
                base.OnElementChanged(e);
    
                if (e.OldElement != null)
                {
                    Control.NavigationCompleted -= OnWebViewNavigationCompleted;
                    Control.ScriptNotify -= OnWebViewScriptNotify;
                }
                if (e.NewElement != null)
                {
                    Control.NavigationCompleted += OnWebViewNavigationCompleted;
                    Control.ScriptNotify += OnWebViewScriptNotify;
                    Control.Source = new Uri($"ms-appx-web:///Content//{((HybridWebView)Element).Uri}");
                }
            }
    
            async void OnWebViewNavigationCompleted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationCompletedEventArgs args)
            {
                if (args.IsSuccess)
                {
                    // Inject JS script
                    await Control.InvokeScriptAsync("eval", new[] { JavaScriptFunction });
                }
            }
    
            void OnWebViewScriptNotify(object sender, NotifyEventArgs e)
            {
                ((HybridWebView)Element).InvokeAction(e.Value);
            }
        }
    }
    

    At the last, call function from code:

      hybridWebView.RegisterAction(data => Browser.OpenAsync(data, BrowserLaunchMode.SystemPreferred));
                    hybridWebView.InvokeAction("http://localhost");
    

Answers

  • JarvanJarvan Member, Xamarin Team Xamurai

    You can directly invoke the javascript of the running environment which is running in a javascript virtual machine. Try to make a call to native javascript:

    WebAssemblyRuntime.InvokeJS("(function(){location.href=\"https://www.wikipedia.com/\";})();");
    

    Refer to:
    https://stackoverflow.com/questions/60152725/open-a-browser-window-with-javascript-on-xamarin-forms

  • berkbababerkbaba TRMember ✭✭

    Thanks @Jarvan,

    How can use it on Xamarin. Forms app ? WebAssemblyRuntime.InvokeJS available in Uno.UI class. If I install NuGet package of it to Forms project, VS cannot build and deploy UWP app side because of class ambiguity.

  • berkbababerkbaba TRMember ✭✭
    Accepted Answer

    Hi.

    I solved it via Customizing via WebView:

    Try to create the webView custom renderer that extends the WebView to
    allow C# code to be invoked from JavaScript.

    The process for WebView Renderer:

    Create the HybridWebView custom control. Consume the HybridWebViewfrom
    Xamarin.Forms. Create the custom renderer for the HybridWebView on
    each platform. When the user clicks an HTML button, a JavaScript
    function will invoke a C# Action.

    1.Create the HybridWebView

    public class HybridWebView : WebView
    {
        Action<string> action;
    
        public static readonly BindableProperty UriProperty = BindableProperty.Create(
            propertyName: "Uri",
            returnType: typeof(string),
            declaringType: typeof(HybridWebView),
            defaultValue: default(string));
    
        public string Uri
        {
            get { return (string)GetValue(UriProperty); }
            set { SetValue(UriProperty, value); }
        }
    
        public void RegisterAction(Action<string> callback)
        {
            action = callback;
        }
    
        public void Cleanup()
        {
            action = null;
        }
    
        public void InvokeAction(string data)
        {
            if (action == null || data == null)
            {
                return;
            }
            action.Invoke(data);
        }
    }
    

    2.Consume the HybridWebView

    <ContentPage
                 xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
                 x:Class="CustomRenderer.HybridWebViewPage"
                 Padding="0,40,0,0">
        <local:HybridWebView x:Name="hybridWebView"
                             Uri="index.html" />
    </ContentPage>
    

    3.Create the custom renderer on UWP

    [assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
    namespace CustomRenderer.UWP
    {
        public class HybridWebViewRenderer : WebViewRenderer
        {
            const string JavaScriptFunction = "function invokeCSharpAction(data){window.external.notify(data);}";
    
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
            {
                base.OnElementChanged(e);
    
                if (e.OldElement != null)
                {
                    Control.NavigationCompleted -= OnWebViewNavigationCompleted;
                    Control.ScriptNotify -= OnWebViewScriptNotify;
                }
                if (e.NewElement != null)
                {
                    Control.NavigationCompleted += OnWebViewNavigationCompleted;
                    Control.ScriptNotify += OnWebViewScriptNotify;
                    Control.Source = new Uri($"ms-appx-web:///Content//{((HybridWebView)Element).Uri}");
                }
            }
    
            async void OnWebViewNavigationCompleted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationCompletedEventArgs args)
            {
                if (args.IsSuccess)
                {
                    // Inject JS script
                    await Control.InvokeScriptAsync("eval", new[] { JavaScriptFunction });
                }
            }
    
            void OnWebViewScriptNotify(object sender, NotifyEventArgs e)
            {
                ((HybridWebView)Element).InvokeAction(e.Value);
            }
        }
    }
    

    At the last, call function from code:

      hybridWebView.RegisterAction(data => Browser.OpenAsync(data, BrowserLaunchMode.SystemPreferred));
                    hybridWebView.InvokeAction("http://localhost");
    
Sign In or Register to comment.