Продолжим проект из прошлой темы и создадим для него графический интерфейс. Итак, по умолчанию у нас уже есть страница MainPage.xaml, которую мы будем использовать для вывода списка друзей. И также добавим в проект новую страницы XAML, которую назовем FriendPage.xaml и которая будет применяться для добавления/изменения друга.
И также добавим в проект класс ApplicationViewModel, через который будет идти взаимодействие графического интерфейса и веб-сервера:
using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Input; using Xamarin.Forms; using System.Linq; using System.Threading.Tasks; namespace HelloApp { public class ApplicationViewModel : INotifyPropertyChanged { bool initialized = false; // была ли начальная инициализация Friend selectedFriend; // выбранный друг private bool isBusy; // идет ли загрузка с сервера public ObservableCollection<Friend> Friends { get; set; } FriendsService friendsService = new FriendsService(); public event PropertyChangedEventHandler PropertyChanged; public ICommand CreateFriendCommand { protected set; get; } public ICommand DeleteFriendCommand { protected set; get; } public ICommand SaveFriendCommand { protected set; get; } public ICommand BackCommand { protected set; get; } public INavigation Navigation { get; set; } public bool IsBusy { get { return isBusy; } set { isBusy = value; OnPropertyChanged("IsBusy"); OnPropertyChanged("IsLoaded"); } } public bool IsLoaded { get { return !isBusy; } } public ApplicationViewModel() { Friends = new ObservableCollection<Friend>(); IsBusy = false; CreateFriendCommand = new Command(CreateFriend); DeleteFriendCommand = new Command(DeleteFriend); SaveFriendCommand = new Command(SaveFriend); BackCommand = new Command(Back); } public Friend SelectedFriend { get { return selectedFriend; } set { if (selectedFriend != value) { Friend tempFriend = new Friend() { Id =value.Id, Name = value.Name, Email = value.Email, Phone = value.Phone }; selectedFriend = null; OnPropertyChanged("SelectedFriend"); Navigation.PushAsync(new FriendPage(tempFriend, this)); } } } protected void OnPropertyChanged(string propName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propName)); } private async void CreateFriend() { await Navigation.PushAsync(new FriendPage(new Friend(), this)); } private void Back() { Navigation.PopAsync(); } public async Task GetFriends() { if (initialized == true) return; IsBusy = true; IEnumerable<Friend> friends = await friendsService.Get(); // очищаем список //Friends.Clear(); while (Friends.Any()) Friends.RemoveAt(Friends.Count - 1); // добавляем загруженные данные foreach (Friend f in friends) Friends.Add(f); IsBusy = false; initialized = true; } private async void SaveFriend(object friendObject) { Friend friend = friendObject as Friend; if (friend != null) { IsBusy = true; // редактирование if (friend.Id>0) { Friend updatedFriend = await friendsService.Update(friend); // заменяем объект в списке на новый if (updatedFriend != null) { int pos = Friends.IndexOf(updatedFriend); Friends.RemoveAt(pos); Friends.Insert(pos, updatedFriend); } } // добавление else { Friend addedFriend = await friendsService.Add(friend); if(addedFriend!=null) Friends.Add(addedFriend); } IsBusy = false; } Back(); } private async void DeleteFriend(object friendObject) { Friend friend = friendObject as Friend; if (friend != null) { IsBusy = true; Friend deletedFriend = await friendsService.Delete(friend.Id); if(deletedFriend!=null) { Friends.Remove(deletedFriend); } IsBusy = false; } Back(); } } }
В этом классе определяется коллекция Friends, где будут храниться все друзья, а также переменная friendsService для взаимодействия с сервером.
Также в классе определяется ряд команд и свойство IsBusy, которое будет указывать, находятся ли данные в процессе загрузки.
В конструкторе определяем пустой список Friends и команды
Начальные данные в список Friends загружаются дс помощью метода GetFriends()
. В этом методе GetFriends()
идет обращение
к серверу через метод Get()
объекта friendsService. Но в данном случае мы будем загружать данные только один раз - при старте приложения. И для
этого в классе определен флаг initialized, который приобретает значение true после начальной загрузки.
Через свойство SelectedFriend
мы будем отслеживать выделенный элемент в списке. Когда пользователь будет выбирать какой-то объект в списке,
то будет происходить изменение этого свойства, будет создаваться копия выделенного друга, который будет передаваться для редактирования/удаления на
страницу FriendPage:
if (selectedFriend != value) { Friend tempFriend = new Friend() { Id =value.Id, Name = value.Name, Email = value.Email, Phone = value.Phone }; selectedFriend = null; OnPropertyChanged("SelectedFriend"); Navigation.PushAsync(new FriendPage(tempFriend, this)); }
Команда CreateFriendCommand будет вызывать метод CreateFriend()
, который будет передавать новый объект на страницу FriendPage:
private async void CreateFriend() { await Navigation.PushAsync(new FriendPage(new Friend(), this)); }
Команда сохранения SaveFriendCommand будет получать созданный/отредактированный объект Friend и в зависимости от режима (добавление или редактирование) использовать один из методов класса FriendService для отправки запроса.
Так как наш веб-сервис, который был создан в позапрошлой теме, при добавлении или изменении возвращает добавленный/измененный объект, то, получв этот объект от сервера, мы можем его добавить в коллекцию Friends или изменить элемент в этой коллекции.
Изменение:
Friend updatedFriend = await friendsService.Update(friend); // заменяем объект в списке на новый if (updatedFriend != null) { int pos = Friends.IndexOf(updatedFriend); Friends.RemoveAt(pos); Friends.Insert(pos, updatedFriend); }
Добавление:
Friend addedFriend = await friendsService.Add(friend); if(addedFriend!=null) Friends.Add(addedFriend);
Команда DeleteFriendCommand выполняет метод DeleteFriend, который во многом аналогичен редактированию объекта:
Friend deletedFriend = await friendsService.Delete(friend.Id); if(deletedFriend!=null) { Friends.Remove(deletedFriend); }
Это основные моменты класса ApplicationViewModel, но надо сказать, что многое зависит от сервера и его API. В данном случае веб-сервис делаем мы сами, и возвращаение при добавлении/редактировании/удалении измененного объекта нам очень сильно помогает. Во-первых, для взаимодействия с сервером нам необходим только один запрос: в запросе посылаем объект на добавление и в ответе получаем добавленный объект. Потом этот объект добавляем в коллекцию. Это экономичнее, чем в одном запросе отправлять добавляемый объект, а в другом запросе получать все объекты с сервера с учетом добавления. Во-вторых, в данном случае экономится мобильный траффик.
Однако используемый нами веб-сервис может не представлять подобной функциональности, как возвращение измененного объекта, либо к данным на сервере могут одновременно обращаться несколько пользователей и соответственно изменять эти данные. И в этом случае надежнее будет заново загружать данные после каждой произведенной операции.
Далее изменим страницу FriendPage. В коде 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="HelloApp.FriendPage" Title="Информация о друге"> <StackLayout> <StackLayout> <Label Text="Имя" /> <Entry Text="{Binding Path=Model.Name}" FontSize="Medium" /> <Label Text="Электронная почта" /> <Entry Text="{Binding Path=Model.Email}" FontSize="Medium" /> <Label Text="Телефон" /> <Entry Text="{Binding Path=Model.Phone}" FontSize="Medium" /> </StackLayout> <StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand"> <Button Text="Сохранить" Command="{Binding ViewModel.SaveFriendCommand}" CommandParameter="{Binding Model}" /> <Button Text="Удалить" Command="{Binding ViewModel.DeleteFriendCommand}" CommandParameter="{Binding Model}" /> <Button Text="Назад" Command="{Binding Path=ViewModel.BackCommand}" /> </StackLayout> </StackLayout> </ContentPage>
А в коде FriendPage.xaml.cs пропишем передачу на страницу объектов Friend и ApplicationViewModel и установку контекста:
using Xamarin.Forms; namespace HelloApp { public partial class FriendPage : ContentPage { public Friend Model { get; private set; } public ApplicationViewModel ViewModel { get; private set; } public FriendPage(Friend model, ApplicationViewModel viewModel) { InitializeComponent(); Model = model; ViewModel = viewModel; this.BindingContext = this; } } }
Далее на странице MainPage.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="HelloApp.MainPage" Title="Список друзей"> <StackLayout> <Button Text="Добавить" Command="{Binding CreateFriendCommand}" IsEnabled="{Binding IsLoaded}" /> <ListView x:Name="friendsList" ItemsSource="{Binding Friends}" SelectedItem="{Binding SelectedFriend, Mode=TwoWay}" HasUnevenRows="True"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <ViewCell.View> <StackLayout> <Label Text="{Binding Name}" FontSize="Medium" /> <Label Text="{Binding Email}" FontSize="Small" /> <Label Text="{Binding Phone}" FontSize="Small" /> </StackLayout> </ViewCell.View> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> <StackLayout IsVisible="{Binding IsBusy}" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" Padding="20"> <Label Text="Загрузка данных..." TextColor="Gray" HorizontalOptions="Center" /> <ActivityIndicator IsRunning="{Binding IsBusy}" Color="Red" > </ActivityIndicator> </StackLayout> </StackLayout> </ContentPage>
А в коде MainPage.xaml.cs пропишем установку контекста страницы и загрузку начальных данных:
using Xamarin.Forms; namespace HelloApp { public partial class MainPage : ContentPage { ApplicationViewModel viewModel; public MainPage() { InitializeComponent(); viewModel = new ApplicationViewModel() { Navigation = this.Navigation }; BindingContext = viewModel; } protected override async void OnAppearing() { await viewModel.GetFriends(); base.OnAppearing(); } } }
И в конце установим страницу NavigationPage в качестве главной в классе App:
using Xamarin.Forms; namespace HelloApp { public partial class App : Application { public App() { InitializeComponent(); MainPage = new NavigationPage(new MainPage()); } protected override void OnStart() { } protected override void OnSleep() { } protected override void OnResume() { } } }
В итоге весь проект будет выглядеть следующим образом:
Запустим веб-сервис и мобильное приложение и нажмем на кнопку добавления. Введем какие-нибудь данные:
И после добавления новый объект отобразится на главной странице: