Статьи

Реальный мир Windows Phone Dev Часть 4: Панорама

В этой серии статей будет рассказано о создании отлаженного, работающего приложения для Windows Phone от начала до конца. Приложение называется Realworld Stocks, и полный исходный код будет доступен на CodePlex по мере развития серии. Я буду использовать Mercurial, чтобы поощрять разветвление и, возможно, даже получать запросы от разработчиков, которые хотят внести свои собственные реальные решения.

Убедитесь, что вы загружаете Windows Phone 7.5 SDK


Посмотреть серию Введение и план

Windows Phone Панорама

Многие приложения прекрасно подходят для Windows Phone Panorama. Часы CTA выглядят похоже, но на самом деле это Pivot с фоновым изображением. Пройдя назад и вперед с реальными акциями, я решил, что панорама выглядела довольно хорошо, и согласился.

Еще раз спасибо @Templarian за фон! Обязательно бейте его, если вам нужен великий дизайнер, который на самом деле ездит на метро!

Этот пост будет посвящен множеству тем, поэтому я приложу все усилия, чтобы подогреть ваш аппетит, но, пожалуйста, не забудьте скачать код и проверить приложение для себя! Код должен быть довольно простым для понимания, так как вы лучше познакомитесь с тем, как Caliburn работает.

Что помогает сделать отличный опыт?

  1. Ленивые разделы загрузки после того, как пользователь пролистал к ним
  2. Панорамные анимации занимают ~ 0,5 секунды; не запускайте свой код, пока анимация не закончится, чтобы предотвратить заикание
  3. Покажите хороший BusyIndictator с дружескими сообщениями для каждого раздела панорамы
  4. Настройте кнопки панели приложения в зависимости от того, какой раздел панорамы активен
  5. В разделе «Новости» отображаются относительные даты времени (только сейчас, 2 часа назад и т. Д.)

отказ

Вся эта серия предполагает базовое понимание Silverlight и MVVM. Если вы не знакомы с этими темами, то я надеюсь, что вы все еще можете следовать, но вам, возможно, придется еще кое-что почитать, чтобы охватить основы и базовые примитивы. Тем не менее, следующий пост будет оставаться на довольно высоком уровне и попытаться позволить исходному коду говорить за себя. Я намеренно не собираюсь углубляться в темы, которые рассматриваются в другом месте.

Документация

Caliburn.Micro имеет отличную документацию. Проверьте следующие ссылки, если вы хотите глубоко погрузиться во все, что здесь описано.

Caliburn.Micro Документация

Работа с Windows Phone 7

 

MVVM Резюме

MVVM покрыт повсюду, поэтому все, что я скажу здесь, это следующее — оно обозначает Model-View-ViewModel.

  1. ViewModels — это просто обычные классы. В Caliburn многие из них наследуют от Screen . Они обеспечивают логику для вашего просмотра, в том числе то, какие данные должны отображаться, и какой код должен выполняться при выполнении действий в представлении (например, нажатие кнопок)
  2. Представления — это ваши стандартные артефакты XAML, такие как Pages и UserControls. В MVVM они в значительной степени полагаются на расширенную поддержку Binding в XAML / Silverlight для взаимодействия с ViewModel, с которой он связан.
  3. Модель это все остальное.

Анатомия HomeViewModel

Итак, HomeViewModel — отличная отправная точка. На момент написания вы можете увидеть полный исходный код HomeViewModel. На самом деле это довольно простой класс, который делает для нас много всего. Давайте рассмотрим этого плохого мальчика.

public class HomeViewModel : Conductor<IScreen>.Collection.OneActive, IRefreshable, IAppBarController
{
    private readonly INavigationService _navigation;
 
    public HomeViewModel(INavigationService navigation, HomeWatchListViewModel watchList, HomeNewsViewModel news,
                            HomeQuoteViewModel quote)
    {
        _navigation = navigation;
        Items.Add(watchList);
        Items.Add(news);
        Items.Add(quote);
    }
}

проводник

Панорама обычно состоит из 3-4 отдельных панелей. В большинстве случаев эти панели полностью изолированы и не имеют ничего общего друг с другом; кроме того, чтобы быть размещенным в том же контейнере. Если вы на самом деле думаете об этом, вы можете придумать несколько поддерживаемых и расширяемых проектов.

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

Вы заметите, что HomeViewModel наследуется от Conductor <IScreen> . Это обеспечивает нам отличную инфраструктуру прямо из коробки. Во-первых, он предоставляет свойство из базового класса времени IObservableCollectio <IScreen>, называемое Items. В приведенном выше коде мы используем эту коллекцию элементов для добавления наших моделей просмотра детей. Он также обрабатывал активацию наших дочерних элементов, сообщая им, что нужно инициализировать — это удивительно легко лениво загружать панели панорамы, обсуждаемые в конце этого поста.

