Thursday, January 15, 2015

Начало работы с Caliburn Micro

Это моя первая статья в Новом году, поэтому хотелось бы ее начать с чего-то нового. А так как с Нового года я начал потихоньку осваивать Caliburn.Micro – фреймворк для написания приложений под WPF, Silverlight, WinRT, WP8 и т.д., используя паттерн MVVM, то первую статью я решил посвятить именно работе с этим фреймворком. После плотной работы с Prism переход на Caliburn.Micro выглядит, словно вы изучали какой-то сложный фреймфорк с огромными возможностями и функционалом, а тут вам дали урезанную версию этого фреймворка. Очевидные плюсы Caliburn.Micro:
  1. Порог вхождения для изучения Caliburn.Micro, по сравнению с Prism, ниже;
  2. Caliburn.Micro написан с поддержкой практически всех платформ, на которые можно писать код, используя xaml;
  3. По популярности этот фреймворк из-за количества платформ превосходит, наверное, все доступные на данный момент фреймворки (я не встречал более популярного бесплатного фреймфорка);
  4. Удобная привязка данных.
К сожалению, есть и определенные недостатки. Например, меня очень напрягает автоматическое связывание свойств класса с контролами через x:Name. До сих пор не могу нормально привыкнуть к такому подходу. Второой субъективный минус – это расширенный синтаксис для функций. Почему-то ребята, спроектировавшие этот фреймворк, посчитали, что использование событий – это намного лучше, чем использование команд с интерфейсом ICommand. К сожалению, вынужден не согласиться с таким суждением и подходом к написанию программ. Но каждый фреймворк должен как-то выделяться, быть в чем-то уникальным, запоминающимся. Так что считаем это фишкой данного фреймворка.
При изучении какого-то нового языка или фреймворка я стараюсь следовать простому правилу: не переносить возможности другого языка или фреймворка на тот язык или фреймворк, который вы изучаете, иначе это не приведет ни к чему хорошему. При изучении, например, языка C# после C попытки искать возможности C++ в C# могут привести к тому, что вы посчитаете один язык хуже или лучше предыдущего, основывая выводы только на сравнении. Старайтесь изучать что-то новое в его окружении, не пытаясь проецировать то, что вы узнали раньше, на то, что изучаете сейчас. Вспомните слова героя Джеки Чана в фильме “Запретное царство”: “Как наполнить чашу, которая уже полна?”. В этом знаменитом каноне о наполненной чаше кроется полезная истина, которую стоит намотать на ус как начинающим разработчикам, так и опытным специалистам с большим багажом знаний.
Итак, я очень постараюсь не проецировать то, что я изучил по Prism, на Caliburn.Micro. Позже подготовлю статью о схожести Caliburn.Micro с Prism и теми возможностями, которые лучше покрываются что одним фреймворком, и что вторым.
Начнем наш экскурс в возможности данного фреймворка. Для начала нам нужно знать, что Caliburn.Micro доступен, начиная с версии NET Framework 4.0. Поэтому создадим простое десктопное приложение на WPF и назовем его FirstAppWithCaliburnMicro. Затем с помощью Manager NuGet Packages добавим себе ссылку на Caliburn.Micro.dll.
Актуальная версия на момент написания статьи была 2.0.1. После того, как вы установите данный пакет через NuGet, в ваш проект будет добавлено сразу три библиотеки.
На рисунке эти сборки подсвечены зеленым цветом. Caliburn.Micro – базовая библиотека данного фреймворка со всеми интерфейсами. Caliburn.Micro.Platform – сборка специально для работы с системой Windows. Там находятся такие классы, как WindowManager для работы с окнами, ViewLocator/ViewModelLocator и другие. Я не так давно начал изучать Caliburn.Micro, поэтому некоторые части могу описать не настолько внятно, как написали бы ребята, которые съели на этом зубы, но мне нравится записывать результат своей работы в виде блога. Как оказалось, это помогает в освоении нового материала также и читателям моего блога, что не может не радовать.
Созданный проект необходимо разбить на следующую структуру:
Структура проекта повторяет структуру проектов с использованием паттерна MVVM (ModelView-ViewModel). В данном примере у меня нет моделей, а используется всего одна модель представления, поэтому создания папки Models для хранения моделей для данного примера мне не понадобилось.
Начнем с небольшого уточнения. В Caliburn.Micro модель представления и само представление связываются по умолчанию. Поэтому чтобы ваше представление знало модель представления, с которой оно связано, вы должны следовать простым правилам. Модель представления должна иметь такое же имя, как само представление, с приставкой Model. Если у вас главное окно называется MainPage, то модель представления будет иметь название MainPageViewModel. Модель представления должна иметь окончание “ViewModel”. Со временем вы к этому привыкнете. По сути, в данном случае Caliburn.Micro задает вам правила именования ваших моделей и моделей представлений. Считать это плюсом для данного фреймворка или нет, по сути, – ваше личное предпочтение. Просто данный фреймворк продвигает подход View-Model-First, в котором первым этапом вы добавляете модель представления (View Model). Поэтому давайте перейдем в нашу папку ViewModels и добавим новый класс MainViewModel.
public class MainViewModel : PropertyChangedBase
{
    public MainViewModel()
    {
        HelloWorld = "Hello World";
    }

    private string _helloWorld;

    public string HelloWorld
    {
        get { return _helloWorld; }
        set
        {
            _helloWorld = value;
            NotifyOfPropertyChange(() => HelloWorld);
        }
    }

    //Don't use this code in production
    public void ShowMessage()
    {
        MessageBox.Show("Test Caliburn.Micro");
    }
}
Здесь нет ничего сверх особенного. Мы наследовались от класса PropertyChangedBase, для того чтобы не реализовывать отдельно интерфейс INotifyPropertyChanged, а использовать вызов NotifyOfPropertyChange, чтобы реализовывать базовую логику с класса PropertyChangedBase, который предоставляет обертки для данных интерфейсов.
Как видите, у нас используется одно свойство HelloWorld и метод ShowMessage с комментарием не использовать такой подход в ваших приложениях. Вызов MessageBox с модели представления считается нарушением паттерна MVVM, потому что модель представления не должна управлять отображением представления. Для таких целей нужно писать отдельные сервисы, чтобы не было нарушения разделения логики.
Теперь перейдем в папку Views, перенесем туда наше созданное по умолчанию главное окно MainWindow.xaml и переименуем его в MainView.xaml.
Примечание. По правилам Caliburn.Micro нам нужно было бы назвать наше главное окно Main.xaml, но мне это имя не нравится, а поскольку Caliburn.Micro нормально связывает названия с приставкой View, я решил использовать такое название.
В это окно я добавил один контрол для отображения сообщения и кнопку для показа MessageBox.
<Window x:Class="FirstAppWithCaliburnMicro.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
      <StackPanel Orientation="Vertical">
        <TextBlock x:Name="HelloWorld" />
        <Button x:Name="ShowMessage" Content="Show Message"/>
        </StackPanel>
    </Grid>
</Window>
Если вы посмотрите внимательно на код, то я не указал ни одной строчки байдинга явно, и самое интересно, из-за чего мне и нравится Caliburn Micro– то, что это реально работает. Не знаю, почему в примерах в инете, которые объясняют использование Caliburn в своих проектах, нет ни слова об этом для начальных проектов. Обычно все это показывают со второй темы по данному фреймворку. Но я решил немного соригинальничать и показать, как работает привязка данных на простом примере. Для простых контролов, как, например TextBox или Button или другие простые контролы, как в нашем примере, если мы укажем имя контрола x:Name таким же, как свойство в модели представления, то оно будет вызвано автоматически. Аналогично для функции, которая для кнопки Button, по сути, делает то же самое, поскольу имя контрола ShowMessage совпадает с именем функции в модели представления MainViewModel. Теперь нам осталось реализовать загрузчик, который инициализирует начальные настройки для запуска приложения. В отличие от версии Сaliburn.Micro 1.5.0, работа с загрузчиком претерпела небольших изменений, о которых вы можете узнать здесь: Upgrading Caliburn Micro from 1.5 to 2.0 for a WPF application. А теперь добавим сам загрузчик в наш проект. Для этого создадим новый класс, который назовем ShellBootstrapper, и наследуем его от BootstrapperBase, как показано в примере ниже.
public class ShellBootstrapper : BootstrapperBase
{
    public ShellBootstrapper() : base(true)
    {
        Initialize();
    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        DisplayRootViewFor<MainViewModel>();
    }
}
Как видите, этих пару строчек достаточно для запуска нашего приложения. Теперь осталось создать объект данного класса в App, для того чтобы инициализировать начальные значения. Для этого мы сделаем всю настройку в xaml. Переходим в наш файл App.xam и в первую очередь удаляем StartupUri, поскольку запуском главного окна будет заниматься наш загрузчик. И слегка модифицируем наш xaml код, как показано ниже в примере.
<Application x:Class="FirstAppWithCaliburnMicro.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:firstAppWithCaliburnMicro="clr-namespace:FirstAppWithCaliburnMicro">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
          <firstAppWithCaliburnMicro:ShellBootstrapper x:Key="Bootstrapper" />
        </ResourceDictionary>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>
Вы можете сделать то же самое через код, переопределив метод OnStartup класса App. Смотрите, как вам удобнее использовать данный подход. Все!!! Этого достаточно, чтобы запустить наше приложение и убедиться, что оно работает.
А также убедиться, что если мы нажмем на кнопку ShowMessage, то у нас все будет работать.

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

No comments:

Post a Comment