Данные во время «позднего связывания» не отображаются на реальном устройстве (Xamarin.Forms + Prism + CarouselViewController) - PullRequest
0 голосов
/ 21 января 2019

Здравствуйте! Приведенный ниже код отлично работает на симуляторе IOS, но не работает на реальном устройстве (Iphone 6S Plus) - коллекция тегов (что это в моем случае - вы смотритедалее) не заполняется.(Xamarin.Forms, проект IOS, Prism, CarouselViewController)

Я заранее прошу прощения за большой объем кода - это часть большой разрабатываемой программы, я сразу решил привести все необходимые листинги.


1. Существуют PageA/PageAViewModel (связанный BindableBase (Prism)), PageB/PageBViewModel (связанный BindableBase (Prism)) и логический ViewModelС.На PageAViewModel создается логический ViewModelС и передается на PageBViewModel в качестве параметра навигации:

async Task GoPageB()
{
    var navigationParams = new NavigationParameters();
    navigationParams.Add("ViewModelС", ViewModelС);
    await NavigationService.NavigateAsync(new System.Uri("http://.../PageB",
                    System.UriKind.Absolute), navigationParams);
}

2.В ходе выполнения кода он собирается PageB, затем его PageBViewModel, затем он разрабатывает метод PageB получения параметров навигации, при котором PageBViewModel получает ViewModelC:

public override void OnNavigatingTo(INavigationParameters parameters)
{
    var modelInParameters = parameters["ViewModelC"] as ViewModelC;
    MyViewModelC = modelInParameters;
}

3. ViewModelC находится в PageBViewModel как свойство, тогда как в конструкторе оно не объявлено, но в основном получается и присваивается в методе OnNavigatingTo

public ViewModelC MyViewModelC
        {
            get => _myViewModelC;
            set => SetProperty(ref _myViewModelC, value);
        }

4. PageB/PageBViewModel на самом деле это встроенная система, основанная на CarouselViewControler (https://github.com/alexrainman/CarouselView). В XAML это выглядит так:

 <ContentPage.Content>
        <abstractions:CarouselViewControl x:Name="OnBrdngPg_CrslVwCntrl"
                                          VerticalOptions="FillAndExpand"
                                          HorizontalOptions="FillAndExpand"
                                          Orientation="Horizontal"
                                          InterPageSpacing="0"
                                          IsSwipeEnabled="True"
                                          IndicatorsTintColor="{StaticResource ClrGreyLight}"
                                          CurrentPageIndicatorTintColor="{StaticResource ClrLightorange}"
                                          AnimateTransition="True"
                                          Position="0"
                                          ShowIndicators="True"
                                          ShowArrows="False">
            <abstractions:CarouselViewControl.ItemsSource>
                <x:Array Type="{x:Type ContentView}">
                    <ContentView x:Name="CW1"/>
                    <ContentView x:Name="CW2"/>
                    <ContentView x:Name="CW3"/>
                    <ContentView x:Name="CW4"/>
                    <ContentView x:Name="CW5"/>
                </x:Array>
            </abstractions:CarouselViewControl.ItemsSource>
        </abstractions:CarouselViewControl>
 </ContentPage.Content>

5. CarouselViewControl в x:Array включает пять ContentViews. Каждый из ContentView имеет пять вариантов представлений (реализация Statecontainer - пять состояний одной страницы Normal, Loading, Error, NoInternet, NoData - состояние страницы задается в коде в зависимости отна логике программы в XAML Statecontainer подписывается на изменение состояния и показывает соответствующее ContentView:

<ContentView x:Name="CW2"
                Style="{DynamicResource ContentViewBoardingStyle}"
                BackgroundColor="{StaticResource ClrGeneralwhite}">
                <ContentView.Content>
                        <Grid BackgroundColor="Transparent">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Grid.Children>
                                <StackLayout Grid.Row="0"
                                              Style="{StaticResource StLt_BaseStyle}">
                                    <StackLayout.Children>
                                        <Images:ImageFit Source="{Binding HeaderImageSource}"/>
                                    </StackLayout.Children>
                                </StackLayout>
                                <stateContainer:StateContainer State="{Binding OnBoardingInState}"
                                                               Grid.Row="1"
                                                               Style="{StaticResource StateContainerNormalStateStyle}"
                                                               BackgroundColor="{StaticResource ClrGeneralwhite}">
                                    <stateContainer:StateCondition State="Normal">
                                        <stateContainer:StateCondition.Content>
                                            <StackLayout x:Name="OnbdngPg_RootStack2"
                                                         Orientation="Vertical"
                                                         Spacing="12">
                                                <StackLayout.Children>
                                                    <StackLayout Padding="0,15,0,10">
                                                        <StackLayout.Children>
                                                            <labels:LblLvl2 Text="{markupExtension:Translate OnBrdPg_Pg2_HdrTxt}"
                                                                    TextColor="{StaticResource ClrGeneraldark}"
                                                                    HorizontalTextAlignment="Start"/>
                                                        </StackLayout.Children>
                                                    </StackLayout>
                                                    <StackLayout>
                                                        <StackLayout.Children>
                                                            <custom:GdyStackPanel x:Name="CustomControl"
                                                                                  CustomViewModel="{Binding ViewModelC, Mode=OneTime}"/>
                                                        </StackLayout.Children>
                                                    </StackLayout>
                                                </StackLayout.Children>
                                            </StackLayout>
                                        </stateContainer:StateCondition.Content>
                                    </stateContainer:StateCondition>
                                    <stateContainer:StateCondition State="Error"/>
                                    <stateContainer:StateCondition State="Loading"/>
                                    <stateContainer:StateCondition State="NoInternet"/>
                                    <stateContainer:StateCondition State="NoData"/>
                                </stateContainer:StateContainer>
                            </Grid.Children>
                        </Grid>
            </ContentView.Content>
</ContentView>

6. Как вы можете видеть на CW2, есть CustomControl - этопользовательский Grid, кроме всего прочего, имеющий облако тегов - коллекцияn кнопок, которые он генерирует из ObservableCollection CustomControlTagsCollection, расположенного в ViewModelC (в управлении это BindableProperty CustomViewModel).

public static readonly BindableProperty CustomViewModelProperty = BindableProperty.Create(
            nameof(CustomViewModel),
            typeof(ViewModelC),
            typeof(CustomControl),
            null,
            BindingMode.TwoWay,
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    var panel = bindable as CustomControl;
                    var oldbinding = oldValue as ViewModelC;
                    var newbinding = newValue as ViewModelC;

                    if (oldbinding == null)
                    {
                        panel.CustomViewModel = newbinding;

                        panel.ButtonNewTag.BindingContext = newbinding;
                        panel.EntryNewTag.BindingContext = newbinding;
                    }
                 // This method collects the tag cloud from the collection.
                    panel.CreateStackPanelAsync();
                });
            });


        public ViewModelC CustomViewModel
        {
            get => (ViewModelC)GetValue(CustomViewModelProperty);
            set => SetValue(CustomViewModelProperty, value);

        }

