如何将值从自定义 PageRenderer 传递到 Xamarin 中的共享 Xaml 页面代码

How to pass values from custom PageRenderer to Shared Xaml Page code behind in Xamarin

我有一个 Xamarin 页面,我必须在其中使用 Android 本机页面渲染器以支持特定平台 API。

BasePage.xaml 通过 Navigation.PushAsync()

将控制权移交给 MyPage.xaml

XAML 页面: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 上面的自定义页面渲染器 如下所示。

[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);

        }


    }
}

我的意图是将控制权从 MyPageRenderer 发送回 MyPage.xaml.csBasePage.xaml.cs,结果为 ButtonTapped。我是使用事件 ItemAdded 并在该页面后面的代码中处理它。我无法仅从共享项目访问 android 特定渲染器中的 ItemAdded 事件。

我必须更新 BasePageViewModel 以便在通过后退按钮添加新项目后弹出 MyPage 时更新那里项目的内容。

问题: 我可以访问 MyPage 和 BasePage,但无法从共享项目访问渲染器方法和变量,因为 Android 项目依赖于共享,反之亦然。

我必须做类似下面的事情,它适用于非本地渲染页面 底页:

    var myPage = new MyPage();
    myPage.ItemAdded += OnItemAdded;
    await Navigation.PushAsync(myPage);

我的页面:

public event EventHandler ItemAdded;
.
.

void SomeMethod(){

ItemAdded(this, EventArgs.Empty);

}

问题:我们如何将控制权从 NativeRenderer 传回 Xamarin Forms 共享代码?

我知道我们可以将控制权传递给 MainActivity class 但我想将控制权传递给 BasePage.xaml.cs 我没有从文档中得到。如果有人在 PageRenderer 上工作过,请提出建议。

在"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());
        }
    }

在 "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();

        }
    }

在"BasePage.xaml.cs",订阅此活动。

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);
    }
}

您应该这样定义您的活动class:

public class SomeButtonClickedEvent : PubSubEvent<SomeButtonClickedEvent>
    {
        //you can define parameters here, if the event needs to pass a parameter.
    }

参考zohre moradi的回答我可以实现如下。 这不使用 IEventAggregator -Subscribe/Publish 的事件方法。如果只在一页需要事件IEventAggregator可以避免。

MyPage.xaml.cs

public event EventHandler ItemAdded;

public void RaiseItemAdded()
{
    ItemAdded(this, EventArgs.Empty);
}


//have to close return call back after item addition in MyPage  
public async void CallPopAsync()
{
    await Navigation.PopAsync();
}

MyPageRenderer.cs

MyPage mypage;

protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
    base.OnElementChanged(e);
    mypage= (MyPage)e.NewElement;

    if (e.OldElement != null || Element == null)
    {
        return;
    }

    try
    {
        SetupUserInterface();
        SetupEventHandlers();
        AddView(view);
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message);
    }
}       

private void ButtonTapped(object sender, EventArgs e)
{
    //do something

    myPage.RaiseItemAdded();

    //notify
    Toast.MakeText(this.Context, "Item created", ToastLength.Long).Show();

    myPage.CallPopAsync();      
}

并且在BasePage.xaml.cs

//in some method    
var myPage = new MyPage();
myPage.ItemAdded += OnItemAdded;
await Navigation.PushAsync(myPage);

private void OnItemAdded(object sender, EventArgs e)
{
    //call method to update binding object of viewmodel
}