Xamarin构成列表中的列表(使用棱镜导航)

时间:2020-07-22 17:01:01

标签: mvvm xamarin.forms prism

问题是我似乎无法通过NavigationService传递给ModulesModel,以用于导航到AnotherView项,而我只能将其传递给SectionModel。但我只能将NavigationService传递给父级ListView(我不希望)它需要通过子级ListView。帮助!!!

UI-代码

      <ListView
      SeparatorColor="Transparent"
      VerticalOptions="FillAndExpand"
      HorizontalOptions="FillAndExpand"
      ItemsSource="{Binding Data}"
      HasUnevenRows="True">
      <ListView.Header>
        <local:ModuleCourseHeaderViewControl />
      </ListView.Header>
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <Frame
              HasShadow="False"
              BackgroundColor="{StaticResource BiancaBackgroundColour}">
              <Frame.CornerRadius>
                <OnIdiom
                  x:TypeArguments="x:Single"
                  Phone="0"
                  Tablet="15" />
              </Frame.CornerRadius>
              <Frame.Margin>
                <OnIdiom
                  x:TypeArguments="Thickness"
                  Phone="0,10"
                  Tablet="20,10" />
              </Frame.Margin>
              <StackLayout>
                <Label
                  FontFamily="{StaticResource LatoRegularFont}"
                  TextColor="{StaticResource MediumGold}"
                  Text="{Binding Title}">
                  <Label.FontSize>
                    <OnIdiom
                      x:TypeArguments="x:Double"
                      Phone="15"
                      Tablet="17" />
                  </Label.FontSize>
                </Label>

                <flv:FlowListView
                  BackgroundColor="Transparent"
                  SeparatorVisibility="None"
                  HasUnevenRows="False"
                  FlowColumnCount="{DynamicResource ModuleFlowColumnCount}"
                  RowHeight="{DynamicResource RiseCardHeight}"
                  FlowItemsSource="{Binding Modules}">
                  <flv:FlowListView.FlowColumnTemplate>
                    <OnIdiom
                      x:TypeArguments="DataTemplate">
                      <OnIdiom.Phone>
                        <DataTemplate>
                          <local:ModuleCourseCardPhoneControl>
                            <local:ModuleCourseCardPhoneControl.GestureRecognizers>
                              <TapGestureRecognizer
                                CommandParameter="{Binding}"
                                Command="{Binding ItemSelectedCommand}" />
                            </local:ModuleCourseCardPhoneControl.GestureRecognizers>
                          </local:ModuleCourseCardPhoneControl>
                        </DataTemplate>
                      </OnIdiom.Phone>
                      <OnIdiom.Tablet>
                        <DataTemplate>
                          <local:ModuleCourseCardControl>
                            <local:ModuleCourseCardControl.GestureRecognizers>
                              <TapGestureRecognizer
                                CommandParameter="{Binding}"
                                Command="{Binding ItemSelectedCommand}" />
                            </local:ModuleCourseCardControl.GestureRecognizers>
                          </local:ModuleCourseCardControl>
                        </DataTemplate>
                      </OnIdiom.Tablet>
                    </OnIdiom>
                  </flv:FlowListView.FlowColumnTemplate>
                </flv:FlowListView>
              </StackLayout>
            </Frame>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

模型-代码

public class NewModuleModel
{
    public IList<SectionModel> Sections { get; set; }
    public int ActiviyId { get; set; }
    public string CourseTitle { get; set; }
}

public class SectionModel
{
    public string Title { get; set; }
    public IList<ModulesModel> Modules { get; set; }
    public INavigationService NavigationService { get; set; }
}

public class ModulesModel
{
    public int ModuleId { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string ModuleImageUrl { get; set; }
    public double Progress { get; set; }
    public double RoundedProgress
    {
        get
        {
            var p = Math.Round(Progress * 100, 0);
            return p;
        }
    }
    public DateTime? LastActivityDTUtc { get; set; }
    public DateTime? CompletedDate { get; set; }
    public string EstimatedDuration { get; set; }
    public int AssetCount { get; set; }
    public int? NavigationAssetID { get; set; }
    public int ActiviyId { get; set; }
    public string CourseTitle { get; set; }
    public int? NavigationAssetContentType { get; set; }
    public string Status
    {
        get
        {
            var result = string.Empty;
            if (CompletedDate != null)
            {
                var c = String.Format("{0:d MMMM yyyy}", CompletedDate);
                result = "Completed on " + c;
            }
            else if (LastActivityDTUtc != null)
            {
                var l = String.Format("{0:d MMMM yyyy}", LastActivityDTUtc);
                result = "Last Activity on " + l;
            }
            else
            {
                result = "New";
            }
            return result;
        }
    }


    public ModulesModel()
    {
        
        ItemSelectedCommand = new DelegateCommand<ModulesModel>(this.OnCardItemSelectedCommand);
    }


    private void OnCardItemSelectedCommand(ModulesModel module)
    {
        if (module.NavigationAssetContentType == 28 && module.AssetCount == 1)
        {
            var navigationParams = new NavigationParameters();
            navigationParams.Add("AssetContentId", module.NavigationAssetID);
            navigationParams.Add("AssetActivityId", module.ActiviyId);
            navigationParams.Add("AssetModuleId", module.ModuleId);
            navigationParams.Add("ParamCourseTitle", module.CourseTitle);
            navigationParams.Add("ParamModuleTitle", module.Title);

            Preferences.Set("NavFromModulePage", true);

            NavigationService.NavigateAsync("AssetWebViewPage", navigationParams, true, false).Forget();

        }
        else
        {
            var navigationParams = new NavigationParameters();
            navigationParams.Add("ParamModuleId", module.ModuleId);
            navigationParams.Add("ParamActivityId", module.ActiviyId);
            navigationParams.Add("ParamCourseTitle", module.CourseTitle);

            Preferences.Set("NavFromModulePage", false);

            //NavigationService.NavigateAsync("ModuleAssetPage", navigationParams, true, false);
        }
    }

    public DelegateCommand<ModulesModel> ItemSelectedCommand { get; set; }
}

}