Как это используется в HomeView.xaml?

Итак, у нас есть HomeViewModel со свойством Items, которое теперь содержит 3 других ViewModel. Как именно это используется на наш взгляд? Двумя способами:

  1. Элемент управления Panorama наследуется от ItemsControl, что означает , что он поддерживает привязку к коллекциям, как это делает ListBox . В конце концов, поскольку наши «Предметы» — это просто сами ViewModel, они, в свою очередь, отображаются как виды в Панораме. Эта концепция является основой, известной как построение составных видов .
  2. Нет ItemsSource = ”{Связывающие элементы}” ?? Вы можете заметить, что в XAML ниже нет явного связывания — Caliburn — это соглашение о конфигурации (если вы этого хотите!). Это означает, что по умолчанию он будет использовать свое x: Name и искать свойство в ViewModel, к которому он может привязаться. Если вы предпочитаете явно связывать свои представления, сделайте это! Соглашения не являются обязательными.

<controls:Panorama x:Name="Items" Title="{StaticResource AppNameUpper}">
    <controls:Panorama.HeaderTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding DisplayName}" />
        </DataTemplate>
    </controls:Panorama.HeaderTemplate>
</controls:Panorama>

МОК / Конструктор Инъекция

Теперь вы можете быть удивлены, что в HomeView я только что создал конструктор, который принимает 4 параметра: INavigationService , HomeWatchListViewModel , HomeNewsViewModel и HomeQuotesViewModel .

Когда наш HomeView.xaml загружен, что создает для нас нашу HomeViewModel и, что более важно, как он узнал, как передать эти параметры нашему конструктору?

Эта концепция известна как инверсия контроля. Наша HomeViewModel полагается на другие классы, чтобы выполнить свою работу, но вместо того, чтобы отвечать за создание этих зависимостей, она выводит эту ответственность на себя и просто заявляет: «Эй, мне нужны эти вещи! Рамки, вы даете их мне!

Когда вы создаете новые ViewModels и другие сервисы, вам просто нужно зарегистрировать их в AppBootstrapper . Фрагмент можно увидеть ниже. Как только эти ViewModel будут зарегистрированы в Bootstrapper, Caliburn позаботится обо всем остальном.

public class AppBootstrapper : PhoneBootstrapper
{
    private PhoneContainer _container;
 
    protected override void Configure()
    {
        LogManager.GetLog = type => new DebugLog();
 
        _container = new PhoneContainer(RootFrame);
        _container.RegisterPhoneServices();
 
        _container.Singleton<IStocksWebService, StocksWebService>();
 
 
        _container.Singleton<HomeViewModel>();
        _container.Singleton<HomeNewsViewModel>();
        _container.Singleton<HomeWatchListViewModel>();
        _container.Singleton<HomeQuoteViewModel>();
    }
}

Давай поговорим по-польски

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

BusyIndicator

Манго улучшил панорамы во многих отношениях. Например, в системном трее теперь есть свойство «Непрозрачность», так что вы можете, наконец, показать лоток в полноэкранной панораме, и он по-прежнему выглядит великолепно. Другим большим улучшением является собственный опыт ProgressIndicator . Он встроен в SystemTray и отлично взаимодействует с остальной частью лотка, например, скрывает загрузочное сообщение, когда пользователь нажимает на него, чтобы увидеть часы. У этого также есть некоторые большие из коробки анимации.

Для нашего приложения я хотел глобальный хук в ProgressIndicator из моих ViewModels. Для этого я создал простой метод BusyIndicator.Show (), который подключается прямо в системный трей. Вы можете использовать этот API прямо из вашей ViewModel очень простым способом.

Я буду больше в BusyIndicator в следующем посте.

public void RefreshData()
{
    BusyIndictator.Show("Loading watch list...");
    ThreadPool.QueueUserWorkItem(callback =>
    {
        Coroutine.BeginExecute(UpdateWatchList().GetEnumerator());
    });
}

IRefreshable

Обновление данных очень распространено в этих типах приложений. Признавая это, я решил создать простой интерфейс IRefreshable , который в случае нашего Conductor просто делегирует вместе с ActiveItem (в настоящее время активная панель ViewModel панорамы) и говорит: «Эй, вы знаете, как обновить данные? Если это так, пожалуйста, продолжайте! »

public void RefreshData()
{
    var refreshableChild = ActiveItem as IRefreshable;
    if (refreshableChild != null)
        refreshableChild.RefreshData();
}

Затем, в HomeWatchListViewModel, например, он также реализует IRefreshable и продолжает обновлять текущий список наблюдения.

Это еще один пример объединения частей для написания действительно расширяемого кода по мере развития нашего приложения.

