Monday, March 30, 2015

Правильная постановка целей

Сегодня разрешите поделиться с вами тем, как я в последнее время ставлю себе цели и достигаю их. 
К примеру, главная цель, поставленная на месяц, – пополнить свой лексический запас английского языка на 500 новых слов, т.е. изучать до 20 слов ежедневно.
Для изучения я решил использовать подход, который практикуется в нашей фирме, а именно: постановка SMART-целей. SMART в данном случае – это  аббревиатура, которая расшифровывается следующим образом:
Каждая из букв-составляющих имеет свой смысл. 
Specific означает направленность вашей цели на определенный конкретный результат
Measurable указывает на ощутимость, измеримость результата; предполагается наличие некоего критерия оценки достижения вашей цели.
Attainable выражает достижимость выбранной цели; развития в себе определенных установок, способностей, навыков, финансовых возможностей, с помощью которых шаг за шагом личность начинает расти, соответствовать поставленной цели. 
Realistic предполагает совпадение понятий "желания-возможности" в выборе цели, которая должна быть высокой, но в то же время реалистичной, и соответствовать жизненным приоритетам человека.
Timely обозначает временные рамки достижения цели, для которой работает не "когда-нибудь", а установленный срок.
В чем плюс использовать SMART-цели для достижения конечного результата, – так это в том, что вы научитесь более точно определять свои возможности в постановке той или иной цели и сможете более эффективно ее достигать. Как показывает практика, это работает не только для бизнес-целей в рамках работы в компании, но также для достижения собственных локальных целей. 
Я считаю, что среди данных критериев самым сложным является последний пункт, связанный со временем. Очень сложно контролировать свое время, а еще сложнее организовать время для изучения чего-либо. 
Для того чтобы достичь поставленной цели, я решил выработать привычку заниматься английским как минимум в течение часа ежедневно. Но всегда стоял вопрос, как определить, достаточно ты потренировался или нет. Последние 2 года я занимаюсь изучением английского на lingualeo. Одно из преимуществ, которое дает LinguaLeo и которого я не нашел на других сайтах по изучению иностранных языков, – это возможность выбора интенсивности занятий в день, причем LinguaLeo позаботился о том, чтобы сделать этот процесс более интерактивным и захватывающим, представленным в виде сытости львенка Leo, "рацион" которого составляют выученные слова и разнообразные тренировки, которые вы проходите. Вот как это выглядит:
С левой стороны показана сытность вашего львенка, а с правой – ваш прогресс в достижении цели. По сути, это SMART-цели в действии. которые позволяют самостоятельно мониторить свой прогресс обучения. Бывают дни, когда желание заниматься учебой пропадает, и занятия будут для вас как подвиг. Но как написано в замечательной статье "Учёбене нужны подвиги, учёбе нужна рутина", ваша основная задача  это выработать в себе привычку делать какие-то регулярные действия для достижения своей цели.
Берегите силу воли  заводите привычки
Думать и говорить, что ваша сила воли неисчерпаема,  хорошо и полезно для достижения целей, и исследования это подтверждают. И в то же время важно не переусердствовать.
Не нужно растрачивать силу воли там, где этого не требуется. Например, вы поставили себе цель  выучить язык. Вместо того, чтобы усилием воли заставлять себя садиться за занятия, стоит выработать привычку делать что-то для достижения цели ежедневно  например, повторять иностранные слова за завтраком и читать 30 минут перед сном. Секрет здесь в том, что регулярные действия имеют особенность с течением времени трансформироваться в потребность. А для того, чтобы удовлетворить потребность, не приходится затрачивать большие волевые усилия.
Рекомендация о  привычках работает только в том случае, если вы ставите себе высокую цель, а на пути к ней  мелкие достигаемые цели. Например, глобальная цель изучить английский язык может стать SMART-целью, если, к примеру:
  • добавить конкретизацию (в данном случае – для чего вы хотите знать английский язык): для путешествий, для переписки с друзьями, для работы, чтения литературы и/или просмотра фильмов в оригинале;
  • определить критерий желаемого результата (здесь – ожидаемый уровень владения языком): от понимания на слух до непринужденного владения английским; 
  • разбить ее на мелкие цели-шаги, установив для них временное ограничение: прослушать и разобрать 1 песню на английском за полдня, потренироваться по 5 грамматическим правилам за день, выучить 100 слов за неделю, прочитать книгу за месяц и т.д. 
