1. Варианты макета
Когда дело доходит до проектирования и размещения экранов вашего приложения, у вас есть два основных варианта: написание кода или использование XAML. Если вы когда-либо занимались разработкой WPF (Windows Presentation Foundation) или Silverlight, то вы, вероятно, уже знакомы с XAML. XAML — это расширяемый язык разметки приложений, который был создан, чтобы помочь определить внешний вид приложения без необходимости обрабатывать все это в коде. Xamarin.Forms работает с обоими вариантами. В конечном итоге вам решать, какой вариант вы предпочитаете.
Важно отметить, что XAML, используемый для Xamarin.Forms, не совместим с другими формами инструментов XAML и XAML.
Вариант 1. Использование кода
Если вы тот человек, который любит быть в коде и не хочет иметь ничего общего с какой-либо разметкой или дизайнером, то вам, вероятно, будет очень удобно с этой опцией. Вы программно создаете экземпляры различных типов объектов View
и добавляете их непосредственно на Page
или в Layout
на Page
. Вот простой пример создания класса SimplePage
, создания нескольких объектов View
и добавления их на Page
через объект StackLayout
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public class SamplePage : ContentPage {
public SamplePage()
{
Padding = new Thickness(20);
var label = new Label
{
Text = «I am a simple page»,
BackgroundColor = Color.Blue,
Font = Font.SystemFontOfSize(30),
WidthRequest = 150,
HeightRequest = 40
};
var button = new Button {
Text = «I have a button»,
BackgroundColor = Color.Red,
Font = Font.SystemFontOfSize( 20 ),
WidthRequest = 200,
HeightRequest = 200
};
var entry = new Entry {
Placeholder = «I have a entry box»,
BackgroundColor = Color.Green,
WidthRequest = 200,
HeightRequest = 150
};
Content = new StackLayout {
Spacing = 10,
Children = {button, entry, label}
};
}
}
|
Как вы можете видеть, объекты View
имеют ряд общих свойств, которые вы можете использовать для установки текста, цветов, интервалов, высоты, ширины и т. Д. Все, что вам нужно сделать сейчас, — это изменить метод GetMainPage
в App
класс, чтобы вернуть новый экземпляр класса SamplePage
, и все SamplePage
.
Никто никогда не обвинял меня в том, что я дизайнер, но так просто создавать базовые страницы в коде.
Вариант 2. Использование XAML
Если вы предпочитаете отделять внешний вид вашего приложения от логики и реализации, тогда XAML может быть просто подходом. XAML позволяет вам создавать весь макет вашего приложения в специализированном формате XML, который Xamarin может преобразовывать в страницы, макеты, представления и ячейки, и отображать их пользователю. Если вы никогда раньше не использовали XAML, это может занять некоторое время. Однако, как только вы это освоите, это может быть довольно приятно.
Чтобы использовать XAML в сочетании с Xamarin.Forms, вам необходимо создать свой проект с помощью шаблона Blank App (Xamarin.Forms Portable), чтобы весь код Xamarin.Forms можно было разделить на его собственную dll.
В примере кода предыдущего раздела вы создали в коде очень простой класс ContentPage
. Чтобы создать тот же ContentPage
с использованием XAML, щелкните правой кнопкой мыши проект PCL и выберите « Добавить»> «Новый элемент» . В диалоговом окне « Добавить новый элемент » выберите шаблон страницы Forms Xaml и замените содержимое по умолчанию следующим текстом :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?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=»SampleFormsXAMLApp.SampleXAMLPage»
Padding=»30″>
<StackLayout Spacing=»10″>
<Label Text=»I am a simple Page»
BackgroundColor=»Blue»
Font=»30″
WidthRequest=»150″
HeightRequest=»40″/>
<Button Text=»I have a button»
BackgroundColor=»Red»
Font=»20″
WidthRequest=»200″
HeightRequest=»200″/>
<Entry Placeholder=»I have a entry box»
BackgroundColor=»Green»
WidthRequest=»200″
HeightRequest=»150″/>
</StackLayout>
</ContentPage>
|
Если вы запустите свое приложение, вы должны увидеть тот же экран, что и в примере кода. Типы Page
, Layout
и View
отображаются на элементы XML, а свойства являются атрибутами элемента. Вы можете использовать любую опцию для создания полностью настраиваемых кроссплатформенных пользовательских интерфейсов.
2. Гибкость благодаря привязке данных
Вы можете создавать приложения, в которых вы создаете объекты View
для своих объектов Page
и явно устанавливаете их свойства, но это быстро становится громоздким. Когда вы явно устанавливаете свойства в своем коде XAML, вы больше не можете повторно использовать эту Page
XAML для чего-либо еще. Другими словами, вы должны создавать новые страницы XAML для каждого варианта, который вам нужен. У кого есть время для этого?
Разве не было бы неплохо, если бы вы могли создавать повторно используемые страницы XAML без кода пользовательского интерфейса и хранить все логически разделенными? Конечно. Добро пожаловать в MVVM.
4. Модель-Вид-ВидМодель
Model-View-ViewMode l — это архитектурный шаблон, созданный с учетом XAML. По своей сути он разделяет основную концепцию других архитектурных шаблонов, таких как MVP и MVC. Он был разработан для отделения данных, уровня модели, от представления и уровня представления. Каналом между ними является ViewModel . Модель представления — это класс, который облегчает связь между уровнями модели и представления посредством механизма, известного как привязка данных . Привязка данных лежит в основе шаблона MVVM и осуществляется через сам XAML. Давайте посмотрим на пример.
5. Создание примера приложения
Начните с создания нового приложения Xamarin.Forms, выбрав шаблон проекта Blank App (Xamarin.Forms Portable) и присвоив ему имя MyRecipeBox .
Как вы уже, наверное, догадались, это будет основой для базового приложения, которое может хранить рецепты. Начнем с создания базовой модели приложения, рецепта.
В проекте MyRecipeBox создайте новую папку и назовите ее « Модели» . Это не является обязательным требованием, оно просто добавляет в проект некоторую организацию, которая всегда помогает по мере роста. В папке Models добавьте новый класс и назовите его Recipe
. Замените реализацию по умолчанию следующим:
1
2
3
4
5
6
7
8
|
public class Recipe
{
public string Name { get;
public string Description { get;
public TimeSpan PrepTime { get;
public TimeSpan CookingTime { get;
public List<string> Directions { get;
}
|
Теперь, когда у вас есть базовый класс модели, вы можете создать для него модель представления. Думайте о модели представления как о классе, который содержит части модели, которые должны отображаться и взаимодействовать на экране. Для простоты мы сосредоточимся на четырех лучших свойствах.
Создайте новую папку в проекте MyRecipeBox и назовите ее ViewModels . В папке ViewModels создайте новый класс и назовите его RecipeViewModel
. При принятии шаблона MVVM в .NET для ViewModels обычно характерно то, что они реализуют интерфейс INotifyPropertyChanged
. Этот интерфейс используется для того, чтобы позволить другим частям кода подписаться на события и включить привязку данных. Замените реализацию по RecipeViewModel
класса RecipeViewModel
следующим:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
public class RecipeViewModel : INotifyPropertyChanged {
private Recipe _recipe;
public event PropertyChangedEventHandler PropertyChanged;
public RecipeViewModel( Recipe recipe ) {
_recipe = recipe;
Directions = new ObservableCollection<string>(_recipe.Directions);
}
public ObservableCollection<string> Directions { get;
public string Name {
get { return _recipe != null ?
set {
if ( _recipe != null ) {
_recipe.Name = value;
if ( PropertyChanged != null ) {
PropertyChanged( this, new PropertyChangedEventArgs( «Name» ) );
}
}
}
}
public string Description {
get { return _recipe != null ?
set {
if ( _recipe != null ) {
_recipe.Description = value;
if ( PropertyChanged != null ) {
PropertyChanged( this, new PropertyChangedEventArgs( «Description» ) );
}
}
}
}
public string PrepTime {
get { return _recipe != null ?
set {
if ( _recipe != null ) {
_recipe.PrepTime = TimeSpan.Parse(value);
if ( PropertyChanged != null ) {
PropertyChanged(this, new PropertyChangedEventArgs(«PrepTime»));
}
}
}
}
public string CookingTime {
get { return _recipe != null ?
set {
if ( _recipe != null ) {
_recipe.CookingTime = TimeSpan.Parse(value);
if ( PropertyChanged != null ) {
PropertyChanged(this, new PropertyChangedEventArgs(«CookingTime»));
}
}
}
}
}
|
Возможно, вы заметили, что RecipeViewModel
реализует интерфейс RecipeViewModel
. Если вы углубитесь в этот интерфейс, вы увидите, что он содержит одно свойство, которое необходимо реализовать.
1
2
3
4
|
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
|
Класс RecipleViewModel
принимает экземпляр класса Recipe
а затем предоставляет только четыре его свойства. Получатели, связанные с этими свойствами, просто возвращают данные в самом экземпляре Recipe
. С другой стороны, сеттеры проверяют, не является ли PropertyChanged
null
. PropertyChanged
будет null
если на это событие нет подписчиков. В этом случае ничего не происходит. Если PropertyChanged
не имеет значение null
, вызывается событие, и каждый подписчик события получает информацию об изменении этой модели представления.
В шаблоне MVVM подписчиком этих событий обычно является представление, описанное XAML, позволяющее пользовательскому интерфейсу обновляться, если базовые модели изменились.
Пришло время создать страницу, которая показывает пользователю данные рецепта и использует привязку данных для обновления пользовательского интерфейса. Начните с создания папки Views в проекте MyRecipeBox . В папке Views добавьте новый Формирует страницу Xaml и назовите ее RecipeSummaryPage
.
Замените XAML по умолчанию в файле следующим:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
<?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=»MyRecipeBox.Views.RecipeSummaryPage»
Title=»{Binding Name}»
Padding=»30″>
<StackLayout Spacing=»10″ VerticalOptions=»FillAndExpand»>
<Label Text=»Recipe Name» TextColor=»Red»/>
<Label Text=»{Binding Name}» />
<Label Text=»Recipe Description» TextColor=»Red»/>
<Label Text=»{Binding Description}»/>
<Label Text=»Total Prep Time» TextColor=»Red»/>
<Label Text=»{Binding PrepTime}»/>
<Label Text=»Total Cook Time» TextColor=»Red»/>
<Label Text=»{Binding CookTime}»/>
<Label Text=»Directions» TextColor=»Red»/>
<ListView ItemsSource=»{Binding Directions}»>
</StackLayout>
</ContentPage>
|
Как вы можете видеть, привязка создается путем размещения некоторого форматированного текста там, где вы хотите, чтобы появлялись связанные данные. Синтаксис для выполнения этого "{Binding xxxxx}"
, где xxxxx
— это имя свойства, с которым вы хотите связать. Наконец, вам может быть интересно, как вы связываете созданную модель представления с этим представлением.
Если вы нажмете маленькую стрелку рядом с файлом RecipeSummaryPage.xaml , вы увидите, что появится другой файл, RecipleSummaryPage.xaml.cs . Это код файла, который содержит код C # для запуска этой страницы. Вам нужно изменить конструктор этого класса, чтобы он выглядел так:
1
2
3
4
5
6
|
public RecipeSummaryPage(RecipeViewModel recipeViewModel)
{
InitializeComponent();
this.BindingContext = recipeViewModel;
}
|
Свойство BindingContext
— это место, где вам нужно назначить модель представления для создания вышеупомянутой привязки. Для этого передайте экземпляр вашего RecipeViewModel
в конструктор.
Чтобы увидеть результаты нашего труда на экране, вам нужно сделать одно небольшое изменение, чтобы это сработало. В файле App.cs в проекте MyRecipeBox обновите метод GetMainPage
как показано ниже.
01
02
03
04
05
06
07
08
09
10
|
public static Page GetMainPage() {
var recipe = new Recipe {
Name = «Toast»,
Description = «It’s toast, are you kidding?»,
PrepTime = new TimeSpan( 0, 0, 15 ),
CookingTime = new TimeSpan( 0, 2, 0 ),
Directions = new List<string>{«Grab bread», «Put bread in toaster», «Eat toast»}
};
return new RecipeSummaryPage( new RecipeViewModel( recipe ) );
}
|
Результат должен выглядеть примерно так, как показано на следующих скриншотах.
На следующем и последнем шаге мы создадим и отобразим список объектов Recipe
которые пользователь может щелкнуть, чтобы перенести их на страницу с подробностями. Начнем с создания новой модели представления, содержащей список объектов Recipe
. Добавьте новый класс в папку ViewModels и назовите его RecipeListViewModel
. Его реализация выглядит так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
public class RecipeListViewModel
{
public ObservableCollection<Recipe> Recipes { get;
public RecipeListViewModel( ) {
Recipes = new ObservableCollection<Recipe>();
Recipes.Add(new Recipe {
Name = «Toast»,
Description = «Are you kidding? It’s toast.»,
CookingTime = new TimeSpan(0, 2, 0),
PrepTime = new TimeSpan(0, 0, 15),
Directions = new List<string> {
«Pick up bread»,
«Put break in toaster»,
«Eat Toast»
}
});
Recipes.Add(new Recipe
{
Name = «Cereal»,
Description = «You know, the breakfast stuff.»,
CookingTime = TimeSpan.Zero,
PrepTime = new TimeSpan(0, 1, 0),
Directions = new List<string> {
«Put cereal in bowl»,
«Put milk in bowl»,
«Put spoon in bowl»,
«Put spoon in mouth»
}
});
Recipes.Add(new Recipe
{
Name = «Sandwich»,
Description = «Bread and stuff. YUM!»,
CookingTime = TimeSpan.Zero,
PrepTime = new TimeSpan(0, 5, 0),
Directions = new List<string> {
«Get 2 slices of bread»,
«Put cheese between break slices»,
«Put ham between break slices»,
«Enjoy»
}
});
}
}
|
Возможно, вы заметили, что мы жестко закодировали рецепты в классе RecipeListViewModel
. В реальном приложении рецепты будут получены из веб-службы или базы данных.
Создайте новую страницу для отображения списка рецептов. В папке Views создайте новую страницу формы Xaml и назовите эту RecipleListPage
. Замените его содержимое следующим:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<?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=»MyRecipeBox.Views.RecipeListPage»
Title=»Recipes»>
<ListView x:Name=»recipeList» ItemsSource=»{Binding Recipes}» ItemTapped=»OnItemSelected»>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text=»{Binding Name}»/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
|
Этот XAML очень похож на предыдущий пример. Однако на этот раз у вас есть только представление списка на странице. При использовании привязки данных в ListView
вам нужно немного углубиться, чтобы выполнить фактическую привязку. Сначала вы связываете полный список со свойством ItemsSource
объекта ListView
а затем вам нужно определить Template
и DataTemplate
объекта ListView
как TextCell
и связать этот TextCell
с отдельным свойством экземпляра Recipe
вы хотите отобразить. Это то, что выводит названия рецептов на экран.
Вы также можете видеть, что есть Name
связанное с ListView
, recipeList
, который пригодится чуть позже, а также обработчик событий. В этом случае, когда пользователь касается элемента в ListView
, ItemTapped
событие ItemTapped
. Вы подписались на это событие и будете использовать метод с именем OnItemSelected
для его обработки.
На следующем шаге нам нужно выполнить некоторую разводку в файле RecipeListPage.xaml.cs, чтобы установить BindingContext
нашей новой страницы, а также реализовать OnItemSelected
события OnItemSelected
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public partial class RecipeListPage
{
public RecipeListPage()
{
InitializeComponent();
this.BindingContext = new RecipeListViewModel( );
}
public void OnItemSelected(object sender, ItemTappedEventArgs args)
{
var recipe = args.Item as Recipe;
if (recipe == null)
return;
Navigation.PushAsync(new RecipeSummaryPage(new RecipeViewModel(recipe)));
// Reset the selected item
recipeList.SelectedItem = null;
}
}
|
Свойство BindingContext
будет просто установлено на новый экземпляр RecipleListViewModel
который вы создали ранее. Метод обработчика событий немного отличается. Во-первых, вам нужно проверить, что выбранный предмет является рецептом, что выполняется в следующих строках:
1
2
3
|
var recipe = args.Item as Recipe;
if (recipe == null)
return;
|
Если выбранный элемент является объектом Recipe
, то вы используете свойство Navigation
для добавления нового экземпляра RecipleSummaryPage
в текущий NavigationView
. Наконец, вам нужно убедиться, что ни один элемент в списке не выбран.
1
2
3
|
Navigation.PushAsync(new RecipeSummaryPage(new RecipeViewModel(recipe)));
// Reset the selected item
recipeList.SelectedItem = null;
|
Доступ к ListView
осуществляется через Name
которое было ему присвоено ранее. Вы можете получить доступ к любому View
на странице, назначив Name
View
и ссылаясь на него по имени в коде.
Последнее изменение, которое нам нужно сделать, — обновить метод GetMainPage в файле App.cs. как показано ниже:
1
2
3
|
public static Page GetMainPage() {
return new NavigationPage(new RecipeListPage());
}
|
Вы возвращаете новый экземпляр класса NavigationPage
качестве главной страницы и устанавливаете для его корневой страницы новый экземпляр класса RecipleListPage
. Теперь, когда все подключено, вы можете запустить свое приложение на всех трех платформах и увидеть что-то вроде следующего:
Нажав на одну из строк в списке, вы попадете на страницу с кратким описанием рецепта, как вы видели раньше.
Вывод
Теперь вы видели различные варианты размещения вашего приложения с использованием Xamarin.Forms. Вам должно быть удобно создавать базовые приложения, которые могут работать на основных мобильных платформах, используя единую кодовую базу как для бизнес-логики, так и для пользовательского интерфейса приложения. Когда вы потратили некоторое время на работу с Xamarin.Forms, следующим шагом будет изучение того, как настроить пользовательский интерфейс приложения и добавить новые элементы управления. Но это на другой день.
Следующий шаг: смотреть курс
Если вы хотите узнать больше о Xamarin, ознакомьтесь с нашим курсом Создание многоплатформенных приложений на C # в Xamarin .
В ходе курса вы узнаете, как создать кроссплатформенное приложение из единой кодовой базы, которая будет работать на трех совершенно разных платформах: iOS, Android и Windows Phone 8. Думаешь, это невозможно? Через некоторое время вы будете делать это самостоятельно. Давай приступим к работе.
Вы можете воспользоваться бесплатной 14-дневной пробной версией подписки Tuts +. Для начала ознакомьтесь с нашими вариантами подписки или, если вы заинтересованы в этом курсе, вы можете приобрести его отдельно за 15 долларов! Вот предварительный просмотр, чтобы вы начали: