четверг, августа 20, 2009

Про google Guice

Оказывается, guice умеет разрешать циклы в зависимостях.

Т.е. если есть острое желание, например, построить связку Presenter и View, в которой оба класса зависят друг от друга, он это разрулит, построив прокси для того, кого ему не получается построить.

Забавно, Castle.Windsor, например, этого не делает принципиально. А Ninject вообще зависает на циклических зависимостях.

четверг, августа 13, 2009

Реализация закладки для элемента управления PropertyGrid

Одной из важных фич Syringe является возможность перехватывать события, порождаемые тестируемым элементом управления.

Я решил реализовать настройку перехватчиков средствами того же PropertyGrid’а, что и используется для редактирования свойств. Тогда интерфейс оказывается максимально похож на VisualStudio и, следовательно, не вызывает удивления.

Логично было вынести список событий на отдельную закладку.

Я попробовал использовать стандартную закладку EventsTab, но не осилил. Для своей инициализации она требует слишком много зависимостей, запрашиваемых неявно через родительский контейнер. Даже если бы я уже приделал DI-контейнер (зря я это откладываю, ох, зря) и отследил все обращения, все равно я оказался бы перед необходимостью реализовывать целую кучу интерфейсов типа IDesignerHost, IEventsBindingService и пр., функциональность которых мне не очень-то нужна.

Поэтому я решил реализовать свою закладку с нуля. Ну, не совсем =) На самом деле, у родительского класса абстрактными являются только свойство TabName и метод GetProperties.

Я реализовал GetProperties так, чтобы он возвращал по PropertyDescriptor’у для каждого события в тестируемом объекте (а список событий получил через TypeDescriptor), причем для этих дескрипторов указал в качестве редактора использовать список доступных логгеров (я их реализовал два – пишущий в лог и показывающий MessageBox). Адаптацию логгеров к разным типам событий была сделана еще раньше.

Я добавил свою закладку в PropertyGrid, и, о чудо… она не появилась. Выяснилось (при просмотре в Reflector’е), что, кроме реализации абстрактных методов, есть еще одно неочевидное условие: закладка должна что-то возвращать по запросу свойства Bitmap, которое используется для выбора иконки закладки! OMFG! Перекрыв это свойство я таки достиг успеха.

Так что теперь Syringe может перехватывать события (правда, пока не умеет нормально форматировать их аргументы), а пользователь может удобно выбрать тип перехватчика. Это первая (и очень полезная, ИМХО) фича, которой принципиально нет в UserControlTestContainer.

Моя доволен =)

вторник, августа 11, 2009

Упрощение реализации INotifyPropertyChanged

Объект, корректно реализующий интерфейс INotifyPropertyChanged оказывается “наблюдаемым” (observable), т.е. изменения его состояния могут легко отслеживаться подписчиками и, например, отображаться на визуальных компонентах в актуальной форме.

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

Опять же, довольно много кода приходится писать по нескольку раз.

Общественность =) неоднократно предлагала различные решения этой проблемы – от построения прокси-INotifyPropertyChanged вокруг POCO-объекта и патча бинарников с помощью PostSharp-а до различных структур, упрощающих привязку.

Я, естественно, пошел своим путем, т.к. во всех вариантах меня что-то не устраивало =)

В итоге остановился на том, что создал интерфейс и пару реализаций (для отдельного свойства и для списка), которые можно увидеть здесь, здесь и здесь. Пример использования можно увидеть здесь. Как видно, boilerplate-а поубавилось.

Недостатки: текущим реализациям надо передать имя свойства и их события надо перенаправить классу-модели.

Первое можно забороть с помощью Reflection-а (смотреть, какое свойство вызвало метод Set()), второе – сделать класс ModelBase, а в нем метод, настраивающий подписку для всех свойств разом.

Но я пока не решил, надо ли мне это =)

Fluent data binding

В .NET есть возможность привязывать свойства графических элементов к свойствам некоторого объекта-модели.

В результате привязки изменения в одном месте отражаются в другом. Например, редактирование текста в TextBox-е может автоматически обновлять поле Text в модели. Или изменение в модели флага , привязанного к свойству Enabled может блокировать элемент управления.

Сампл можно посмотреть здесь (показана только т.н. “простая привязка”, “сложная” делается ), хотя, уверен, ни для кого ничего фундаментально нового в этом нет.

Чем это хорошо? Хорошо это тем, что существенно уменьшается объем кода, связанного с отображением данных.

Чем это плохо? Плохо это тем, что свойства как модели, так и представления кодируются строками. Со всеми вытекающими отсюда следствиями, от которых и велемудрый R# не всегда защитит. Пытается защищать от этого графический редактор в VS,  но, опять же, только частично (к тому же, я лично предпочитаю писать код, а не тыкать мышкой).

Работая над Syringe, я решил защититься от возможных проблем со строками и набросал небольшой набор классов и методов-расширений, обеспечивающих устойчивую к опечаткам и типобезопасную привязку.

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

Пример можно увидеть здесь. Как видно, код стал компактней, при его написании сложнее ошибиться.

Реализацию можно посмотреть в исходниках проекта на google.code. Там же можно посмотреть и пример привязки к спискам данных.