В данной статье мне хочется рассказать, как я визуализировал статистику по происходящей пандемии, и поделиться с какими особенностями можно встретиться, используя для этого Avalonia UI.
В этой статье подразумевается, что вы уже можете создать простое приложение с использованием AvaloniaUI. Если же нет, можно обратиться к туториалам AvaloniaUI или Habr.
В первую очередь встал вопрос где же взять актуальные данные и быстрым поиском в гугле было найдено следующее апи, которое удовлетворяло всем моим требованиям:
Приложение состоит из 3х различных экранов.
Перемещения между экранами построены при помощи роутинга. Интересной особенностью в данном моменте была передача тригера для роута в UserControl.
Для этого его ViewModel должна была принять Action:
И вызовом этого экшена служит выбор элемента в списке:
Для обращений к апи была использована библиотека flurl. Которая внутри себя использует HttpClient и Newtosoft, а сама является расширением для строк.
По этому получение модельки из запроса можно реализовать в одну строку:
Для работы с изображением при помощи flurl получался stream который было необходимо упаковать в memorystream для создания экземпляра Bitmap:
Для отображения коллекции была использована Dinamic Data из ReactiveUI .
Так же я решил добавить фильтр для быстрого поиска страны по ее имени или сокращению ISO3.
Для этого строка поиска была привязана к свойству:
Добавлено правило фильтрации:
И создан сам фильтр:
Для графиков я воспользовался Oxyplot библиотекой для AvaloniaUI.
К сожалению в nuget версия устарела или я ее не нашел, так что вы можете воспользоваться собранным мной nuget пакетом, или утянуть исходники с github.
Для работы необходимо добавить в App.xaml:
Для отображения оси с конкретной датой, а не произвольным числом (в которое oxyplot конвертирует дату для внутренней работы) необходимо указать тип оси в разметке:
Используя dotnet publish я собрал бинарники для основных платформ (тут), а для тех, кому станет интереснее. исходники можно найти на github.
Данное приложение базируется на стороннем апи, и надеюсь в скором времени данное апи перестанут поддерживать, ибо будет незачем.
Мойте ручки, и сидите дома. А поддержку по Авалонии можно найти тут.
От автора
Я делал данный проект просто, что бы занять себя и не думать. Данный проект не претендует ни на какую достоверность. Если вам захочется — все распространяется под MIT лицензией и вы можете использовать мои наработки.
Введение
В этой статье подразумевается, что вы уже можете создать простое приложение с использованием AvaloniaUI. Если же нет, можно обратиться к туториалам AvaloniaUI или Habr.
Данные
В первую очередь встал вопрос где же взять актуальные данные и быстрым поиском в гугле было найдено следующее апи, которое удовлетворяло всем моим требованиям:
- Общая информация по миру
- Общая информация по каждой стране
- История по каждой стране
Структура приложения
Приложение состоит из 3х различных экранов.
Скрины
Перемещения между экранами построены при помощи роутинга. Интересной особенностью в данном моменте была передача тригера для роута в UserControl.
Для этого его ViewModel должна была принять Action:
public CountriesVM(IScreen screen, Action<Country> a)
CurrentCountyCommand =
CountriesCommand = ReactiveCommand.CreateFromObservable(
() => Router.Navigate.Execute(new CountriesVM(this,GoToCurrentCountry))
);
....
public void GoToCurrentCountry(Country c)
{
CurrentCountyCommand.Execute(c);
}
И вызовом этого экшена служит выбор элемента в списке:
public Country SelectedCountry
{
get => _country;
set
{
this.RaiseAndSetIfChanged(ref _country, value);
A.Invoke(value);
}
}
Запросы
Для обращений к апи была использована библиотека flurl. Которая внутри себя использует HttpClient и Newtosoft, а сама является расширением для строк.
По этому получение модельки из запроса можно реализовать в одну строку:
var allCases = await "https://corona.lmao.ninja/all".GetJsonAsync<AllCases>();
Изображения
Для работы с изображением при помощи flurl получался stream который было необходимо упаковать в memorystream для создания экземпляра Bitmap:
private async Task<Bitmap> GetFlag(String flagUrl)
{
var flagStream = await flagUrl.GetStreamAsync();
var memoryStream = new MemoryStream();
flagStream.CopyTo(memoryStream);
memoryStream.Position = 0;
var bitmap = new Bitmap(memoryStream);
return bitmap;
}
Коллекция и фильтрация
Для отображения коллекции была использована Dinamic Data из ReactiveUI .
SourceList<Country> countries = new SourceList<Country>();
private ReadOnlyObservableCollection<Country> _collection;
public ReadOnlyObservableCollection<Country> Collection => _collection;
countries.Connect().Filter(filter).ObserveOn(RxApp.MainThreadScheduler).Bind(out _collection).Subscribe();
Так же я решил добавить фильтр для быстрого поиска страны по ее имени или сокращению ISO3.
Для этого строка поиска была привязана к свойству:
public string FilterName
{
get => _filterName;
set { this.RaiseAndSetIfChanged(ref _filterName, value); }
}
Добавлено правило фильтрации:
private Func<Country, bool> Filter(string filterName)
{
if (string.IsNullOrEmpty(filterName)) return x => true;
return x => x.Name.ToUpper().Contains(filterName.ToUpper()) || x.ISO3.Contains(filterName.ToUpper());
}
И создан сам фильтр:
var filter = this.WhenValueChanged(x => x.FilterName).Select(Filter);
Отображение графиков
Для графиков я воспользовался Oxyplot библиотекой для AvaloniaUI.
К сожалению в nuget версия устарела или я ее не нашел, так что вы можете воспользоваться собранным мной nuget пакетом, или утянуть исходники с github.
Для работы необходимо добавить в App.xaml:
<StyleInclude Source="resm:OxyPlot.Avalonia.Themes.Default.xaml?assembly=OxyPlot.Avalonia"/>
Для отображения оси с конкретной датой, а не произвольным числом (в которое oxyplot конвертирует дату для внутренней работы) необходимо указать тип оси в разметке:
<avalonia:Plot.Axes>
<avalonia:DateTimeAxis StringFormat="yyyy-MM-dd" Position="Bottom"></avalonia:DateTimeAxis>
</avalonia:Plot.Axes>
Исходники и приложения
Используя dotnet publish я собрал бинарники для основных платформ (тут), а для тех, кому станет интереснее. исходники можно найти на github.
Вместо послесловия
Данное приложение базируется на стороннем апи, и надеюсь в скором времени данное апи перестанут поддерживать, ибо будет незачем.
Мойте ручки, и сидите дома. А поддержку по Авалонии можно найти тут.