Xamarin навигация: Часть 2 (Xamarin navigation)

В этом кратком руководстве рассматриваются следующие темы:

  • Добавление дополнительных страниц в приложение Оболочки в Xamarin.Forms.
  • Xamarin навигация между страницами.
  • Использование привязки данных для синхронизации данных между элементами пользовательского интерфейса и их источником данных.

В этом кратком руководстве описано, как на основе кроссплатформенного приложения Оболочки в Xamarin.Forms, которое может хранить одну заметку, создать приложение, позволяющее хранить несколько заметок. Ниже показано итоговое приложение:

Обновление приложения с помощью Visual Studio

1) Запустите Visual Studio. В начальном окне щелкните решение Notes в списке недавних проектов/решений или элемент Открыть проект или решение, а затем в диалоговом окне Открыть проект/решение выберите файл решения для проекта Notes:

2) В обозревателе решений щелкните проект Notes правой кнопкой мыши и выберите Добавить > Новая папка.

3) В обозревателе решений присвойте новой папке имя Models:

4) В обозревателе решений выберите папку Models, щелкните правой кнопкой мыши и выберите Добавить > Класс...:

5) В диалоговом окне Добавление нового элемента выберите Элементы Visual C# > Класс, назовите новый файл Note и нажмите кнопку Добавить:

Это приведет к добавлению класса с именем Note в папку Models проекта Notes.

6) Удалите в файле Note.cs весь код шаблона и замените его приведенным ниже:

using System;


namespace Notes.Models
{
    public class Note
    {
        public string Filename { get; set; }
        public string Text { get; set; }
        public DateTime Date { get; set; }
    }
}

Этот класс определяет модель Note, где будут храниться данные о каждой заметке в этом приложении.

Сохраните изменения в Note.cs, нажав клавиши CTRL+S.

7) В обозревателе решений в проекте Notes выберите папку Views, щелкните ее правой кнопкой и выберите Добавить > Новый элемент.... В диалоговом окне Добавление нового элемента выберите Элементы Visual C# >Xamarin.Forms> Страница содержимого, присвойте новому файлу имя NoteEntryPage и нажмите кнопку Добавить:

В результате этого будет добавлена новая страница с именем NoteEntryPage в папку Views проекта. Эта страница будет использоваться для записи заметки.

8) В NoteEntryPage.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="Notes.Views.NoteEntryPage" title="Note Entry">
    <!-- Layout children vertically -->
    <stacklayout margin="20">
        <editor placeholder="Enter your note" text="{Binding Text}" heightrequest="100"></editor>
        <!-- Layout children in two columns -->
        <grid columndefinitions="*,*">
            <button text="Save" clicked="OnSaveButtonClicked"></button>
            <button grid.column="1" text="Delete" clicked="OnDeleteButtonClicked"></button>
        </grid>
    </stacklayout>
</contentpage>

Этот код декларативно определяет пользовательский интерфейс для страницы, который состоит из Editor для ввода текста, а также двух объектов Button, которые предписывают приложению сохранить или удалить файл. Два экземпляра Button располагаются по горизонтали в Grid, а Editor и Grid — по вертикали в StackLayout. Кроме того, Editor использует привязку данных для привязки к свойству Text модели Note.

Сохраните изменения в файле NoteEntryPage.xaml, нажав клавиши CTRL+S.

9) Удалите из NoteEntryPage.xaml.cs весь шаблонный код и замените его приведенным ниже.

using System;
using System.IO;
using Notes.Models;
using Xamarin.Forms;


namespace Notes.Views
{
    [QueryProperty(nameof(ItemId), nameof(ItemId))]
    public partial class NoteEntryPage : ContentPage
    {
         public string ItemId
         {
            set
            {
                LoadNote(value);
            }
         }


    public NoteEntryPage()
    {
          InitializeComponent();


           // Set the BindingContext of the page to a new Note.
           BindingContext = new Note();
    }


     void LoadNote(string filename)
     {
         try
         {
         // Retrieve the note and set it as the BindingContext of the page.
         Note note = new Note
         {
            Filename = filename,
            Text = File.ReadAllText(filename),
            Date = File.GetCreationTime(filename)
         };
            BindingContext = note;
         }
         catch (Exception)
         {
             Console.WriteLine("Failed to load note.");
         }
      }