public class HomeWatchListViewModel : Screen, IRefreshable
{
    public void RefreshData()
    {
        BusyIndictator.Show("Loading watch list...");
        ThreadPool.QueueUserWorkItem(callback =>
        {
            Coroutine.BeginExecute(UpdateWatchList().GetEnumerator());
        });
    }
}

Полировка AppBar

Если вы внимательно посмотрите на видео, вы заметите, что мы внимательно посмотрели на нашем домашнем экране. Каждое движение панорамы каким-то образом меняет панель приложений .

  1. Список отслеживания показывает 2 кнопки панели приложения: Добавить и Обновить
  2. В новостях отображается только 1 кнопка на панели приложения: Обновить
  3. Поиск показывает 0 кнопок панели приложения, и он установил свой режим на свернутый

Для достижения этой цели я создал простой IAppBarController интерфейс и AppBarHelper класс. Чтобы этот пост не стал слишком длинным, я скоро посвятию целую статью этой теме. В то же время, пожалуйста, посмотрите код, если вы хотите увидеть, как он работает!

Водяной знакTextbox и PriceChangeArrow

Снова зоркий глаз, возможно, заметил 2 других тонкости на главном экране:

  1. PriceChangeArrow — стрелка изменения цены (показывающая положительный или отрицательный выигрыш) имела небольшую анимацию при загрузке списка наблюдения. Это мелочь, но я думаю, что это делает приложение чарующим и приятным. Я буду вести блог, как именно создать такой контроль в будущем.
  2. WatermarkedTextBox — этот элемент управления я нашел в проекте Silverlight с открытым исходным кодом и немного улучшил его для Mango. Я буду вести блог более подробно об этом контроле в будущем.

Ленивый Загрузка Панорамных Панелей и Сохранение Жидкости Анимаций!

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

У Caliburn Screens есть несколько важных методов, которые вы хотите переопределить, чтобы приложение что-то делало.

  1. OnInitialize — переопределить этот метод, чтобы добавить логику, которая должна выполняться только при первом включении экрана. После завершения инициализации IsInitialized будет иметь значение true.
  2. OnActivate — переопределить этот метод, чтобы добавить логику, которая должна выполняться каждый раз, когда активируется экран. После завершения активации IsActive будет иметь значение true.
  3. OnDeactivate — переопределите этот метод, чтобы добавить пользовательскую логику, которая должна выполняться всякий раз, когда экран деактивируется или закрывается. Свойство bool будет указано, если деактивация действительно закрыта. После завершения деактивации IsActive будет иметь значение false.
  4. OnViewLoaded — поскольку Screen реализует IViewAware, он использует эту возможность, чтобы сообщить вам, когда запускается событие Loaded вашего представления. Используйте это, если вы следуете стилю SupervisingController или PassiveView и вам нужно работать с представлением. Это также место для размещения логики модели представления, которая может зависеть от наличия представления, даже если вы не работаете с представлением напрямую.

Узнайте больше о экранах, проводниках и композиции

В дополнение к вышеупомянутым событиям, некоторое время назад я придумал OnViewReady и отправил патч Робу. Идея проста: запускать после OnViewLoaded , но также и после первого LayoutUpdated ; это означает, что базовый интерфейс отображается, поэтому не стесняйтесь начинать загружать процессор! Дождавшись OnViewReady, вы убедитесь, что ваш код не запускается и не сжигает драгоценные циклы ЦП, пока платформа пытается завершить запуск (и анимацию объектов, таких как панорама).

Обходной путь для OnViewReady

На момент написания этой статьи в пакете 1.2 NuGet я не мог заставить его вести себя правильно, поэтому вместо этого выбрал немного более уродливый обходной путь. По сути, это создает фоновый поток и затем спит в течение 1 секунды в этом потоке, прежде чем начать работать. 1 секунда должна быть достаточной для завершения любой базовой загрузки и анимации.

protected override void OnInitialize()
{
    RefreshData();
}
 
public void RefreshData()
{
    BusyIndictator.Show("Loading watch list...");
 
    // TODO: Move this to OnViewReady in CM 1.3
    // For now sleep for a bit to let the panorama load smoothly
    ThreadPool.QueueUserWorkItem(callback =>
    {
        Thread.Sleep(1000);
        Coroutine.BeginExecute(UpdateWatchList().GetEnumerator());
    });
}

Резюме

Престижность, если вы следовали всему этому! Это заняло у меня немного времени, чтобы написать, и содержит много вещей, но это также оставляет дверь открытой для намного большего количества разговоров в будущем!

Пожалуйста, оставляйте здесь любые вопросы, и снова, пожалуйста, сверните код и следуйте инструкциям. Обратная связь с благодарностью!

Источник:  http://www.matthidinger.com/archive/2011/10/21/RealWorldWPDev-Part-4-The-Panorama.aspx