понедельник, 27 декабря 2010 г.

iOS Silverlight theme

Стиль iOS очень продуман и очень не плох. Немного подумав, решил реализовать наиболее интересные контролы этой операционной системы в Silverlight. Ниже представлен “Переключатель” в стиле IPhone.

IPhone toggle button
Загрузить исходный код можно здесь.
Новые компоненты буду выкладывать по мере реализации.

пятница, 17 декабря 2010 г.

Autofac extensions for PRISM 4

PRISM
Prism это набор практик , для более простого проектирование богатых, гибких и легких в обслуживании Windows Presentation Foundation (WPF), десктоп приложений, Silverlight Rich Internet Applications (RIA), и Windows 7 Phone приложений. Использование паттернов, воплощают важные архитектурные принципы дизайна, такие как разделение ответственности и слабую связь. Prism позволяет проектировать и строить приложения с использованием слабо связанных компонентов, которые могут развиваться самостоятельно, но которые могут быть легко интегрированы. Такой тип приложений известен как «Модульные приложения».
Последняя версия Prism направленна на Microsoft. NET Framework 4.0 и Silverlight 4 и включает в себя новый принцип реализации модели Model-View-ViewModel (MVVM) паттерна, навигации и Managed Extensibility Framework (MEF).

Подробно.

Autofac
Autofac это IoC контейнер для Microsoft .NET. Он управляет зависимостями между классами таким образом, чтобы приложения оставалось легко изменяемым по мере роста  размера и сложности. Это достигается путем обработки обычных классов в виде компонентов.
Последняя версия (2.3) распространяется в виде одной dll’ки размером около 100KB.
Подробно.

Enable Autofac in PRISM
Контейнер Инъекции Зависимостей (Dependency injection containers), дальше просто "контейнер" используются для удовлетворения зависимостей между компонентами; удовлетворение таких зависимостей, как правило, предполагает регистрацию и разрешение компонентов. Из коробки призм обеспечивает поддержку Unity контейнер и Mef, но он является независимым от конкретной реализации контейнера. Поскольку библиотека получает доступ к контейнеру через интерфейс IServiceLocator, он может быть заменен. Для этого, ваш контейнер должен реализовать  интерфейс IServiceLocator. Обычно, если вы заменяете контейнер, вы должны также предоставить собственный загрузчик приложения (Bootstrapper) который зависит от реализации контейнера. Какой контейнер использовать – дело личных предпочтений. Я предпочитаю использовать Autofac, поэтому для того чтобы связать его и Prism, необходимо написать собственную реализацию Bootstrapper и IServiceLocator.
Prism очень гибок и расширяем, что позволяет его «заточить» под решение конкретной задачи. При этом архитектура приложения будет «правильной», т.е. готова к изменениям на любом уровне и без последствий (ну или с минимальными). Что еще не маловажно, Prism помогает писать тестируемые приложения с помощью UnitTest’ов.

AutofacBootstrapper
AutofacBootstrapper – реализация абстрактного класса Bootstrapper. Данный класс обеспечивает последовательность начальной загрузки приложения, и регистрирует необходимые компоненты в контейнере Autofac. Реализация ключевых методов класса AutofacBootstrapper выглядит сл. образом:
 
 public abstract class AutofacBootstrapper : Bootstrapper
 {
  
  protected virtual IContainer CreateContainer()
  {
   return ConfigureContainer().Build();
  }

  protected virtual ContainerBuilder ConfigureContainer()
  {
    var containerBuilder = new ContainerBuilder();

    containerBuilder.RegisterInstance(Logger);
    containerBuilder.RegisterInstance(ModuleCatalog);
    containerBuilder.RegisterSource(new ResolveAnythingRegistrationSource());

    if (_useDefaultConfiguration)
    {
      containerBuilder.Register(x => new AutofacServiceLocatorAdapter(Container))
      .As<IServiceLocator>().SingleInstance();
   
      containerBuilder.RegisterType<ModuleInitializer>()
      .As<IModuleInitializer>().SingleInstance();
   
      containerBuilder.RegisterType<ModuleManager>().
      As<IModuleManager>().SingleInstance();
   
      containerBuilder.RegisterType<EventAggregator>()
      .As<IEventAggregator>().SingleInstance();

      containerBuilder.RegisterModule(new RegionInitializerModule());
      containerBuilder.RegisterModule(new RegionAdapterInitializerModule());
    }

    return containerBuilder;
  }

  public override void Run(bool runWithDefaultConfiguration)
  {
   //run workflow
  }
 }

