Как передать значения из пользовательского PageRenderer в общий код страницы Xaml в Xamarin - PullRequest
0 голосов
/ 25 сентября 2018

У меня есть страница Xamarin, на которой мне пришлось использовать собственный рендерер страниц Android для поддержки API, специфичного для платформы.

BasePage.xaml передает управление MyPage.xaml с Navigation.PushAsync()

Страница 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);

        }


    }
}

Myнамерение - вернуть управление на MyPage.xaml.cs или BasePage.xaml.cs с MyPageRenderer с результатом ButtonTapped. Я использую событие ItemAdded и обрабатываю его в коде позади этой страницы.Я не могу получить доступ к событию ItemAdded, которое находится в Android-рендерер только из общего проекта.

Мне нужно обновить ViewModel из BasePage, чтобы обновлять содержимое элементов там, когда MyPage был извлечен после добавления нового элемента кнопкой назад.

Проблема: Я могу получить доступ к MyPage и BasePage, но не могу получить доступ к методу и переменным рендерера из общего проекта, потому что проект Android зависит от общего доступа, а не наоборот.

Мне нужно сделать что-то вроде ниже которая работает для несобственной страницы рендеринга BasePage:

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

MyPage:

public event EventHandler ItemAdded;
.
.

void SomeMethod(){

ItemAdded(this, EventArgs.Empty);

}

Вопрос: Как передать управление из NativeRenderer обратно в общий код Xamarin Forms?

Я знаю, что мы можем передать управление классу MainActivity, но я хочу передать управление BasePage.xaml.cs, которое я не получил из документации.Если кто-то работал над PageRenderer, пожалуйста, предложите.

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

Со ссылкой на ответ 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
}
0 голосов
/ 25 сентября 2018

в классе "MyPage"

  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.
    }
...