Во дни сомнений, во дни тягостных раздумий

    В данной статье мне хочется рассказать, как я визуализировал статистику по происходящей пандемии, и поделиться с какими особенностями можно встретиться, используя для этого Avalonia UI.



    От автора
    Я делал данный проект просто, что бы занять себя и не думать. Данный проект не претендует ни на какую достоверность. Если вам захочется — все распространяется под MIT лицензией и вы можете использовать мои наработки.

    Введение


    В этой статье подразумевается, что вы уже можете создать простое приложение с использованием AvaloniaUI. Если же нет, можно обратиться к туториалам AvaloniaUI или Habr.

    Данные


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

    1. Общая информация по миру
    2. Общая информация по каждой стране
    3. История по каждой стране

    Структура приложения


    Приложение состоит из 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.

    Вместо послесловия


    Данное приложение базируется на стороннем апи, и надеюсь в скором времени данное апи перестанут поддерживать, ибо будет незачем.

    Мойте ручки, и сидите дома. А поддержку по Авалонии можно найти тут.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 11

      +7
      Откровенно говоря, посты про вирусы уже осточертели на Хабре.
      Каждый второй.
      В хайп не попадаете.
      Давайте думать позитивно, просто тему смените.
      Будет интереснее, думаю.
        +1
        Возможно вы и правы.
        В хайп не попадаете.

        Я и не стремился, что бы попасть в хайп стоит писать не о десткопе и не о шарпе)
        Постов много не только потому, что кто-то пытается сесть на хайптрейн, а потому что людей это волнует. И некоторые стремятся выразить свои переживания в статьях или в чем-то еще.
        Я вот нашел успокоение в коде, да и на мой взгляд в нем разобрано несколько интересных технических моментов, которые можно потом использовать в каких то реальных продуктах.
        Для меня хабр — все еще площадка для выражения своих мыслей, какого-то творчества, возможно идей и переживаний, я не то чтобы часто пишу, да и пишу только о том, что находит во мне отклик.
        +2
        Я вот нашел успокоение в коде

        Имело бы смысл прямо на этом месте и остановиться.

          +3
          Хорошая статья с примерами по работе с Avalonia UI. Думаю надо было на этом акцентировать внимание, а не на вирусе, ибо авалония актуальна уже тем что это единственная полностью кроссплатформенная GUI библиотека для C#.
            +1
            А какие в 2020 году могут быть причины для выбора создания кроссплатформенного desktop-приложения вместо веб-приложения?
              +1
              не каждый программист клепает сайт визитку. какие десктопные приложения вы используете сейчас и представьте каковы они были в виде веб приложения
                0
                Скажем так, всякий энтерпрайз, который в основном и использует C#, как раз пишет софт, который очень хорошо переносится в веб. С другой же стороны то, что использую я, вряд ли пишется на Шарпе. Однако тот же Visual Studio Code, например, собран на Electron, насколько мне известно. Так что в некотором роде его можно притянуть к веб-приложению
                0
                Действительно десктоп не очень популярен, но вот майки отмечали его достоинства
                тут
                  0
                  А какие в 2020 году могут быть причины для выбора создания кроссплатформенного desktop-приложения вместо веб-приложения?

                  Если этому приложению не нужен веб в принципе. А нужен доступ к файловой системе. Например, если это новый редактор vcf файлов.
                +2

                Очень много опечаток, плюс имеются ошибки:


                var flagStream = await flagUrl.GetStreamAsync();
                flagStream.CopyTo(memoryStream);

                Почему используется CopyTo, а не CopyToAsync, раз уж везде используется асинхронщина?

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

                Only users with full accounts can post comments. Log in, please.