Создание интерфейса для работы с веб-сервисом

Последнее обновление: 14.01.2021

Продолжим проект из прошлой темы и создадим для него графический интерфейс. Итак, по умолчанию у нас уже есть страница 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() { }
    }
}

В итоге весь проект будет выглядеть следующим образом:

веб-сервис в Xamarin Forms

Запустим веб-сервис и мобильное приложение и нажмем на кнопку добавления. Введем какие-нибудь данные:

Добавление на веб-сервис в Xamarin Forms

И после добавления новый объект отобразится на главной странице:

Работа с веб-сервисом в Xamarin Forms
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850