      async void OnSaveButtonClicked(object sender, EventArgs e)
      {
         var note = (Note)BindingContext;


         if (string.IsNullOrWhiteSpace(note.Filename))
         {
             // Save the file.
            var filename = Path.Combine(App.FolderPath, $"{Path.GetRandomFileName()}.notes.txt");
            File.WriteAllText(filename, note.Text);
         }
         else
         {
             // Update the file.
             File.WriteAllText(note.Filename, note.Text);
         }


         // Navigate backwards
         await Shell.Current.GoToAsync("..");
       }


        async void OnDeleteButtonClicked(object sender, EventArgs e)
        {
            var note = (Note)BindingContext;


            // Delete the file.
            if (File.Exists(note.Filename))
            {
                File.Delete(note.Filename);
            }


            // Navigate backwards
            await Shell.Current.GoToAsync("..");
          }
     }
}

Этот код сохраняет экземпляр Note, представляющий одну заметку, в BindingContext страницы. Класс снабжен атрибутом QueryPropertyAttribute, что позволяет передавать данные на страницу во время навигации с помощью параметров запроса. Первый аргумент в QueryPropertyAttribute указывает имя свойства, которое будет получать данные, а второй аргумент задает идентификатор параметра запроса. Таким образом, атрибут QueryParameterAttribute в коде выше указывает, что свойство ItemId будет получать данные, переданные в параметр запроса ItemId из URI, указанного в вызове метода GoToAsync. Затем свойство ItemId вызывает метод LoadNote для создания объекта Note из файла на устройстве и задает для BindingContext страницы объект Note.

При нажатии кнопки СохранитьButton выполняется обработчик событий OnSaveButtonClicked, который сохраняет содержимое Editor либо в новый файл со случайно сформированным именем, либо в существующий файл, если заметка обновляется. В обоих случаях файл хранится в локальной папке данных для этого приложения. Затем метод осуществляет возврат на предыдущую страницу. При нажатии кнопки Удалить Button выполняется обработчик событий OnDeleteButtonClicked, который удаляет файл, если он существует, и осуществляет возврат на предыдущую страницу.

Сохраните изменения в файле NoteEntryPage.xaml.cs, нажав клавиши CTRL+S.

В данный момент сборка приложения не будет выполнена из-за ошибок, которые будут исправлены в последующих шагах.

10) В обозревателе решений в проекте Notes откройте NotesPage.xaml в папке Views.

11) Удалите из NotesPage.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="Notes.Views.NotesPage" title="Notes">
    <!-- Add an item to the toolbar -->
    <contentpage.toolbaritems>
        <toolbaritem text="Add" clicked="OnAddClicked"></toolbaritem>
    </contentpage.toolbaritems>


    <!-- Display notes in a list -->
    <collectionview x:name="collectionView" margin="20" selectionmode="Single" selectionchanged="OnSelectionChanged">
        <collectionview.itemslayout>
            <linearitemslayout orientation="Vertical" itemspacing="10"></linearitemslayout>
        </collectionview.itemslayout>
        <!-- Define the appearance of each item in the list -->
        <collectionview.itemtemplate>
            <datatemplate>
                <stacklayout>
                    <label text="{Binding Text}" fontsize="Medium"></label>
                    <label text="{Binding Date}" textcolor="Silver" fontsize="Small"></label>
                </stacklayout>
            </datatemplate>
        </collectionview.itemtemplate>
    </collectionview>
</contentpage>

Этот код декларативно определяет пользовательский интерфейс для страницы, который состоит из CollectionView и ToolbarItem. CollectionView использует привязку данных для отображения заметок, полученных приложением. При выборе заметки будет выполнен переход к NoteEntryPage, в которой можно изменить заметку. Кроме того, можно создать заметку, нажав кнопку ToolbarItem.

Сохраните изменения в файле NotesPage.xaml, нажав клавиши CTRL+S.

12) В обозревателе решений в проекте Notes разверните NotesPage.xaml в папке Views и откройте NotesPage.xaml.cs.

13) Удалите из NotesPage.xaml.cs весь шаблонный код и замените его приведенным ниже.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Notes.Models;
using Xamarin.Forms;


namespace Notes.Views
{
    public partial class NotesPage : ContentPage
    {
        public NotesPage()
    {
    InitializeComponent();
 }