Например, я стараюсь писать в блог статью как минимум раз в месяц. К сожалению, единственным исключением стал Февраль, поскольку я решил себя немного подтянуть в английском (ненадолго сменил приоритеты). У меня отличные условия для изучения языка:
  • Работа в команде разработчиков с распределенным штатом, часть из которых находятся в другой стране. Поэтому основной язык общения у нас – английский, что способствует развитию коммуникативных навыков. 
  • Посещение занятий в малых группах с преподавателем, во время которых  в живом общении изучается теория и закрепляется на практике.
  • Самообучение с помощью разных языковых сервисов. В данном случае не важно в каком сервисе вы занимаетесь, главное, чтобы этот сервис позволял в интерактивном режиме следить за своим прогрессом. 
  • Просмотр фильмов в оригинале с английскими субтитрами или без них (максимальное совмещение приятного с полезным).
  • Чтение, чтение, чтение... Любых интересных материалов: от заметок, статьей, анекдотов – до классической литературы.
По поводу последнего пункта, я решил вопрос очень просто: максимально сократить  чтение литературы на родных языках. Основная часть литературы, с которой я работаю, – это литература по программированию и технические статьи на английском языке. Для этого достаточно найти несколько кумиров, чьи интересы в чем-то совпадают с вашими, и регулярно ознакомляться с их заметками, статьями. Например, один из моих любимых блогов, которые я стараюсь почитывать, – это блог Скотта Хансельмана. Это технический блог по разработке программного обеспечения, разных новинках в этой сфере и не только.

Сейчас вернусь к тому, как я построил свой процесс обучения. Слова, которые я добавляю на изучение, в основном идут из видеоуроков на LinguaLeo, а также те, которые встречаются в процессе изучения того или иного фильма, урока, клипа. Это сделано благодаря интерактивным субтитрам. Вот как это выглядит на практике:
Во время промотки видеоряда вы можете в любой момент выделить нужное слово и добавить его на изучение.
Второе интересное нововведение на данном сайте, которое способствует запоминанию слов, – это то, что слова, которые вы недавно изучали, у вас добавляются на повторение. При этом время интервала изменяется. Чем лучше вы знаете слово (чем меньше ошибаетесь в тренировках с этим словом), тем реже оно будет у вас на повторении.
Я стараюсь не накапливать много слов, находящихся "на повторении", тренируя их практически каждые 3 часа, так как это не занимает много времени. После прохождения вы сможете увидеть результат, как на рисунке ниже.
С правой стороны показано, через какое время эти слова появятся следующий раз в тренировке на повторение. На втором месте по запоминанию слов стоит тренировка под названием Brainstorm. Задача этой тренировки заключается в комплексном подходе к изучении группы (до шести) слов. Тренировка позволяет запомнить значение, произношение и написание слова. Выглядит эта тренировка так:
Сам процесс прохождения тренировки:
После прохождения тренировки вы получите результат :
Если у вас были какие-то ошибки, то данное слово будет снова фигурировать в тренировке Brainstorm через некоторое время, что способствует лучшему запоминанию новых слов.
Также популярна тренировка Leo-sprint, которая позволяет повторить в течение 30 секунд последние изученные слова.
Есть интересные тренировки Crossword для разгадывания кроссвордов с изучаемыми словами, но эта тренировка доступна только для пользователей с золотым статусом (то есть платная). Добавление новых слов в словарь выше определенного максимума также сделано платно, существует ограничение по добавлению новых слов в день на бесплатной основе. Правда, разработчики сайта дали шанс испробовать эти привилегии даже тем пользователям, которые не приобрели золотой статус, но занимаются каждый день. Обычно это подарок в виде пользования одного для золотым статусом, на протяжении которого доступны все тренировки. В такой день вы можете добавить неограниченное количество слов, которые хотите изучать.
Также интересная возможность на данном сайте – это читать литературу на английском языке и сразу добавлять незнакомые слова на тренировку.
К сожалению, как замечали многие пользователи и что не нравится также мне, это очень страшно отформатированный текст: большие отступы, текст без разделения на абзацы, нет нормального разделения на закладки понравившихся страниц.
Если бы я мог подправить этот функционал, я бы сделал интерфейс по добавлению слов так, как это сделано в словаре.
Я считаю, что добавить чекбокс к каждому слову с правой стороны не так и сложно. Тогда кнопку "LEARN WORDS" можно было бы сделать в таком стиле:
К сожалению, в данном сервисе очень не хватает очень тренировок для произношения, а также тренировок которые более направлены на восприятие английского на слух, как, например, на сайте Puzzle English.
Статья получилась как микс о постановке целей , выработке полезных привычек и как своего рода реклама сервиса LinguaLeo
Искренне желаю вам достичь тех целей и высот, к которым стремитесь!