РЕЗЮМЕ

Подводя итог, можно сказать, что:

  • PageA/PageAViewModel передает ViewModelC на PageB/PageBViewModel, который получает ViewModelC в качестве «параметра навигации» метода OnNavigatingTo (первоначально в конструкторе PageBViewModel ViewModelC не определено в PageBViewModel как публичное свойство PageBViewModel первоначально инициализировано и используется в методе OnNavigatingTo).По этой причине PageB/PageBViewModel изначально создается без ViewModelС
  • соответственно PageB/PageBViewModelCarouselViewControlerContentView x:Name:"CW2"CustomControl при первоначальной инициализации получает пустой CustomControlTagsCollection и облако теговне собирается
  • , когда PageB/PageBViewModel получает ViewModelС, по пути PageB/PageBViewModelCarouselViewController -> ContentView x:Name="CW2"CustomControl получает переданный ViewModelС, он выполняет propertyChanged BindableProperty CustomViewModelPropertyпривязка обновляется и вызывается метод panel.CreateStackPanelAsync (), который заполняет коллекцию тегами.В некотором смысле это «поздняя привязка».

Буду признателен за любую помощь.

Прошу прощения за переводчик Google.

Хороший и чистый кодвсем вам (нам:))!

1 Ответ

0 голосов
/ 25 января 2019

Hello!

Я решил свою проблему в контексте работы с Призмой:

  1. Я заменил логическую модель ViewModelC на ServiceC, в то же время я создал IServiceC и ServiceC, зарегистрировал их в App.xaml.cs:

    containerRegistry.Register<IServiceC, ServiceC>();

  2. Вся логика работы из ViewModelC была перенесена в ServiceC.

  3. Заинтересованные ViewModels, которые работают с ServiceC данными, реализовали это:

    <code>//Declaration
    private IServiceC _serviceC;
    //Initialization 
    public ViewModelB(ServiceC serviceC)
    {
        __serviceC =  serviceC;
    }
  4. В ViewModels, где ServiceC требуется для привязки через xaml, реализовал это следующим образом ( не уверен, если это правильно с точки зрения безопасности ):

    //Declaration
    private IServiceC _serviceC;
    public IServiceC  ServiceC
    {
       get => _serviceC;
       set => SetProperty(ref _serviceC, value);
    }
    //Initialization 
    public ViewModelB(ServiceC serviceC)
    {
        ServiceC =  serviceC;
    }
  5. BindableProperty в CustomControl:

    public static readonly BindableProperty serviceCProperty = BindableProperty.Create(
        nameof(MyServiceC),
        typeof(ServiceC),
        typeof(CustomControl),
        null,
        BindingMode.OneTime,
        propertyChanged: (bindable, oldValue, newValue) =>
        {
            var panel = bindable as CustomControl;
    
    <pre><code>    Device.BeginInvokeOnMainThread(() =>
        {
            var oldbinding = oldValue as ServiceC;
            var newbinding = newValue as ServiceC;
    
            if (oldbinding == null)
            {
                panel.MyServiceC = newbinding;
    
                panel.ButtonNewTag.BindingContext = newbinding;
                panel.EntryNewTag.BindingContext = newbinding;
    
                //This is where the panel is filled with tags
                panel.CreateStackPanelAsync();
            }
        });
    });
    
    Публичный СервисC MyServiceC { get => (ServiceC) GetValue (ServiceCProperty); set => SetValue (serviceCProperty, value); }
    1. При выходе из моделей просмотра реализован метод Destroy (для избежания утечек памяти):
       public override void Destroy()
      {
      base.Destroy();
      this.ServiceC = null;
      }
  6. Результат - все отлично работает на симуляторе и на реальном устройство.

Выводы

  1. Я понял, что при работе с Prism и проектировании логики обмена данными view / viewmodels со сторонними существенными сущностями нет необходимости пытаться реализовать эти сущности через классы, которые объявлены в viewmodels (MyClass myClass = new MyClass () {} ), а затем передавать ссылки или клоны этого класса между моделями представления - это приводит к путанице и возможной потере (своевременной недогрузке) данных на одном из этапов.

  2. Если мои дальнейшие исследования безопасности и производительности служб в Prism подтвердят предположение о стабильной и ресурсосберегающей работе, я рекомендую этот способ.

  3. Буду признателен за любые советы, комментарии и советы по этому вопросу.

  4. Спасибо всем!

...