  protected override void OnAppearing()
  {
      base.OnAppearing();


      var notes = new List();


      // Create a Note object from each file.
      var files = Directory.EnumerateFiles(App.FolderPath, "*.notes.txt");
      foreach (var filename in files)
      {
          notes.Add(new Note
          {
              Filename = filename,
              Text = File.ReadAllText(filename),
              Date = File.GetCreationTime(filename)
           });
       }


        // Set the data source for the CollectionView to a
        // sorted collection of notes.
        collectionView.ItemsSource = notes
        .OrderBy(d => d.Date)
        .ToList();
    }


     async void OnAddClicked(object sender, EventArgs e)
     {
         // Navigate to the NoteEntryPage, without passing any data.
         await Shell.Current.GoToAsync(nameof(NoteEntryPage));
     }


      async void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
      {
          if (e.CurrentSelection != null)
          {
              // Navigate to the NoteEntryPage, passing the filename as a query parameter.
              Note note = (Note)e.CurrentSelection.FirstOrDefault();
              await Shell.Current.GoToAsync($"{nameof(NoteEntryPage)}?{nameof(NoteEntryPage.ItemId)}={note.Filename}");
           }
       }
   }
}

Этот код определяет функциональные возможности для NotesPage. При отображении страницы выполняется метод OnAppearing, который заполняет CollectionView всеми заметками, полученными из локальной папки данных приложений. При нажатии кнопки ToolbarItem запускается обработчик событий OnAddClicked. Этот метод переходит к NoteEntryPage. При выборе элемента в CollectionView запускается обработчик событий OnSelectionChanged. Этот метод переходит к NoteEntryPage при условии, что выбран элемент CollectionView, передавая свойство Filename выбранного Note в качестве параметра запроса на страницу.

Сохраните изменения в файле NotesPage.xaml.cs, нажав клавиши CTRL+S.

В данный момент сборка приложения не будет выполнена из-за ошибок, которые будут исправлены в последующих шагах.

14) В обозревателе решений в проекте Notes разверните AppShell.xaml и откройте AppShell.xaml.cs. Затем замените существующий код следующим:

using Notes.Views;
using Xamarin.Forms;


namespace Notes
{
    public partial class AppShell : Shell
    {
        public AppShell()
    {
    InitializeComponent();
    Routing.RegisterRoute(nameof(NoteEntryPage), typeof(NoteEntryPage));
}

Этот код регистрирует маршрут для NoteEntryPage, который не представлен в визуальной иерархии Оболочки (AppShell.xaml). Затем на этой странице можно перейти к использованию навигации на основе URI с помощью метода GoToAsync.

Сохраните изменения в файле AppShell.xaml.cs, нажав клавиши CTRL+S.

15) В обозревателе решений разверните App.xaml и откройте App.xaml.cs в проекте Notes. Затем замените существующий код следующим:

using System;
using System.IO;
using Xamarin.Forms;


namespace Notes
{
    public partial class App : Application
    {
        public static string FolderPath { get; private set; }


        public App()
        {
            InitializeComponent();
            FolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
            MainPage = new AppShell();
        }


        protected override void OnStart()
        {
        }


        protected override void OnSleep()
        {
        }


        protected override void OnResume()
        {
        }
    }
}

Этот код добавляет объявление пространства имен System.IO и объявление для статического свойства FolderPath типа string. Свойство FolderPath используется для хранения пути на устройстве, где будут храниться данные заметки. Кроме того, код инициализирует свойство FolderPath в конструкторе App, а также инициализирует свойство MainPage в производном объекте Shell.

Сохраните изменения в файле App.xaml.cs, нажав клавиши CTRL+S.

16) Создайте и запустите проект на каждой соответствующей платформе.

На странице NotesPage нажмите кнопку Добавить, чтобы перейти к странице NoteEntryPage и ввести заметку. После сохранения заметки приложение вернется на страницу NotesPage.

Введите несколько заметок разной длины, чтобы понаблюдать за поведением приложения. Закройте приложение и повторно запустите его, чтобы проверить, сохранены ли на устройстве введенные заметки.

Дальнейшие действия

В этом кратком руководстве рассматривались следующие темы:

  • Добавление дополнительных страниц в приложение Оболочки в Xamarin.Forms.
  • Навигация между страницами.
  • Использование привязки данных для синхронизации данных между элементами пользовательского интерфейса и их источником данных.

Перейдите к следующему краткому руководству, чтобы изменить приложение таким образом, чтобы оно сохраняло свои данные в локальной базе данных SQLite.NET.

Материалы по теме