Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Control OnElementChanged not called when BindableProperty is changed in Xamarin Forms

I have been trying to figure out how to invoke the custom renderer OnElementChanged for the next time when BindableProperty is changed.

In my case to keep it short for understanding purpose I need to refresh the webview url in android, uwp, ios renderer's.

First time initializing the value works but later when URL changes it doesn't invoke.

So alteast if there would have been a function in custom renderer class then we could have called and updated the native control.

Tried with android and uwp but have same issue. Once url loaded further changing it doesn't refresh the webview.

So for sample sake I have provided UWP renderer if it works here then similar changes can be done in android renderer. But not sure where exactly changes to be made to make it work.

XAML

<StackLayout>
     <Entry Keyboard="Url" x:Name="txtUrl" Placeholder="Enter the URL" />

      <Button x:Name="ButtonImage" Text="Search"  Clicked="OnButtonClicked" />

     <local:HybridWebView x:Name="webView" />
</StackLayout>

Xaml.cs

    public partial class WebVieDemo : ContentPage
    {
        public WebVieDemo()
        {
            InitializeComponent();
            webView.Uri = "https://www.101cookbooks.com/archives/blueberry-beet-pancakes-vegan-recipe.html";
        }

        private void OnButtonClicked(object sender, EventArgs e)
        {
            txtUrl.Text = "https://stackoverflow.com/questions/1531093/how-do-i-get-the-current-date-in-javascript";
            webView.Uri = txtUrl.Text; // doesn't refresh the webview
        }
    }

HybridWebView.cs

    public class HybridWebView : WebView
    {
        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 static readonly BindableProperty HtmlSourceProperty = BindableProperty.Create(
          propertyName: "HtmlSource",
          returnType: typeof(string),
          declaringType: typeof(HybridWebView),
          defaultValue: default(string));

        public string HtmlSource
        {
            get { return (string)GetValue(HtmlSourceProperty); }
            set { SetValue(HtmlSourceProperty, value); }
        }
    }

UWP Renderer

[assembly: ExportRenderer(typeof(HybridWebView), typeof(WindowsWebViewRenderer))]
namespace DemoApp.UWP
{
    public class WindowsWebViewRenderer : ViewRenderer<HybridWebView, WebView>
    {
        HybridWebView hybridWebView = null;
        protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
            {
                // Create the native control and use SetNativeControl
                var webView = new WebView();
                webView.LoadCompleted += WebView_LoadCompleted;
                SetNativeControl(webView);
            }
            if (e.OldElement != null)
            {
                // Cleanup resources and remove event handlers for this element.
            }
            if (e.NewElement != null)
            {
                // Use the properties of this element to assign to the native control, which is assigned to the base.Control property
                hybridWebView = e.NewElement;
                if (hybridWebView.Uri != null)
                    Control.Source = new Uri(e.NewElement.Uri);
            }
        }

        async private void WebView_LoadCompleted(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
        {
            hybridWebView.HtmlSource = await Control.InvokeScriptAsync("eval", new string[] { "document.documentElement.outerHTML;" });
        }
    }
}
like image 949
Sharath Avatar asked Apr 28 '18 17:04

Sharath


Video Answer


1 Answers

In your custom renderer, you need to override OnElementPropertyChanged for this, not OnElementChanged, so something like:

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if (e.PropertyName == HybridWebView.UriProperty.PropertyName)
        {
            if (Control.Uri != null)
                Control.Source = new Uri(Control.Uri);
        }
    }
like image 59
DavidS Avatar answered Sep 28 '22 08:09

DavidS