Wednesday, March 25, 2015

Caliburn.Micro & Simple IoC контейнер

Здравствуйте, уважаемые читатели. Буквально месяц я не писал статьи в своем блоге, чему было несколько причин. Среди причин, которые хоть немного могут меня оправдать, – плотное изучение английского (поставил за цель ежедневное чтение литературы в оригинале и выучить за февраль 500 новых слов, что успешно выполнил). Плюс за этот месяц начал писать статью по паттерну UnitOfWork, которая требует много времени. 
Надеюсь, что материал сегодняшней статьи вам пригодится при использовании Caliburn.Micro для ваших проектов, и что вам не приходилось (и не придется) наступать на те "грабли", с которыми столкнулся я.
Наверняка вам приходилось слышать о таких понятиях, как IoC контейнер, внедрение зависимостей DI. Сегодня мы поговорим о том, как реализовано простое управление внедрением зависимостей с помощью SimpleContainer в Caliburn Micro. Предполагаю, вам приходилось сталкиваться с таким паттерном, как Service Locator, в таком случае, вы понимаете, как работают DI контейнеры. Основное отличие ServiceLocator от самых простых DI контейнеров заключается в том, что ServiceLocator может быть представлен в виде статического класса с набором статических методов или в виде интерфейса. Ниже вы можете посмотреть реализацию простого IoC контейнера по ссылке. Для примера взглянем на часть реализации SimpleContainer с фреймворка Caliburn.Micro. Мы не будем детально и долго останавливаться на разборе этого контейнера, поскольку он очень простой, и вы сами можете посмотреть его реализацию, например, через ILSpy.
public class SimpleContainer
{
    private class ContainerEntry : List<Func<SimpleContainerobject>>
    {
        public string Key;
        public Type Service;
    }

    private class FactoryFactory<T>
    {
        public Func<T> Create(SimpleContainer container)
        {
            return () => (T) ((object) container.GetInstance(typeof (T), null));
        }
    }

    private static readonly Type delegateType = typeof (Delegate);
    private static readonly Type enumerableType = typeof (IEnumerable);
    private readonly List<SimpleContainer.ContainerEntry> entries;

    /// <summary>
    ///   Occurs when a new instance is created.
    /// </summary>
    public event Action<object> Activated = delegate(object param0)
    {
    };
}
Для хранения связывания интерфейсов с их реализацией у нас есть коллекция entries. Все остальное – дело техники. Например, если нам нужно зарегистрировать интерфейс как Singleton, нам достаточно посмотреть, есть ли уже зарегистрированный ранее такой тип; если есть, то просто возвратить его реализацию, иначе зарегистрировать этот тип данных.
public void RegisterSingleton(Type service, string key, Type implementation)
{
    object singleton = null;
    RegisterHandler(service, key, container =>
    {
        object value;
        if ((value = singleton) == null)
        {
            value = (singleton = container.BuildInstance(implementation));
        }
        return value;
    });
}
/// <summary>
///   Registers a custom handler for serving requests from the container.
/// </summary>
/// <param name="service">The service.</param>
/// <param name="key">The key.</param>
/// <param name="handler">The handler.</param>
public void RegisterHandler(Type service, string key, Func<SimpleContainerobject> handler)
{
    this.GetOrCreateEntry(service, key).Add(handler);
}

