第二十四章:页面导航(十四)

简介: 切换到ViewModel此时应该很明显,Information类应该真正实现INotifyPropertyChanged。 在DataTransfer5中,Information类已成为InformationViewModel类。

切换到ViewModel
此时应该很明显,Information类应该真正实现INotifyPropertyChanged。 在DataTransfer5中,Information类已成为InformationViewModel类。 它派生自Xamarin.FormsBook.Toolkit库中的ViewModelBase,以减少过度使用:

public class InformationViewModel : ViewModelBase
{
    string name, email, language;
    DateTime date = DateTime.Today;
    public string Name
    {
        set { SetProperty(ref name, value); }
        get { return name; }
    }
    public string Email
    {
        set { SetProperty(ref email, value); }
        get { return email; }
    }
    public string Language
    {
        set { SetProperty(ref language, value); }
        get { return language; }
    }
    public DateTime Date    
    {
        set { SetProperty(ref date, value); }
        get { return date; }
    }
}

DataTransfer5中添加了一个名为AppData的新类。 该类包括ListView的ObservableCollection信息对象以及信息页面的单独Information实例:

public class AppData
{
    public AppData()
    {
        InfoCollection = new ObservableCollection<InformationViewModel>();
    }
    public IList<InformationViewModel> InfoCollection { private set; get; }
    public InformationViewModel CurrentInfo { set; get; }
}

App类在实例化主页之前实例化AppData并使其可用作公共属性:

public class App : Application
{
    public App()
    {
        // Ensure link to Toolkit library.
        new Xamarin.FormsBook.Toolkit.ObjectToIndexConverter<object>();
        // Instantiate AppData and set property.
        AppData = new AppData();
        // Go to the home page.
        MainPage = new NavigationPage(new DataTransfer5HomePage());
    }
    public AppData AppData { private set; get; }
    __
}

DataTransfer5HomePage的XAML文件为页面设置BindingContext,其绑定包含静态Application.Current属性(返回App对象)和
AppData实例。 这意味着ListView可以将其ItemsSource属性绑定到AppData的Info Collection属性:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataTransfer5.DataTransfer5HomePage"
             Title="Home Page"
             BindingContext="{Binding Source={x:Static Application.Current},            
                                      Path=AppData}">
    <Grid>
        <Button Text="Add New Item"
                Grid.Row="0"
                FontSize="Large"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Clicked="OnGetInfoButtonClicked" />
        <ListView x:Name="listView"
                  Grid.Row="1"
                  ItemsSource="{Binding InfoCollection}"
                  ItemSelected="OnListViewItemSelected">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Label Text="{Binding Name}" />
                            <Label Text=" / " />
                            <Label Text="{Binding Email}" />
                            <Label Text=" / " />
                            <Label Text="{Binding Language}" />
                            <Label Text=" / " />
                            <Label Text="{Binding Date, StringFormat='{0:d}'}" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</ContentPage>

该程序的早期版本依赖于信息中的ToString覆盖来显示项目。 既然Information已被InformationViewModel替换,那么ToString方法就不够了,因为没有通知ToString方法可能会返回一些不同的东西。 相反,ListView使用包含元素的ViewCell,这些元素绑定到InformationViewModel的属性。
代码隐藏文件继续实现Button的Clicked处理程序和ListView的ItemSelected处理程序,但现在它们非常相似,可以使用名为GoToInfoPage的常用方法:

public partial class DataTransfer5HomePage : ContentPage
{
    public DataTransfer5HomePage()
    {
        InitializeComponent();
    }
    // Button Clicked handler.
    void OnGetInfoButtonClicked(object sender, EventArgs args)
    {
        // Navigate to the info page.        
        GoToInfoPage(new InformationViewModel(), true);
    }
    // ListView ItemSelected handler.
    void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args)
    {
        if (args.SelectedItem != null)
        {
            // Deselect the item.
            listView.SelectedItem = null;
            // Navigate to the info page.
            GoToInfoPage((InformationViewModel)args.SelectedItem, false);
        }
    }
    async void GoToInfoPage(InformationViewModel info, bool isNewItem)
    {
        // Get AppData object (set to BindingContext in XAML file).
        AppData appData = (AppData)BindingContext;
        // Set info item to CurrentInfo property of AppData.
        appData.CurrentInfo = info;
        // Navigate to the info page.
        await Navigation.PushAsync(new DataTransfer5InfoPage());
        // Add new info item to the collection.
        if (isNewItem)
        {
            appData.InfoCollection.Add(info);
        }
    }
}

