Контрол EntityEntry - QualitySolution/QSProjects GitHub Wiki
Данный элемент управления в интерфейсе пользователя, позволяет выбрать сущность из любого справочника. Так как эволюция библиотеки со временем породила как минимум 3 версии этого элемента. При переходе на диалоги с ViewModel был сделан универсальный элемент управления, реализующий обшую функциональность, а все взаимодействие с внешним миром было вынесено в отдельные классы, позволяющие легко менять то, как элемент управления работает.
Сам элемент управления построен из двух основных классов ViewModel и View. К ViewModel-и можно подцеплять различные модули(классы), каждый из которых реализует одну из функций элемента управления, что позволяет комбинировать различные реализации возможностей элемента управления или реализовывать свои. Не один из модулей не является обязательным, поэтому если он отсутствует, данная возможность элемента управления, будет просто неактивна.
К ViewModel можно подключить модули реализующие следующие интерфейсы:
Модуль, реализующий возможность выбора элемента справочника через открытие диалога справочника.
Реализации:
- JournalViewModelSelector<TEntity, TJournalViewModel> - открывает журналы нового стиля с ViewModel, основанные на классе JournalViewModelBase.
- JournalViewModelSelector<TEntity, TJournalViewModel, TJournalFilterViewModel> - Открывает журналы ViewModel с установкой параметров фильтра.
- OrmReferenceSelector - открывает журнал самого старого стиля, реализуемый классом OrmReference, который работал с сущностями напрямую.
Модуль, реализующий возможность пользователю выбрать элемент справочника через автодополнение при написании текста внутри самого контрола.
Реализации:
- JournalViewModelAutocompleteSelector<TEntity, TJournalViewModel> - автодополнение, работающее на основе новых журналов JournalViewModelBase.
- JournalViewModelAutocompleteSelector<TEntity, TJournalViewModel, TJournalFilterViewModel> - автодополнения, с установкой параметров фильтра.
- OrmReferenceAutocompleteSelector<TEntity> - автодополнение, работающее на базе запроса NHibernate к базе, получающего сущности целиком. Реализация работает тем же способом, как виджет, открывающий журналы OrmReference.
Модуль, позволяющий пользователю открыть диалог редактирования для содержащейся в элементе управления сущности.
Реализации:
- EntityViewModelOpener<TEntityViewModel> - Открывает диалоги на базе ViewModel.
- OrmObjectDialogOpener<TEntity> - Открывает диалог на базе TDI, используя настройки в OrmMain для связывания типа сущности с типом диалога. Также имеет возможность открыть простой диалог редактирования.
Модуль, позволяющий привязывать элемент управления внешним объектам.
Пока имеется только одна реализация:
- PropertyBinder<TBindedEntity, TProperty> - Позволяет связать
Модуль, реализующий загрузку объекта справочника необходим, так как большинство журналов после выбора пользователем возвращают node, этот модуль позволяет из node получить сущность. При этом при необходимости, привязанную именно к UnitOfWork диалога.
Реализации:
- UowEntityAdapter<TEntity> - Получает из node свойство Id, по нему подгружает в текущим UnitOfWork сущность TEntity. Также подписывается на изменения объектов этого типа и автоматически обновляет сущности в диалоге.
- FuncEntityAdapter<TEntity> - При создании получает внешний делегат, который вызывает для преобразования node к TEntity, что позволяет внутри диалога устроить любое преобразование или загрузку сущности любым способом.
ViewModel элемента управления можно создать вручную и в нестандартных ситуациях это возможно единственный способ. Но, так как создание модели достаточно сложное, и в конструкторы каждого из модулей обычно нужно передать множество однообразной информации, созданы специальные сборщики, позволяющие упростить этот процесс в коде.
В общем случае процесс создания ViewModel-ей для EntityEntry выглядит из двух этапов, на первом мы создаем билдер(фабрику), в которую передаем все основные параметры для текущего диалога. На втором уже из этой фабрики создаем сколько угодно ViewModel-ей для различных свойств.
Существует несколько реализаций фабрики для разных случаев использования:
- CommonEEVMBuilderFactory - самый простой вариант фабрики создающий EntityEntry только для журналов JournalViewModelBase и диалогов ViewModel, при этом элементы управления не связываются со свойствами ViewModel-и диалога или свойствами редактируемой сущности.
- CommonEEVMBuilderFactory<TBindedEntity> - тоже самое что и предыдущий, но позволяющие привязать элемент управления к свойству объекта, реализующего INotifyPropertyChanged. Предполагается что это будет основной билдер для проектов полностью перешедших на ViewModel.
- MagicEEVMBuilderFactory<TBindedEntity> - тоже самое что и предыдущий, но для диалогов типа EntityDialogViewModelBase<TEntity>. Сборщик сам берет большинство параметров из диалога.
- LegacyEEVMBuilderFactory<TBindedEntity> - вариант сборщика поддерживающего устаревшие проекты. В дополнении к предыдущим, позволяющий как создавать EntityEntry для устаревших журналов и диалогов, так и размещать этот контрол внутри старого диалога на базе TDI.
- LegacyEEVMBuilderFactory - аналогичный предыдущему, но без связывания со свойством.
Создаем обычный билдер и EntityEntryViewModel для одного из свойств сущности.
var builder = new CommonEEVMBuilderFactory<Employee>(this, Entity, UoW, NavigationManager, AutofacScope);
EntryLeaderViewModel = builder.ForProperty(x => x.Leader)
.UseViewModelJournalAndAutocompleter<LeadersJournalViewModel>()
.UseViewModelDialog<LeadersViewModel>()
.Finish();
Создание EntityEntryViewModel в диалоге TDI для выбора сущности нового журнала.
var builder = new LegacyEEVMBuilderFactory<Expense>(this, Entity, UoW, MainClass.MainWin.NavigationManager, AutofacScope);
entryEmployee.ViewModel = builder.ForProperty(x => x.Employee)
.UseViewModelJournalAndAutocompleter<EmployeeJournalViewModel>()
.UseViewModelDialog<EmployeeViewModel>()
.Finish();
Пример открытия из диалога ViewModel справочников старого OrmReference и нового образца ViewModel.
var entryBuilder = new LegacyEEVMBuilderFactory<IssuanceSheet>(this, TdiTab, Entity, UoW, navigationManager) {
AutofacScope = AutofacScope
};
OrganizationEntryViewModel = entryBuilder.ForProperty(x => x.Organization)
.UseViewModelJournalAndAutocompleter<OrganizationJournalViewModel>()
.UseViewModelDialog<OrganizationViewModel>()
.Finish();
SubdivisionEntryViewModel = entryBuilder.ForProperty(x => x.Subdivision)
.UseOrmReferenceJournalAndAutocompleter()
.UseTdiEntityDialog()
.Finish();
Фильтр журнала может быть преднастроенным. Как показано ниже:
NomenclatureEntryViewModel = entryBuilder.ForProperty(x => x.Nomenclature)
.UseViewModelJournalAndAutocompleter<NomenclatureJournalViewModel, NomenclatureFilterViewModel>(f => f.ProtectionTools = protectionTools, f => f.ShowArchived = true)
.UseViewModelDialog<NomenclatureViewModel>()
.Finish();
Общий и легаси билдеры поддерживаю автоматический подбор класса журнала и класса диалога. Для этого требуется чтобы AutoFac мог создать реализацию для интерфейса IViewModelResolver
. На текущий момент имеется реализация AutofacViewModelReslover
этого интерфейса. Пример необходимой настройки в AutoFac:
builder.Register(x => new AutofacViewModelResolver(AppDIContainer)).As<IViewModelResolver>();
Данная реализация ищет по всем ViewModel-ям зарегистрированным в AutoFac. Журналы должен быть создан на базе класса EntityJournalViewModelBase
, а диалог на базе EntityDialogViewModelBase
, тогда решатель сможет найти подходящую Viewmodel по типу сущности для которой она сделана. Если ViewModel-ей для сущьности несколько то в результате подбора произойдет Exception. Если в результате подбора ViewModel не будет найдена то решатель вернет null. А билдер просто не создаст селектор или класс открытия диалога. Такое поведение позволит не падать, если в наличии только ViewModel журнала или только ViewModel диалога.
Пример использования в коде диалога
entryEmployee.ViewModel = builder.ForProperty(x => x.Employee)
.MakeByType()
.Finish();