private ContainerEntry GetOrCreateEntry(Type service, string key)
{
    ContainerEntry containerEntry = GetEntry(service, key);
    if (containerEntry == null)
    {
        containerEntry = new ContainerEntry
        {
            Service = service,
            Key = key
        };
        entries.Add(containerEntry);
    }
    return containerEntry;
}
private ContainerEntry GetEntry(Type service, string key)
{
    if (service == null)
    {
        return entries.FirstOrDefault(x => x.Key == key);
    }
    if (key == null)
    {
        ContainerEntry entry;
        if ((entry = this.entries.FirstOrDefault(x => x.Service == service && x.Key == null)) == null)
        {
            entry = this.entries.FirstOrDefault(x => x.Service == service);
        }
        return entry;
    }
    return this.entries.FirstOrDefault(x => x.Service == service && x.Key == key);
}
Как видите, ничего страшного в данном коде нет. Он очень простой. Думаю, на этом теории достаточно. Пора перейти к практике по использованию данного IoC контейнера. Я решил использовать старенький пример, на котором демонстрировал использование разных типов контейнеров, как AutofacNinject, Unity и других. Это пример по созданию электронной библиотеки, в которой можно посмотреть технические книги по программированию.
Для начала создадим новый проект  WPF Application и дадим ему имя CaliburnMicroSimpleIoC.
Следующим делом установим Caliburn.Micro фреймворк с помощью NuGet Package Manager для нашего тестового проекта. 
Затем создаем классическую структуру для проекта, который использует паттерн MVVM для работы.

Примечание. Чтобы Caliburn.Micro мог нормально связывать вашу модель представления с самим представлением, они должны быть в одном пространстве имен. Учитывайте этот факт сразу, чтобы у вас не возникало вопросов, почему не работает связывание для вашего примера.
Если посмотреть на примеры, которые можно скачать на github и которые объясняют, как работает Caliburn.Micro, то в большинстве примеров у нас представление лежит в той же папке, что и модель представления. Если вам удобен такой вариант, то вы можете его использовать. В моем примере я использую такую разбивку, наверное, больше по привычке, которую пришил к себе из-за постоянного использования паттерна MVVM в WPF
Рекомендую вам начать немного от обратного и перейти к созданию моделей, которые будут использоваться в нашем примере. Обычно в Caliburn.Micro практикуется подход ViewModel-First. в котором работа начинается с модели представления. Мы нарушим его лишь слегка, поскольку для нашей модели представления будут нужны уже готовые модели.
Начнем с интерфейса, который будет отображать информацию об электронной книге. Назовём этот интерфейс IBook и посмотрим на его реализацию.
public interface IBook
{
    string Author { getset; }
    string Title { getset; }
    DateTime Year { getset; }
    string SN { getset; }
    int Count { getset; }
}
Имплементация данного интерфейса приведена в классе Book ниже.
public class Book : PropertyChangedBaseIBook
{
    private string _author;

    public string Author
    {
        get { return _author; }
        set
        {
            _author = value;
            NotifyOfPropertyChange(() => Author);
        }
    }

    private string _title;