Метод “ConfigureContainer” конфигурирует контейнер и возвращает объект, «ContainerBuilder» который, после операции “Build” превратится в рабочий контейнер. В обязательном порядке, контейнер регистрируются “Logger” и “ModuleCatalog”. В контейнер они регистрируются как созданные и сконфигурированные объекты, потому при инъекциях, не будут создаваться новые, а будут использоваться те, что использовались при регистрации. Если приложение использует конфигурацию по умолчанию, в контейнер регистрируются следующие компоненты:
•    AutofacServiceLocatorAdapter - адаптер контейнера, для абстракции от конкретной реализации контейнера
•    ModuleInitializer – компонент, который создает экземпляр загружаемого модуля и инициализирует его.
•    ModuleManager – компонент, который производит загрузку удаленного модуля и зависимостей. После загрузки, модуль инициализируется при помощи компонента “ModuleInitializer”.
•    EventAggregator – компонент, который реализует одноименный паттерн.
•    RegionInitializerModule – класс, который производит настройку компонентов для работы с регионами.

Также в контейнер регистрируется компонент “ResolveAnythingRegistrationSource”, который расширяет возможности Autofac способностью создавать типы не зарегистрированые в нем непосредственно. Это необходимо для создания экземпляров модулей, которые описаны с помощью структуры «ModuleInfo», которая обеспечивает позднее связывание.
Это метод необходимо перегрузить для регистрации собственных компонентов. Ниже представлен пример из кода “Quickstarts- Modularity”:
protected override ContainerBuilder ConfigureContainer()
{
 var containerBuilder = base.ConfigureContainer();

 containerBuilder.RegisterType()
                 .As().SingleInstance();
 containerBuilder.RegisterInstance(callbackLogger);
 containerBuilder.RegisterType();

 return containerBuilder;
}
Метод «CreateContainer» создает контейнер из сконфигурированного «СontainerBuilder». Практическт во всех случаях его не нужно переопределять.

Метод «Run» запускает процесс инициализации приложения в строго определенной последовательности.


Последовательность инициализиции компонентов

 
Данную последовательность необходимо придерживать при реализации собственного метода.
Параметр, передаваемый в метод, регулирует процесс инициализиции. Если  метод был вызван с параметром «True», то нициализация пройдет в обычном режиме и будут проинициализированы и зарегистрированы все необходимые компоненты. Иначе, класс наследник обязывается провести инициализацию этих компонентов самостоятельно. В большинстве случаев нет необходимости менять логику работы этого метода.

AutofacServiceLocatorAdapter
Определяет адаптер для интерфейса IServiceLocator, которые будут использоваться Composite Application библиотеки. Интерфейс IServiceLocator находится  в  Common Service Locator библиотеке. Эта библиотека с открытым исходным кодом, предназначена для того, чтобы обеспечить абстракцию от IoC (Inversion of Control) контейнера, и сервис локатора (service locator). Цель использования этой библиотеки заключается в привлечении IoC и Service Location без привязки к конкретной реализации. Prism не ссылается или полагаться на конкретный контейнер, но приложение может полагаться на конкретный контейнер. Это означает, что целесообразно для конкретного приложения ссылаться на контейнер, но Prism этого не делает непосредственно.

RegionInitializerModule
Данный модуль использоваться для расслоения на множество связанных с ним компонентов для упрощения настройки и развертывания. Модуль предоставляет преднамеренно ограниченный набор конфигурационных параметров, которые могут изменяться независимо от компонентов, используемых для реализации модуля.

ResolveAnythingRegistrationSource
Данное расширание к Autofac позволяет решить проблему инстенцирования компонентов,  которые не были зарегистрированиы в контейнер во время инициализации приложения. Это нужно в момент загрузки и инициализации распределенных модулей приложения. Необходимо отметить, что его использование для инстенцеирование заранее известных компонентов не рекомендуется, так как скорость поиска объектов в контейнере снижается.

Source code

суббота, 9 октября 2010 г.

Rhino Mocks 3.6 for Silverlight


Порт популярной библиотеки (Rhino Mocks 3.6) для создания Mock объектов на платформу Silverlight. Странно что этого не сделал автор библиотеки. Скачать можно здесь.


Update: Core 2.5.1 - September 21st, 2010, исправлены ошибки портирования

пятница, 1 октября 2010 г.

Money sidebar Gadget