ViewModel-代码

    private async void LoadData()
    {           
            this.ExecuteAsyncTask(async () =>
            {
                var result = await this.ModuleService.GetNewModuleAsync(ActivityID);
                if (result != null)
                {
                    CourseTitle = result.CourseTitle;
                    CourseDescription = result.CourseDescription;
                    CourseImageUrl = result.CourseImageUrl;
                    DurationInSeconds = result.DurationInSeconds;
                    DurationAsReadableString = result.DurationAsReadableString;
                    DisplayDescription = result.DisplayDescription;
                    Progress = Math.Round(result.Progress * 100, 0);
                    StartDateUTC = result.StartDateUTC;
                    EnrolmentDate = result.EnrolmentDate;
                    LastActivityDate = result.LastActivityDate;

                    foreach (var item in result.Sections)
                    {
                        var itemToAdd = new SectionModel
                        {
                            Title = item.Title,                  
                            Modules = item.Modules,
                            NavigationService = navigationService
                        };
                        Device.BeginInvokeOnMainThread(() =>
                        {
                            this.Data.Add(itemToAdd);
                        });
                    }
                }
                else
                {
                    var exception = new Exception($"Api Error");
                }
            });
        }
    }

1 个答案:

答案 0 :(得分:0)

请记住,MVVM是Model-View-ViewModel。我们经常看到人们将模型和ViewModel混淆。我将通过向您展示适当的架构来间接地回答您的问题。首先,我们将看一个ToDoItem模型。

public class ToDoItem
{
    public string Name { get; set; }
    public DateTime Created { get; set; }
    public DateTime Due { get; set; }
}

有了这个注意事项,我们可以使模型继承自Prism的BindableBase或ReactiveUI的ReactiveObject之类,从而使绑定变得更容易。我将在此假设您已经了解如何使这些属性实现INotifyPropertyChanged。

接下来我们需要的是ViewModel,我们将说这是ToDoItemsPageViewModel

public class ToDoItemsPageViewModel : BindableBase
{
    public ObservableCollection<ToDoItem> Items { get; set; }
    public DelegateCommand<ToDoItem> ItemSelectedCommand { get; }

    private async void ItemSelectedCommandExecuted(ToDoItem item)
    {
        // do your navigation here..
    }
}

最后,我们有了ListView或CollectionView(不推荐使用ListView时应使用它)。为简单起见,让我们看以下内容。

<ListView ItemsSource="{Binding Items}">
  <ListView.ItemTemplate>
    <DataTemplate>
      <StackLayout>
        <Label Text="{Binding Name}" />
        <Button Text="Show"
                Command="{Binding ItemSelectedCommand}"
                CommandParameter="{Binding .}" />
      </StackLayout>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

这里要理解的核心思想是ListView共享其父Page的BindingContext。在我们的DataTemplate中,我们的BindingContext是ToDoItem,而不是ToDoItemsPageViewModel。结果,我们对Name值的绑定将返回各个ToDoItem的名称。虽然我们对CommandParameter的绑定将在整个ToDoItem中传递,但由于ToDoItem没有这样的命令,因此我们对Command的绑定将不起作用。

现在让我们看看如何从ViewModel中访问Command。

<ListView ItemsSource="{Binding Items}" x:Name="list">
  <ListView.ItemTemplate>
    <DataTemplate>
      <StackLayout>
        <Label Text="{Binding Name}" />
        <Button Text="Show"
                Command="{Binding BindingContext.ItemSelectedCommand,Source={x:Reference list}}"
                CommandParameter="{Binding .}" />
      </StackLayout>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

我们在这里所做的是三个小的更改。

  • 我们已将x:Name添加到ListView。也可以将其放在父页面上。
  • 我们已将命令绑定从ItemSelectedCommand更改为BindingContext.ItemSelectedCommand
  • 我们已将命令绑定更改为包括一个引用父项(本例中为列表)的Source

这完成的是更新一个特定的绑定以查看父对象。这将BindingContext(针对此单个属性)有效地设置为实际的父级。在这种情况下,您可以将其视为设置为ListView实例的BindingContext。这意味着我们的绑定必须引用BindingContext,然后我们可以在ListView上点进入我们的BindingContext的属性。