对于这两种情况,GoToInfoPage方法设置AppData的CurrentInfo属性。对于Clicked事件,它被设置为新的InformationViewModel对象。对于ItemSelected事件,它设置为ListView集合中的现有InformationViewModel。 GoToInfoPage方法的isNewItem参数指示是否还应将此InformationViewModel对象添加到AppData的InfoCollection中。
请注意,在PushAsync任务完成后,新项目将添加到InfoCollection中。如果在PushAsync调用之前添加了该项,那么 - 根据平台 - 您可能会注意到此新项突然出现在页面转换之前的ListView中。这可能有点令人不安!
DataTransfer5InfoPage的XAML文件将页面的BindingContext设置为AppData的CurrentInfo属性。 (主页在实例化信息页面之前设置AppData的CurrentInfo属性,因此AppData不必实现INotifyPropertyChanged。)BindingContext的设置允许页面上的所有可视元素绑定到InformationViewModel类中的属性:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="DataTransfer5.DataTransfer5InfoPage"
             Title="Info Page"
             BindingContext="{Binding Source={x:Static Application.Current},
                                     Path=AppData.CurrentInfo}">
 
    <StackLayout Padding="20, 0"
                  Spacing="20">
        <Entry Text="{Binding Name}"
               Placeholder="Enter Name" />
        <Entry Text="{Binding Email}"
               Placeholder="Enter Email Address" />
        <Picker x:Name="languagePicker"
                Title="Favorite Programming Language">
            <Picker.Items>
                <x:String>C#</x:String>
                <x:String>F#</x:String>
                <x:String>Objective C</x:String>
                <x:String>Swift</x:String>
                <x:String>Java</x:String>
            </Picker.Items>
        <Picker.SelectedIndex>
            <Binding Path="Language">
                <Binding.Converter>
                    <toolkit:ObjectToIndexConverter x:TypeArguments="x:String">
                        <x:String>C#</x:String>
                        <x:String>F#</x:String>
                        <x:String>Objective C</x:String>
                        <x:String>Swift</x:String>
                        <x:String>Java</x:String>
                   </toolkit:ObjectToIndexConverter>
                </Binding.Converter>
            </Binding>
       </Picker.SelectedIndex>
    </Picker>
 
    <DatePicker Date="{Binding Date}" />
    </StackLayout>
</ContentPage>

注意在Picker的SelectedIndex属性和InformationViewModel的字符串Language属性之间的绑定中使用ObjectToIndexConverter。 这个绑定转换器在第19章“集合视图”中的“数据绑定选择器”一节中介绍。
DataTransfer5InfoPage的代码隐藏文件实现了MVVM的目标,即只是对InitializeComponent的调用:

public partial class DataTransfer5InfoPage : ContentPage
{
    public DataTransfer5InfoPage()
    {
        InitializeComponent();
    }
}

DataTransfer5的另一个方便的方面是不再需要覆盖OnAppearing和OnDisappearing方法,也不需要在页面导航期间想知道这些方法调用的顺序。
但真正好的是,在程序终止时将DataTransfer5迁移到保存应用程序数据的版本很容易,并在下次运行程序时将其恢复。

目录
相关文章
|
2月前
|
小程序
【微信小程序】-- 页面导航 -- 编程式导航(二十三)
【微信小程序】-- 页面导航 -- 编程式导航(二十三)
|
2月前
|
小程序 API
【微信小程序】-- 页面导航 -- 声明式导航(二十二)
【微信小程序】-- 页面导航 -- 声明式导航(二十二)
|
9月前
|
前端开发
前端学习笔记202305学习笔记第二十二天-菜单路由和面包屑2
前端学习笔记202305学习笔记第二十二天-菜单路由和面包屑2
30 0
|
9月前
|
前端开发
前端学习笔记202305学习笔记第二十二天-菜单路由和面包屑1
前端学习笔记202305学习笔记第二十二天-菜单路由和面包屑1
28 0
|
Android开发 索引 iOS开发
第二十四章:页面导航(十七)
像现实生活中的应用程序理想情况下,用户在终止并重新启动应用程序时不应该知道。应用程序体验应该是连续且无缝的。即使程序没有一直运行,一个半月进入的条目从未完成也应该在一周后处于相同的状态。NoteTaker程序允许用户记录由标题和一些文本组成的注释。
524 0
|
JavaScript 前端开发 Android开发
第二十四章:页面导航(十六)
保存和恢复导航堆栈 许多多页面应用程序的页面体系结构比DataTransfer6更复杂,您需要一种通用的方法来保存和恢复整个导航堆栈。此外,您可能希望将导航堆栈的保存与系统方式集成,以保存和恢复每个页面的状态,特别是如果您不使用MVVM。
464 0
|
XML JSON Android开发
第二十四章:页面导航(十五)
保存和恢复页面状态特别是当您开始使用多页面应用程序时,将应用程序的页面视为数据的主要存储库非常有用,而仅仅是作为底层数据的临时可视化和交互式视图。这里的关键词是暂时的。如果您在用户与之交互时保持基础数据是最新的,那么页面可以显示和消失而不必担心。
629 0
|
Android开发 索引
第二十四章:页面导航(十二)
事件在方法调用方法和消息中心通信方法中,信息页面需要知道主页的类型。 如果可以从不同类型的页面调用相同的信息页面,这有时是不合需要的。这个问题的一个解决方案是info类实现一个事件,这就是DataTransfer3中采用的方法。
491 0
|
Android开发 索引
第二十四章:页面导航(十一)
消息中心您可能不喜欢两个页面类直接相互调用方法的想法。 它似乎适用于小样本,但对于具有大量类间通信的大型程序,您可能更喜欢一些不需要实际页面实例的更灵活的东西。这样的工具是Xamarin.Forms MessagingCenter类。
524 0
|
JavaScript Android开发 索引
第二十四章:页面导航(十)
属性和方法调用调用PushAsync或PushModalAsync的页面显然可以直接访问它导航到的类,因此它可以设置属性或调用该页面对象中的方法以将信息传递给它。但是,调用PopAsync或PopModalAsync的页面还有一些工作要做,以确定它返回的页面。
523 0