Начало.
Money sidebar gadget - программа которая встраивается в боковую панель Windows и отображает текущие курсы валют в Украине с возможностью выбора определенной территории для более точного курса. Также есть возможность отслеживания динамики курсов валют по графику. Программа написана с помошью технологии Silverlight 4, поэтому он должен быть установлен на компьютер. Последнюю версию всегда можно скачать с офф. сайта MicrosoftGadget можно скачать с Windows Live Gallery или архивом отсюда. Архиватор здесь.
Все данные, гаджет получает с сайта finance.ua.
Money sidebar gadget не работает на 64-битных системах сразу, как этого хотелось бы. Для этого надо поставить в автозагрузку 32-х битную версию Sidebar. 
Вариант I:
  1. Запустить файл "RegSidebar86.reg"(лежит в архиве вместе с gadget, или скачать) для того чтобы  поставить в автозагрузку 32-х битную версию Sidebar;
  2. Сделать Logoff/Logon;
  3. Добавить гаджет из репозитория.
Вариант II:
  1. Запустить диспетчер задач и убить процесс "sidebar.exe";
  2. Запустить файл "RegSidebar86.reg"(лежит в архиве вместе с gadget, или скачать) для того чтобы  поставить в автозагрузку 32-х битную версию Sidebar;
  3. Запустить самостоятельно процесс "sidebar" (C:\Program Files (x86)\Windows Sidebar\sidebar.exe)
  4. Добавить гаджет из репозитория.

    Все варианты жизнеспособны, но второй вариант мне нравится больше :).

    О чем речь


    Главное окно приложения
    В главном окне программ, помимо курса валют, отображена информация о регионе для которого актуален данный курс. Выбрать регион для которого отображать курсы валют можно в настройках.

    График динамики валюты
    Щелкнув дважды по коду курса валют, откроется график на котором представлена динамика изменения данной валюты.
    Окно настроек приложения
    В настройках приложения можно указать период с которым необходимо выполнять обновления информации. Период обновления указывается в минутах и может находиться в диапазоне от 15 до 720 мин.(12 часов). Также можно выбрать источник (в перспективе); в данный момент он только один.
    Выбор региона
    В поле "Select region" можно указать регион для которого отображать курсы валют. В противном случае он будет браться усредненный по всей Украине, что соответствует значение "None".
    Должен отметить, что все доступные регионы загружаются с сервера, поэтому если возникают проблемы не сервере, приложение не может их отобразить. При этом будет соответствующее сообщение.



    Требования
    • Windows 7 :)
    • Silverlight 4
    • Подключение к интернету :)
    Дальнейшее развитие
    Прежде всего необходимо добавить несколько провайдеров, также считать статистику о потребляемом трафике и убрать вкладку Log :).
    Затем "причесать" код и открыть его народу.
    Решить проблему с работой на платформе х64.
    Реализовать способ авто-обновлений.

    ЗЫ. Благодарность Димону "З" за тестирование ;).

    пятница, 16 июля 2010 г.

    Презинтация патерна MVVM


    Классная презентация. Почти все расставила по своим местам.

    http://houseofbilz.net/codemash/MVVM.ppt

    1. Уменьшите или исключите целиком ваш code-behind
    2. "Прибивайте" все операции ввода-вывода пользовательского интерфейса к вашей ViewModel
    3. Реализуйте INotifyPropertyChanged у вашей ViewModel
    4. Разместите поведение View во ViewModel
    5. Не размещайте никакого визуального состояния в вашу модель (логично)
    6. К модели "прибивайте" только те объекты, которые не содержат никакой UI-специфичной информации (похоже моя идея с локализованными сущностями была забанена)
    7. Во время тестирования относитесь ко ViewModel как к настоящему UI (это прокатит только если мы code behind сможем свести к нулю)
    8. Избегайте событий. Используйте вместо них команды

    суббота, 10 апреля 2010 г.

    Entity Framework 4 & Func предикат



    В одном и проектов был разработан слой доступа к данным через паттерн Repository и Entity Framework 4 (code only). Интерфейс репозтория выглядел следующим образом:

    public interface IRepository<T> where T : class
    {
        IEnumerable<T> Find(Func<T, bool>; where);
            
        IEnumerable<T> Find(Specification<T> specification);
    
        T Single(Func<T, bool> where);
    
        T Single(Specification<T> specification);
    
        void Delete(T entity);
    
        void Add(T entity);
    }
    

    Пока все хорошо, но при реализации первого же метода и анализе запроса к базе данных было обнаружено (совсем не обноружено) что отсутствует условие. Т.е. запрос к базе приходит на выборку всех данных и их последующую обработку на стороне клиента (клиент по отношению к базе данных). Конечно, это не простительное убийство ресурсов машины.

    Решение очень протое. Вместо использования предикатов типа:

    Func<T, bool>
    необходимо использовать:
    Expression<Func<TEntity, bool>>
    

    Для полноты приведу пример реализации интерфейса репозитория

    public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private readonly IObjectContext _objectContext = null;
        private readonly IObjectSet<TEntity> _objectSet = null;
    
        public BaseRepository(IObjectContext objectContext)
        {
            if (objectContext == null)
                throw new ArgumentNullException("objectContext");
    
            _objectContext = objectContext;
            _objectSet = _objectContext.CreateObjectSet<TEntity>();
        }
    
        protected IQueryable<TEntity> GetQuery()
        {
            return _objectSet;
        }
    
        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> where)
        {
            return _objectSet.Where(where);
        }
    
        public IEnumerable<TEntity> Find(Specification<TEntity> specification)
        {
            return specification.SatisfyingElementsFrom(GetQuery());
        }
    
        public TEntity Single(Specification<TEntity> specification)
        {
            return _objectSet.SingleOrDefault(specification.MatchingCriteria);
        }
    
        public TEntity Single(Expression<Func<TEntity, bool>> where)
        {
            return _objectSet.SingleOrDefault(where);
        }
    
        public void Delete(TEntity entity)
        {
            _objectSet.DeleteObject(entity);
        }
    
        public void Add(TEntity entity)
        {
            _objectSet.AddObject(entity);
        }
    }
    

    Полезные ссылки:


    пятница, 19 марта 2010 г.

    Silverlight DataForm и стилизация генерируемых им компонентов

    Silverlight DataForm очень мощный компонент. С его помощью можно легко управлять данными. При этом в нашем распоряжении есть компонент для валидатции пользовательского ввода. При всем этом великолепии есть один недостаток - установить стиль генерируемого им компонента очень сложно. Но это с первого взгляда :).
    Чтобы установить кастомные стили генерируемым компонентам, воспользуемся способом, которым Вы все наверняка пользовались (указав стиль командным кнопкам DataForm'ы).
     Для начала необходимо создать компонент наследник DataForm и назвать его, например, CustomDataForm.

    public class CustomDataForm : DataForm
    {
    }
    
    Теперь необходимо создать DependencyProperty для всех компонентов, которые хотим закостомезировать. Напомню, что DependencyProperty необходимо использовать для того, чтобы работал Binding. Рекомендую воспользоваться Code Snippets.

    public class CustomDataForm : DataForm
    {
        public static readonly DependencyProperty TextBoxStyleProperty =
            DependencyProperty.Register("TextBoxStyle", typeof (Style), typeof (CustomDataForm), new PropertyMetadata(null));
    
        /// 
        /// TextBox style
        /// 
        public Style TextBoxStyle
        {
            get { return (Style)GetValue(TextBoxStyleProperty); }
            set { SetValue(TextBoxStyleProperty, value); }
        } 
    }
    
    Далее обработаем событие генерации интересующего контрола обернутого в компонент DataField.
    /// 
    ///Extends  by replacing es with es
    ///whenever applicable
    /// 
    protected override void OnAutoGeneratingField(DataFormAutoGeneratingFieldEventArgs e)
    {
        //устанавливаем стили 
        SetStyling(e.Field);
    
        //Пробросим событие дальше
        base.OnAutoGeneratingField(e);
    }
    

    И собственно реализация метода SetStyling(DataField f)

    private void SetStyling(DataField field)
    {
        if(field.Content is TextBox)
        {
            field.Content.Style = TextBoxStyle;
        }
    } 
    

    Как видите все очень просто и элегантно ;).
    Понятно, что данный пример предоставляет стилизацию одного компонента, но таким же образом можно сделать и для всех компонентов.

    Ниже представлен полный код.

    public class CustomDataForm : DataForm
    {
        public static readonly DependencyProperty TextBoxStyleProperty =
                DependencyProperty.Register("TextBoxStyle", typeof (Style), typeof (CustomDataForm), new PropertyMetadata(null));
           
        /// 
        /// TextBox style
        /// 
        public Style TextBoxStyle
        {
            get { return (Style)GetValue(TextBoxStyleProperty); }
            set { SetValue(TextBoxStyleProperty, value); }
         } 
           
        // 
        ///     Extends  by replacing es with es
        ///     whenever applicable
        /// 
        protected override void OnAutoGeneratingField(DataFormAutoGeneratingFieldEventArgs e)
        {
            //устанавливаем стили 
            SetStyling(e.Field);
    
            //Пробросим событие дальше
            base.OnAutoGeneratingField(e);
        }
       
        private void SetStyling(DataField field)
        {
            if(field.Content is TextBox)
            {
                field.Content.Style = TextBoxStyle;
            }
        } 
    }
    


    Newer Posts Older Posts