    public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            NotifyOfPropertyChange(() => Title);
        }
    }

    private DateTime _year;

    public DateTime Year
    {
        get { return _year; }
        set
        {
            _year = value;
            NotifyOfPropertyChange(() => Year);
        }
    }

    private string _sn;

    public string SN
    {
        get { return _sn; }
        set
        {
            _sn = value;
            NotifyOfPropertyChange(() => SN);
        }
    }

    private int _count;

    public int Count
    {
        get { return _count; }
        set
        {
            _count = value;
            NotifyOfPropertyChange(() => Count);
        }
    }
}
На данном этапе у вас не должно возникнуть каких-либо проблем. Как минимум, ваш проект должен с легкостью компилироваться.
Следующим этапом нам необходимо добавить интерфейс IBookService, задачей которого будет предоставление интерфейса для работы с книгами (отложенная подгрузка, поиск и т.д.).
public interface IBookService
{
    void GetData(Action<BindableCollection<IBook>, Exception> callback);
    IBook FindBook(IBook findBook);
    void CreateNewBook();
    void RemoveBook(IBook book);
}
Теперь посмотрим на имплементацию этого интерфейса в классе BookService.
public class BookService : IBookService
{
    #region Variable
    private BindableCollection<IBook> _books;
    #endregion

    #region Constructor
    public BookService()
    {
        _books = new BindableCollection<IBook>();
        _books.Add(new Book { Author = "Jon Skeet", Title = "C# in Depth", Count = 3, SN = "ISBN: 9781617291340", Year = new DateTime(2013, 9, 10) });
        _books.Add(new Book { Author = "Martin Fowler", Title = "Refactoring: Improving the Design of Existing Code", Count = 2, SN = "ISBN-10: 0201485672", Year = new DateTime(1999, 7, 8) });
        _books.Add(new Book { Author = "Jeffrey Richter", Title = "CLR via C# (Developer Reference)", Count = 5, SN = "ISBN-10: 0735667454", Year = new DateTime(2012, 12, 4) });
    }
    #endregion

    #region Public Methods
    public void GetData(Action<BindableCollection<IBook>, Exception> callback)
    {
        callback(_books, null);
    }

    public IBook FindBook(IBook findBook)
    {
        if (findBook == null)
            return null;

        return _books.FirstOrDefault(book => book.Author == findBook.Author
                                    && book.Title == findBook.Title
                                    && book.SN == findBook.SN
                                    && book.Year == findBook.Year);
    }

    public void CreateNewBook()
    {
        _books.Add(new Book { Author = "Test1", Title = "Test1", Count = 5, SN = "ISBN-10: 0735667454", Year = DateTime.Now });
    }

    public void RemoveBook(IBook book)
    {
        if (book == null)
            return;

        _books.Remove(book);
    }
    #endregion
}
Для теста данные добавлены вручную. Но в реальных приложениях эти данные могут быть загружены с БД, получены через WCF сервис или любыми другими способами.
Теперь нам нужно сделать несколько шагов, чтобы запустить пустой проект и посмотреть, что он работает. Если вы до этого не работали с Caliburn.Micro, вы можете почитать статью "Начало работы с Caliburn Micro", чтобы получить необходимые начальные навыки. Мы же будем быстро рассказывать о том что нужно сделать дальше. Итак, перенесем наше главное окно MainWindow в папку Views и переименуем его на ShellView. Следующим делом добавим пустую модель представления, которую назовем ShellViewModel по правилам связывания модели представления с представлением, которое по умолчанию существует в Caliburn. Если вы не знаете, как происходит такое связывание, обратитесь к ссылке, которая приведена вначале этого абзаца. Реализация данной модели представления пока будет иметь следующий вид:
public class ShellViewModel : PropertyChangedBase
{
}
Остальное наполнение мы добавим немого позже. Осталось дело за загрузчиком. Добавим новый класс ShellBootstrapper, который наследуем от класса BootstrapperBase.
public class ShellBootstrapper : BootstrapperBase
{
    public ShellBootstrapper()
        : base(true)
    {
        Initialize();
    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        DisplayRootViewFor<ShellViewModel>();
    }
}
Осталось сделать загрузку нашего загрузчика при старте нашего приложения. Для этого изменим немного наш App.xaml файл, как приведено ниже.
<Application x:Class="CaliburnMicroSimpleIoC.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:caliburnMicroSimpleIoC="clr-namespace:CaliburnMicroSimpleIoC">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <caliburnMicroSimpleIoC:ShellBootstrapper x:Key="Bootstrapper" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
Если вы не забыли, что для модели представления и для представления нужно указать одно и то же пространство имен, то вы уже сможете увидеть пустое окно. Если же вы забыли это сделать, то увидите окно с сообщением о том, что не удалось обнаружить вашу модель ShellViewModel (вы можете загрузить рабочий пример, который будет приведен в конце статьи).
Пришло время усложнить нашу задачу и добавить использование созданных ранее моделей в нашем приложении. Для этого нам нужно все это настроить в нашем классе ShellBootstrapper. Для того чтобы работать с SimpleContainer, нам необходимо переопределить как минимум три метода: GetInstance, GetAllInstances и BuildUp, как показано ниже.
private readonly SimpleContainer _container = new SimpleContainer();

