Pull to refresh
0
Rating
Mobile Dimension
Mobile Dimension — разработчик мобильных решений

Самодостаточные контроллы на Xamarin.Forms: «Переиспользуй код на максимум!». Часть 1

Mobile Dimension corporate blog Development for iOS *Development for Android *


Ещё в качестве идеи Xamarin.Forms понравился всем WPF разработчикам: популярность создания приложений под Android и iOS росла, WPF становился пережитком, а востребованность WPF разработчиков неуклонно стремилась к нулю — Forms звучал, как спасение. Появилась надежда, что мы со своим знанием XAML и паттерна MVVM будем кому-нибудь нужны. Конечно, изначально Xamarin.Forms оказался сырым, с большим количеством багов и отсутствием некоторых жизненно необходимых вещей (вспомнить хотя бы input control без возможности указания maxwith).

Прошло три года и Microsoft приобрел Xamarin. Теперь он поставляет его вместе с Visual Studio, и как следствие: багов стало меньше, а возможностей из коробки — больше. Но осталась одна проблема: приложения с единым интерфейсом не получаются нативными. То есть, если появляется различие в интерфейсах Android и iOS, разработчик сталкивается с болью в виде создания отдельных ViewModel под каждую платформу…и это только цветочки.

Но мы в Mobile Dimension специализируемся на корпоративных приложениях, и в этом случае это единство интерфейса является плюсом. Более того, когда в компании много решений для различных целей, даже целые функциональные контроллы (форма авторизации или каталог товаров) должны выглядеть одинаково.

В такой ситуации хочется иметь возможность переиспользовать код не только в разных платформах, но и на разных проектах. Соответственно, нужно предусмотреть самодостаточность контроллов, т.е. контролл, существующий со всеми его функциями бизнес-логики, взаимодействием с REST API, кэшированием и пр. Причем такие контроллы должны быть независимы от наличия других контроллов. Необходимо создавать ситуацию, при которой контролл живет своей жизнью и вызывает (invoke) какие-то ивенты, когда что-то произошло.

Забегая вперед, отмечу, что выглядит все просто и естесственно, однако такой подход пришел к нам через долгие недели рефакторинга кода, где изначально сильно отделялся слой данных от слоя интерфейса с бизнесслогикой. При таком классическом подходе все здорово: понятно какой модуль за что отвечает, порог входа нового разработчика низок. Однако, когда проект разрастается до нескольких десятков контроллов, каждый из которых взаимодействет с другими — появляются неожиданные ошибки, связанные с запутанностью вызовов методов разных контроллов. Это все, в конечном итоге привел нас к так называемому «аду вьюмоделей».

Распутывается он благодаря подходу через сервисы и подписки на изменения в этих сервисах. Т.е. мы делаем акцент не на том, чтоб слой данных был абсолютно независимым от логики или интерфейса, а на том, чтоб какой-то конкретный контролл был абсолютно незамисимым от остальных контроллов.

Сегодня я покажу самый простой пример – авторизацию. Представим, что у нас есть REST точка для авторизации. Мы создаем контролл, способный авторизовывать пользователей во всех системах в компании. Его интерфейс реализуется один раз. Все требования по стилям указываются в разметке в контролле авторизации:

Разметка контролла авторизации
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <ActivityIndicator Grid.RowSpan="2" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Color="Red" IsEnabled="True" IsVisible="{Binding IsLoading}" IsRunning="True"/>
    <Label Text="Авторизация" FontSize="Large" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/>
    <StackLayout Grid.Row="1" Orientation="Horizontal">
        <Entry Text="{Binding Login, Mode=TwoWay}" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand"/>
        <Button Text="OK" VerticalOptions="CenterAndExpand" Command="{Binding RegisterCommand}" BackgroundColor="Red" TextColor="White"/>
    </StackLayout>
</Grid>

Верстка такого контролла — самая сложная часть его создания: надо учитывать то, что он должен быть адаптивным к любому из контейнеров, где в последствии может оказаться. Буквально надо остерегаться любой конкретной величины, пытаясь растягивать контент. Тут нам поможет Grid с его автоматической растяжкой контента по ячейкам и Vertical/HorizontalOptions. К сожалению, ViewBox (контролл, который растягивает контент) из UWP в Forms еще не реализовали, однако такая инициатива была предложена и за нее можно проголосовать на странице разработчиков Xamarin, где пользователи буквально приоритезируют «фичи» которые войдут в следующие билды Xamarin.Forms.

Затем реализуем ViewModel, в которой вызывается метод сервиса авторизации из бизнес-логики.

ViewModel регистрации
IAuthorizationService _authorizationService;
public AuthorizationViewModel()
{
    this.RegisterCommand = new Command(async () => { await Registration(); });
    _authorizationService = Core.DI.Container.GetInstance<IAuthorizationService>();
}
private async Task Registration()
{
    IsLoading = true;
    await _authorizationService.Login(Login);
    IsLoading = false;
}
private string _login;
public string Login
{   get {   return _login;  }
    set {   _login = value; RaizePropertyChanged(nameof(Login));}
}
//INotifyPropertyChanged realization

Использование контролла
	
<local:AuthorizationView WidthRequest="300" VerticalOptions="CenterAndExpand" HorizontalOptions="Center"/>

Подписываться на изменение этого сервиса можно из любого места в приложении и обработать это соответствующим образом. Сервис отвечает только за рейз.

Подписка на событие контролла
var authorizationService = Core.DI.Container.GetInstance<IAuthorizationService>();
authorizationService.AuthorizationChanged += AuthorizationService_ AuthorizationChanged;

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

Читайте в части 2: более сложные контроллы и их взаимодействие друг с другом.

Ссылка на github

image image
Tags:
Hubs:
Total votes 6: ↑6 and ↓0 +6
Views 3.2K
Comments Comments 6

Information

Founded
Location
Россия
Website
www.mobiledimension.ru
Employees
51–100 employees
Registered