【问题标题】:Xamarin.Forms+Prism Navigation target controlXamarin.Forms+Prism 导航目标控件
【发布时间】:2017-03-29 08:20:43
【问题描述】:

我有一个由多种页面类型组成的 Xamarin 应用程序。我正在使用棱镜。

我希望应用程序的导航目标是主页内应用程序中的嵌套视图,而不是主页本身,以便 MainPage 用作始终存在的外框,并且嵌套页面承载应用的导航服务。
通过这种方式,我可以始终存在一些控件(即后退/前进/主页)。 如何将NavigationService 的“MainPage”配置为实际MainPage 中的嵌套页面?
我是 XF 的新手,之前没有使用过 NavigationPage,但它在这里有用吗?

【问题讨论】:

    标签: c# navigation xamarin.forms prism


    【解决方案1】:

    你没有。 Prism 的导航服务将不支持您尝试执行的操作,因为 INavigationService 是基于 Page 的。听起来你的方法很不正统。为什么不直接使用 NavigationPage 并在自定义渲染器或工具栏项中提供您的选项?

    【讨论】:

    • 只是为了澄清这只是我与 XF 合作的一个月,但仍然缺乏很多知识。根据我从您的回答中了解到的情况,我想我有两个选择,要么在所有应用程序页面中嵌入我的工具栏布局的自定义渲染器,要么在嵌入了工具栏的页面上使用自定义页面渲染器或 ControlTemplate。第一个选项需要将工具栏添加到所有页面,而另一个需要为我将使用的所有页面类型(即MasterDetailPageCarouselPage 等)制作自定义渲染器,我做对了吗?如果是,我会选择选项 1。
    • 或者您是否建议我不应该使用 Prism 的导航?我喜欢它,因为我使用 AutoWireViewModel 并注册 ViewModels 以进行导航
    • 做对你有用的事。您可以在任何类型的 BindableObject 上使用 AutowireViewModel
    • K.因此,由于我想坚持使用 Prism 导航,我将选择自定义视图解决方案。
    【解决方案2】:

    Prism 的INavigationService 并非旨在完成您想做的事情。幸运的是自定义视图!您可以按照之前的建议进行操作,但我通常会避免创建自定义页面,而是创建一个拥有自己的内容属性的自定义视图。

    编辑:

    这些术语可能会与 Xamarin Forms 混淆,尤其是如果您是 MVVM 和 Xamarin Forms 的新手,和/或已从其他平台上开发。

    Xamarin Forms 所指的 View 在其他平台上通常被称为 Control。这在使用 Prism 等 MVVM 框架时尤其令人困惑,因为我们的 MVVM Views 实际上是 Xamarin Forms Pages

    虽然有时我们需要创建自定义类型的页面,但通常我们只是实现一些页面类型。例如,假设我有一个要用于登录页面的页面:

    LoginPage.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"
                 Title="{Binding Title}"
                 x:Class="MyAwesomeApp.Views.LoginPage">
    
        <Grid BackgroundColor="{DynamicResource primary}">
            <Grid.RowDefinitions>
                <RowDefinition Height="1*" />
                <RowDefinition Height="3*" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>
            <Grid.Resources>
                <ResourceDictionary>
                    <Style TargetType="StackLayout">
                        <Setter Property="BackgroundColor" Value="White" />
                        <Setter Property="VerticalOptions" Value="FillAndExpand" />
                    </Style>
                    <Style TargetType="Entry">
                        <Setter Property="Margin" Value="40,10" />
                    </Style>
                    <Style TargetType="Button">
                        <Setter Property="Margin" Value="20" />
                        <Setter Property="BackgroundColor" Value="{DynamicResource primaryDark}" />
                        <Setter Property="TextColor" Value="White" />
                        <Setter Property="BorderRadius" Value="5" />
                        <Setter Property="BorderWidth" Value="1" />
                        <Setter Property="BorderColor" Value="Black" />
                    </Style>
                </ResourceDictionary>
            </Grid.Resources>
    
            <Image Source="logo.png" 
                   Aspect="AspectFit"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
    
            <StackLayout Grid.Row="1">
                <Entry Text="{Binding UserName}" 
                       Placeholder="Enter your username"
                       x:Name="userNameEntry"
                       VerticalOptions="EndAndExpand" />
                <Entry Text="{Binding Password}" 
                       Placeholder="Enter your password"
                       IsPassword="True"
                       x:Name="passwordEntry"
                       VerticalOptions="StartAndExpand" />
                <Button Text="Submit"
                        Command="{Binding LoginCommand}" />
            </StackLayout>
    
        </Grid>
    
    </ContentPage>
    

    LoginPage.xaml.cs

    public partial class LoginPage
    {
        public LoginPage()
        {
            InitializeComponent();
        }
    }
    

    虽然 XAML 中发生了很多事情,但这只是一个作为 ContentPage 的页面。无论如何,它不是自定义页面。您会注意到这里后面的代码中绝对没有任何内容。我可能会创建一些事件处理程序,例如,如果用户在键盘上点击 Done/Enter 以转到下一个字段或调用按钮单击,但这仍然不能使其成为自定义页面。

    有关“自定义页面”的示例,您可以查看XLabs ExtendedTabbedPage。您会注意到的一件事是在他们的 TabbedPage 版本上有几个新属性。当然,他们的页面仅因renderer而有效

    继续我已经展示的示例,假设我们想要采用我在页面中创建的登录布局,并希望能够跨项目重用它,或者可能在我的应用程序的不同页面上重用它。然后我想做类似的事情:

    LoginView.xaml

    <?xml version="1.0" encoding="UTF-8"?>
    <Grid xmlns="http://xamarin.com/schemas/2014/forms" 
          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
          BackgroundColor="{DynamicResource primary}"
          x:Name="grid"
          x:Class="MyAwesomeApp.Controls.LoginView">
    
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="3*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <Grid.Resources>
            <ResourceDictionary>
                <Style TargetType="StackLayout">
                    <Setter Property="BackgroundColor" Value="White" />
                    <Setter Property="VerticalOptions" Value="FillAndExpand" />
                </Style>
                <Style TargetType="Entry">
                    <Setter Property="Margin" Value="40,10" />
                </Style>
                <Style TargetType="Button">
                    <Setter Property="Margin" Value="20" />
                    <Setter Property="BackgroundColor" Value="{DynamicResource primaryDark}" />
                    <Setter Property="TextColor" Value="White" />
                    <Setter Property="BorderRadius" Value="5" />
                    <Setter Property="BorderWidth" Value="1" />
                    <Setter Property="BorderColor" Value="Black" />
                </Style>
            </ResourceDictionary>
        </Grid.Resources>
    
        <Image Source="logo.png" 
               Aspect="AspectFit"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    
        <StackLayout Grid.Row="1" BindingContext="{x:Reference grid}">
            <Entry Text="{Binding UserName}" 
                   Placeholder="Enter your username"
                   x:Name="userNameEntry"
                   VerticalOptions="EndAndExpand" />
            <Entry Text="{Binding Password}" 
                   Placeholder="Enter your password"
                   IsPassword="True"
                   x:Name="passwordEntry"
                   VerticalOptions="StartAndExpand" />
            <Button Text="Submit"
                    Command="{Binding LoginCommand}" />
        </StackLayout>
    
    </Grid>
    

    LoginView.xaml.cs

    public partial class LoginView : Grid
    {
        public static readonly BindableProperty UserNameProperty =
            BindableProperty.Create(nameof(UserName), typeof(string), typeof(LoginView), string.Empty);
    
        public static readonly BindableProperty PasswordProperty =
            BindableProperty.Create(nameof(Password), typeof(string), typeof(LoginView), string.Empty);
    
        public static readonly BindableProperty LoginCommandProperty =
            BindableProperty.Create(nameof(LoginCommand), typeof(ICommand), typeof(LoginView), null);
    
        public LoginView()
        {
            InitializeComponent();
        }
    
        public string UserName
        {
            get { return (string)GetValue(UserNameProperty); }
            set { SetValue(UserNameProperty,value); }
        }
    
        public string Password
        {
            get { return (string)GetValue(PasswordProperty); }
            set { SetValue(PasswordProperty, value); }
        }
    
        public ICommand LoginCommand
        {
            get { return (ICommand)GetValue(LoginCommandProperty); }
            set { SetValue(LoginCommandProperty, value); }
        }
    }
    

    重构的 LoginPage.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"
                 xmlns:controls="clr-namespace:MyAwesomeApp.Controls"
                 Title="{Binding Title}"
                 x:Class="MyAwesomeApp.Views.LoginPage">
    
            <controls:LoginView UserName="{Binding UserName}" 
                                Password="{Binding Password}"
                                LoginCommand="{Binding LoginCommand}" />
    </ContentPage>
    

    LoginView 最终得到的实际上是自定义控件。虽然它可能源自网格,但您通常不关心我决定如何创建布局...您只关心我给您一些具有UserNamePassword 属性的控件,您可以附加到还有一些可以执行的LoginCommand。正是这种固有的差异实际上使它成为了自定义视图。

    【讨论】:

    • 您能否解释一下为什么要避免创建自定义页面,以及如何按照您的建议使用自定义视图。谢谢。
    • 参见编辑部分的解释。希望这有助于回答您可能遇到的任何问题。
    • 嘿@Dans。并感谢您花时间写下您的答案。我实际上对 XF 术语非常熟悉,并且知道 XF/Prism 中的 Views 和 Pages 等指的是什么。所以最重要的是,您是否建议使用自定义Page,将其内容呈现在各种工具栏之间作为我应用程序中所有页面的基类?
    • @Shimmy,一点也不!你在说的是一个 View 对象。您可以创建您可以创建一个自定义布局,您可以跨页面重复使用它来完成您想要做的事情。查看 Xamarin Forms Grid、StackLayout 等,了解如何实现。
    • @Dans。我想创建一个继承自 Page 的类,但它的内容被工具栏包围,并且应用程序中的所有页面都继承自它,因此它们的内容被这些工具栏包围。
    【解决方案3】:

    我有一种方法。不完全是你想要的,但可以让你深思。您可以使用SomeContent 属性创建SomeBaseContentPage

    public View SomeContent
    {
      set
      {
        someGrid.Children.Add(value);
      }
    }
    

    然后从SomeBaseContentPage 派生您的SomeContentPageA,并在XAML 中将您的特定内容放入属性&lt;SomeBaseContentPage.SomeContent&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-09
      • 2017-10-06
      • 1970-01-01
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      • 2016-11-21
      • 2017-08-27
      相关资源
      最近更新 更多