protected override object GetInstance(Type serviceType, string key)
{
    var instance = _container.GetInstance(serviceType, key);
    return instance;
}

protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
    return _container.GetAllInstances(serviceType);
}

protected override void BuildUp(object instance)
{
    _container.BuildUp(instance);
}
Но для нашего примера этого, к сожалению, будет недостаточно. Наши интерфейсы не будут автоматически связаны  с реализацией. Вторая проблема заключается в том, что если мы используем SimpleContainer для управления зависимостями, нам необходимо явно указать использование WindowManager, который будет использоваться в нашем приложении. Это становится обязательным условием.
Для конфигурирования DI  в нашем приложении нужно переопределить метод Configure.
 protected override void Configure()
{
    _container.Singleton<IBookServiceBookService>();
    _container.Singleton<IWindowManagerWindowManager>();
    _container.PerRequest<IBookBook>();
    _container.PerRequest<ShellViewModel>();
}
Ниже приведена полная реализация класса ShellBootstrapper.
public class ShellBootstrapper : BootstrapperBase
{
    private readonly SimpleContainer _container = new SimpleContainer();

    public ShellBootstrapper()
    {
        Initialize();
    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        DisplayRootViewFor<ShellViewModel>();
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        var instance = _container.GetInstance(serviceType, key);
        return instance;
    }

    protected override IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return _container.GetAllInstances(serviceType);
    }

    protected override void BuildUp(object instance)
    {
        _container.BuildUp(instance);
    }

    protected override void Configure()
    {
        _container.Singleton<IBookServiceBookService>();
        _container.Singleton<IWindowManagerWindowManager>();
        _container.PerRequest<IBookBook>();
        _container.PerRequest<ShellViewModel>();
    }
}
Теперь самое время приступить к реализации самой модели представления ShellViewModel. Ниже приведена реализация данной модели представления с добавлением двух методов, с помощью которых мы можем добавлять и удалять книги. А также интерфейс IBookService, который нам позволит загружать нужную информацию о книгах.
public class ShellViewModel : PropertyChangedBase
{
    private readonly IBookService _bookDataService;

    public ShellViewModel(IBookService dataService)
    {
        _bookDataService = dataService;

        _bookDataService.GetData(
            (items, error) =>
            {
                Books = items;
            });
    }

    #region Public Properties

    public BindableCollection<IBook> Books { getset; }

    private IBook _selectedBook;
    public IBook SelectedBook
    {
        get { return _selectedBook; }
        set
        {
            if (_selectedBook != value)
            {
                _selectedBook = value
                NotifyOfPropertyChange();
                NotifyOfPropertyChange(() => CanRemoveBook);
            }
        }
    }

    #endregion

    #region Command

    public void AddNewBook()
    {
        _bookDataService.CreateNewBook();
    }

    public void RemoveBook()
    {
        Books.Remove(SelectedBook);
    }

