I have a Xamarin page in which I had to use Android native page renderer in order to support platform specific API.
BasePage.xaml
passes control to MyPage.xaml
with Navigation.PushAsync()
XAML page : MyPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Views.MyPage" Title="My Page">
<ContentPage.Content>
</ContentPage.Content>
</ContentPage>
Android Custom page renderer for the above is something like below.
[assembly: ExportRenderer(typeof(MyPage), typeof(MyPageRenderer))]
namespace MyApp.Droid.Renderers
{
public class MyPageRenderer : PageRenderer
{
private Context _localContext;
private global::Android.Views.View view;
private Activity activity;
public event EventHandler ItemAdded;
public MyPageRenderer(Context context) : base(context)
{
_localContext = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
try
{
SetupUserInterface();
SetupEventHandlers();
AddView(view);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message);
}
}
private void SetupUserInterface()
{
activity = this.Context as Activity;
view = activity.LayoutInflater.Inflate(Resource.Layout.axml_layout, this, false);
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
view.Measure(msw, msh);
view.Layout(0, 0, r - l, b - t);
}
private void SetupEventHandlers()
{
//blah blah
}
private void ButtonTapped(object sender, EventArgs e)
{
//do something
//Here Navigate back to page which triggered this with outcome parameter or some event
ItemAdded(this, EventArgs.Empty);
}
}
}
My intention is send back control to MyPage.xaml.cs
or BasePage.xaml.cs
from MyPageRenderer
with outcome of ButtonTapped
.I am using event ItemAdded
and handle it in code behind of that page. I can not access ItemAdded
event which is in android specific renderer only from shared project.
I have to update ViewModel
of BasePage
so that I update the content of the items there when MyPage has been popped after adding new item by back button.
Problem: I can access MyPage and BasePage but can not access renderer method and variables from Shared project because Android project depends on shared not vice versa.
I have to do something like below which is working for non-native render page BasePage:
var myPage = new MyPage();
myPage.ItemAdded += OnItemAdded;
await Navigation.PushAsync(myPage);
MyPage:
public event EventHandler ItemAdded;
.
.
void SomeMethod(){
ItemAdded(this, EventArgs.Empty);
}
Question: How do we pass control from NativeRenderer back to Xamarin Forms shared code?
I know we can pass control to MainActivity
class but I want to pass control to BasePage.xaml.cs
which I did not get from documentation. If anyone has worked on PageRenderer please suggest.
in "MyPage" Class
public class MyPage : ContentPage
{
public void RaiseSomeButtonClicked() => OnSomeButtonClickeded();
private void OnSomeButtonClicked()
{
//by using aggregators you can publish any event and subscribe it in you BasePage.xaml.cs
((App)App.Current).Container.Resolve<IEventAggregator>()
.GetEvent<SomeButtonClickedEvent>().Publish(new SomeButtonClickedEvent());
}
}
in "MyPageRenderer" Class :
public class MyPageRenderer : PageRenderer
{
MyPage myPage;
//...
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
myPage = (MyPage)e.NewElement;
//...
}
private void ButtonTapped(object sender, EventArgs e)
{
//do something
myPage.RaiseSomeButtonClicked();
}
}
in "BasePage.xaml.cs", subscribe this event.
public partial class BasePage : ContentPage
{
private readonly SubscriptionToken _SomeButtonClickedEventSubscription;
public BasePage()
{
InitializeComponent();
_SomeButtonClickedEventSubscription = eventAggregator.Value.GetEvent<SomeButtonClickedEvent>().SubscribeAsync(async e =>
{
//anything you want to do when button clicked!
}, threadOption: ThreadOption.UIThread, keepSubscriberReferenceAlive: true);
}
}
You should define Your event class in this way:
public class SomeButtonClickedEvent : PubSubEvent<SomeButtonClickedEvent>
{
//you can define parameters here, if the event needs to pass a parameter.
}