What is the Best Practice for Raising Custom Events in the MVVM Pattern?

I am currently building a small app using MVVM pattern.
I also implemented custom ViewRenderer by following the guide on building hybridwebview.

I wanted to extend this example a bit and add a button which will trigger Control.EvaluateJavascript(Control is platform specific WebView) to execute the script that's been injected.
I was able to get this working in two different ways, but I wasn't satisfied with either of these solutions

  • Binding Hack

    1. Define bindable property in the hybridwebview
    2. Create a boolean value in the viewmodel and bind it to the bindable property that was just created in the hybridwebview
    3. Define command for the button, which just flips the value of the boolean in the view model
    4. In the ViewRenderer, subscribe to PropertyChanged, and call Control.EvaluateJavascript
  • Bind HybridWebView as Button Command's Command Parameter

    1. Define command on the button.
    2. Bind the command parameter to hybridwebview via reference (eg: CommandParameter="{x:Reference hybridWebView}")
    3. Define EventHandler and a method which raises this event in hybridwebview
    4. In the view model, command for clicking the button will call the method which was defined in the view.
    5. Subscribe to the event in the ViewRenderer and call Control.EvaluateJavascript

How would I go about doing this while conforming to Xamarin best practices?
Thanks!

Best Answer

  • SungJeongSungJeong US ✭✭
    Accepted Answer

    Okay... so I think this might be one of those cases where plain old event handler should be used over command.
    In my case, I am not dealing with any bounded data(ViewModel), so there shouldn't be any issues with View and ViewModel being coupled.
    So I decided to go with modified version of hack 2

    • Define event handler for click on the button
    • Define event in the hybridwebview and a method for raising it
    • When the button is clicked, raise the event via accessing the hybridwebview on the page and calling the method for raising the event
    • Subscribe to the event in the ViewRenderer and call Control.EvaluateJavascript if event is raised

Answers

  • SungJeongSungJeong USMember ✭✭
    Accepted Answer

    Okay... so I think this might be one of those cases where plain old event handler should be used over command.
    In my case, I am not dealing with any bounded data(ViewModel), so there shouldn't be any issues with View and ViewModel being coupled.
    So I decided to go with modified version of hack 2

    • Define event handler for click on the button
    • Define event in the hybridwebview and a method for raising it
    • When the button is clicked, raise the event via accessing the hybridwebview on the page and calling the method for raising the event
    • Subscribe to the event in the ViewRenderer and call Control.EvaluateJavascript if event is raised
  • nicolacoolshopnicolacoolshop Member ✭✭

    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

  • SungJeongSungJeong USMember ✭✭
    edited May 2018

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭
    edited May 2018

    This forum is absolute garbage...
    My posts never get posted and disappear randomly.
    I had answer that was pending for a week or longer, but I can't seem to post it.

    P.S. I sent you a message with my reply

  • SungJeongSungJeong USMember ✭✭

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭
    1. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭
    1. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
  • SungJeongSungJeong USMember ✭✭

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭
    1. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

  • SungJeongSungJeong USMember ✭✭
    1. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
  • SungJeongSungJeong USMember ✭✭

    @nicolacoolshop said:
    Hi SungJeong, can you share an example please? I try to do the same thing but I have some problems.
    Thank you

    Here's what I have done:

    HybridWebView.cs:

    1. Define Action as a Bindable Property (will see why later)

      public static readonly BindableProperty ActionProperty = BindableProperty.Create(
      propertyName: "Action",
      returnType: typeof(Action<String>),
      declaringType: typeof(HybridWebView),
      defaultValue: null,
      defaultBindingMode: BindingMode.OneWay);
      
    2. Define getter and setter for action

      public Action<String> Action {
        get { return (Action<String>)GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
      }
      
    3. Define event handler for handling your special event.

      public event EventHandler TriggerSave;
      
      public void OnTriggerSave() {
        TriggerSave(this, EventArgs.Empty);
      }
      
    4. Define method for invoking action

      public void InvokeAction(String data) {
        if (Action == null || data == null) {
          return;
        }
        Action.Invoke(data);
      }
      

    XAML Page:

    1. Toolbar button to trigger the event

        <ContentPage.ToolbarItems>
          <ToolbarItem Text="Save" Clicked="SaveData">
          </ToolbarItem>
        </ContentPage.ToolbarItems>
      
    2. HybridWebView with data binding

      <local:HybridWebView Uri="{Binding Uri}" x:Name="hybridWv" Action="{Binding Action}"
          HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
      />
      

    XAML Page Code Behind:

    1. Method that handles the click and triggers our custom event.

      private void SaveData(Object sender, EventArgs e) {
        hybridWv.OnTriggerSave();
      }
      

    ViewModel for the Page:

    1. Define Action property that will be databound. Perform any action you want to perform here.

      public Action<String> Action { get; set; }
      
      // In the constructor:
      Action = new Action<String>(data => Console.WriteLine(data));
      

    HybridWebViewRender:

    1. Perform setup (TriggerSave is defined above), mainly interested in hooking up the TriggerSave event handler:

      protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e) {
        base.OnElementChanged(e);
      
        if (Control == null) {
          userController = new WKUserContentController();
          // handle element changed event
          Element.PropertyChanged += PropertyChangedEventHandler;
          Element.TriggerSave += TriggerSaveEventHandler;
        ...
      
    2. Handle the event. Here I just call a dummy function and return the value that is returned from that function. Element.InvokeAction will execute the action that we databound before with the data we got back from executing javascript.

      void TriggerSaveEventHandler(Object sender, EventArgs e) {;
        // define handler for processing the returned value from javascript
        WKJavascriptEvaluationResult handler = (NSObject result, NSError err) => {
          if (err != null) {
            System.Console.WriteLine(err);
          }
          if (result != null) {
            System.Console.WriteLine(result);
            // invoke action using the value returned from javascript
            Element.InvokeAction(result.ToString());
          }
        };
        Control.EvaluateJavaScript("function test(x) { return x; }('test');", handler);
      }
      

    Hope this helps.

Sign In or Register to comment.