【问题标题】:How to pass values from custom PageRenderer to Shared Xaml Page code behind in Xamarin如何将值从自定义 PageRenderer 传递到 Xamarin 中的共享 Xaml 页面代码
【发布时间】:2018-09-24 21:53:55
【问题描述】:

我有一个 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 并在其后面的代码中处理它页。我无法访问 ItemAdded 事件,该事件仅在共享项目中位于 android 特定渲染器中。

我必须更新 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 类,但我想将控制权传递给BasePage.xaml.cs,这不是我从文档中获得的。如果有人在 PageRenderer 上工作过,请提出建议。

【问题讨论】:

    标签: c# xamarin.forms xamarin.android custom-renderer


    【解决方案1】:

    在“我的页面”类中

      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”类中:

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

    你应该这样定义你的事件类:

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

    【讨论】:

    • 谢谢! ,我无法解决(App)App.Current).Container.Resolve&lt;IEventAggregator&gt;() 我根据您的方法使用了简单的代码。它奏效了。
    • 你说得对,我忘了说我的代码是基于 xamarin.forms 和 Prism 的。
    【解决方案2】:

    参考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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-04
      • 1970-01-01
      • 2011-09-18
      • 2019-04-19
      • 1970-01-01
      • 1970-01-01
      • 2017-05-28
      相关资源
      最近更新 更多