    public bool CanRemoveBook
    {
        get
        {
            if (SelectedBook == null)
                return false;
            var findBook = _bookDataService.FindBook(SelectedBook);
            if (findBook == null)
                return false;

            return true;
        }
    }
    #endregion
}
Остались буквально последние штрихи: реализация самого представления. Небольшое напоминание для тех, кто забыл, что в Caliburn.Micro есть такая замечательная особенность, как связывание свойств модели представления с контролами в модели представления через имя x:Name, которое должно совпадать с именем свойства в VM. Еще один интересный нюанс, который касается нашего свойства SelectedBook
private IBook _selectedBook;
public IBook SelectedBook
{
    get { return _selectedBook; }
    set
    {
        if (_selectedBook != value)
        {
            _selectedBook = value
            NotifyOfPropertyChange();
            NotifyOfPropertyChange(() => CanRemoveBook);
        }
    }
}
Приставка Selected позволяет нам сразу связать наш элемент с выбранной книгой, например, в ListBox, ComboBox и других контролах, которые поддерживают выбор элементов. Раньше для того чтобы такое поведение заработало, нам  нужно было написать код, подобный коду ниже.
<ListView x:Name ="library" Grid.Column ="0" ItemsSource="{Binding Books}" SelectedItem="{Binding SelectedBook}">
Сейчас же нам не нужно явно прописывать связку с SelectedBook, за нас это автоматически сделает Caliburn.Micro. Теперь пришло время перейти к самой сложной части: к реализации UI (ShellView).
<Window x:Class="CaliburnMicroSimpleIoC.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <DataTemplate x:Key="BookDataTemplate">
                <WrapPanel cal:Bind.Model="{Binding}">
                    <TextBlock Text="Author: " />
                    <TextBlock x:Name="Author" FontWeight="Bold" />
                    <TextBlock Text=", " />
                    <TextBlock Text="Caption: " />
                    <TextBlock x:Name="Title" FontWeight="Bold" />
                    <TextBlock Text="Count: " />
                    <TextBlock x:Name="Count" FontWeight="Bold" />
                </WrapPanel>
            </DataTemplate>
        </Grid.Resources>
        
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ListView x:Name ="Books" Grid.Column ="0" ItemTemplate="{DynamicResource BookDataTemplate}" />
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid Grid.Row="0" DataContext="{Binding SelectedBook}" cal:Bind.Model="{Binding}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Label Grid.Row="0" Grid.Column="0" Content="Author" />
                <TextBox x:Name="Author" Grid.Row="0" Grid.Column="1" />
                <Label Grid.Row="1" Grid.Column="0" Content="Title" />
                <TextBox x:Name="Title" Grid.Row="1" Grid.Column="1" />
                <Label Grid.Row="2" Grid.Column="0" Content="Year" />
                <DatePicker x:Name="Year" Grid.Row="2" Grid.Column="1" />
                <Label Grid.Row="3" Grid.Column="0" Content="Serial Number" />
                <TextBox x:Name="SN" Grid.Row="3" Grid.Column="1"  />
                <Label Grid.Row="4" Grid.Column="0" Content="Count" />
                <TextBox x:Name="Count" Grid.Row="4" Grid.Column="1" />
            </Grid>

            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <Button Content="Add new book" Margin="3"  cal:Message.Attach="[Event Click] = [Action AddNewBook()]" />
                <Button Content="Remove book" Margin="3" cal:Message.Attach="RemoveBook()"/>
            </StackPanel>
        </Grid>
    </Grid>
</Window>
Вот пример, как у меня получилось сделать привязку с помощью Caliburn.Micro, оставив только в одном месте установку DataContext.
<Grid Grid.Row="0" DataContext="{Binding SelectedBook}" cal:Bind.Model="{Binding}">
Было бы интересно узнать, как этот финт можно было бы провернуть без использования установки явно DataContext. Результат того, что получилось, можно посмотреть на рисунке ниже.
Возможно, стоило как-то поиграть с View.Model свойством, но у меня это не особо получилось.
Итоги
В статье мы рассмотрели использование SimpleContainer, который идет в Caliburn.Micro, для управления зависимостями. Надеюсь, что пример поможет вам справиться с трудностями, если такие у вас возникнут.
Источники