public static void Bind(object viewModel, DependencyObject view, object context) { var viewModelType = viewModel.GetType(); var elements = view.Descendants <FrameworkContentElement>() .Where(e => !string.IsNullOrEmpty(e.Name)) .Distinct() .ToList(); foreach (var element in elements) { var convention = ConventionManager.GetElementConvention(element.GetType()); if (convention == null) { continue; } PropertyInfo property; Type resultViewModelType; var bindPath = NotifyValueSupport.Patch(viewModelType, element.Name, out property, out resultViewModelType); if (property == null) { continue; } Bind(convention, element, bindPath, property, viewModelType); } }
public static void Register() { ConventionManager.ApplyValueConverter = ApplyValueConverter; ConventionManager.AddElementConvention <SplitButton>(ContentControl.ContentProperty, "DataContext", "Click"); ConventionManager.AddElementConvention <Run>(Run.TextProperty, "Text", "DataContextChanged"); ConventionManager.AddElementConvention <IntegerUpDown>(UpDownBase <int?> .ValueProperty, "Value", "ValueChanged"); ConventionManager.AddElementConvention <FlowDocumentScrollViewer>(FlowDocumentScrollViewer.DocumentProperty, "Document ", "DataContextChanged"); ConventionManager.AddElementConvention <DocumentViewerBase>(DocumentViewerBase.DocumentProperty, "Document ", "DataContextChanged"); ConventionManager.AddElementConvention <PasswordBox>(ContentElementBinder.PasswordProperty, "Password", "PasswordChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => { //по умолчанию для PasswordBox установлен шрифт times new roman //высота поля ввода вычисляется на основе шрифта //если расположить TextBox и PasswordBox на одном уровне то разница в высоте будет заметна //правим эту кривизну ((PasswordBox)element).FontFamily = SystemFonts.MessageFontFamily; return(ConventionManager.SetBindingWithoutBindingOverwrite(viewModelType, path, property, element, convention, convention.GetBindableProperty(element))); }; ConventionManager.AddElementConvention <MultiSelector>(ItemsControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => TryBindSelectedItems(viewModelType, path, property, element, convention, ((MultiSelector)element).SelectedItems); ConventionManager.AddElementConvention <ListBox>(ItemsControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => TryBindSelectedItems(viewModelType, path, property, element, convention, ((ListBox)element).SelectedItems); ConventionManager.AddElementConvention <ComboBox>(ItemsControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => { NotifyValueSupport.Patch(ref path, ref property); if (property.PropertyType.IsEnum) { if (NotBindedAndNull(element, ItemsControl.ItemsSourceProperty) && !ConventionManager.HasBinding(element, Selector.SelectedItemProperty)) { var items = DescriptionHelper.GetDescriptions(property.PropertyType); element.SetValue(ItemsControl.DisplayMemberPathProperty, "Name"); element.SetValue(ItemsControl.ItemsSourceProperty, items); var binding = new Binding(path); binding.Converter = new ComboBoxSelectedItemConverter(); binding.ConverterParameter = items; BindingOperations.SetBinding(element, Selector.SelectedItemProperty, binding); } } else { var fallback = ConventionManager.GetElementConvention(typeof(Selector)); if (fallback != null) { return(fallback.ApplyBinding(viewModelType, path, property, element, fallback)); } } return(true); }; ConventionManager.AddElementConvention <DataGrid>(ItemsControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => { var dataGrid = (DataGrid)element; if (dataGrid.Columns.Count > 1) { Interaction.GetBehaviors(element).Add(new CustomMenu()); } var fallback = ConventionManager.GetElementConvention(typeof(MultiSelector)); if (fallback != null) { var actualProperty = property; var result = fallback.ApplyBinding(viewModelType, path, actualProperty, element, fallback); var dummy = ""; NotifyValueSupport.Patch(ref dummy, ref actualProperty); var propertyType = actualProperty.PropertyType; if (result && propertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(propertyType)) { ConfigureDataGrid(dataGrid, propertyType.GetGenericArguments()[0]); } return(result); } return(false); }; ConventionManager.AddElementConvention <Label>(ContentControl.ContentProperty, "Content", "DataContextChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => { return(ConventionManager.SetBindingWithoutBindingOverwrite(viewModelType, path, property, element, convention, convention.GetBindableProperty(element))); }; }
public void Init(bool failfast) { MessageBus.Current.RegisterScheduler <string>(ImmediateScheduler.Instance, "db"); ConventionManager.Singularize = s => { if (s == "Taxes") { return("Tax"); } if (s == "Value") { return(s); } return(s.Singularize() ?? s); }; //нужно затем что бы можно было делать модели без суффикса ViewModel //достаточно что бы они лежали в пространстве имен ViewModels ViewLocator.NameTransformer.AddRule( @"(?<nsbefore>([A-Za-z_]\w*\.)*)(?<subns>ViewModels\.)" + @"(?<nsafter>([A-Za-z_]\w*\.)*)(?<basename>[A-Za-z_]\w*)" + @"(?!<suffix>ViewModel)$", "${nsbefore}Views.${nsafter}${basename}View"); //что бы не нужно было использовать суффиксы View и ViewModel ViewLocator.NameTransformer.AddRule( @"(?<nsbefore>([A-Za-z_]\w*\.)*)(?<subns>ViewModels\.)" + @"(?<nsafter>([A-Za-z_]\w*\.)*)(?<basename>[A-Za-z_]\w*)" + @"(?!<suffix>)$", "${nsbefore}Views.${nsafter}${basename}"); //безумие - сам по себе Caliburn если не найден view покажет текст Cannot find view for //ни исключения ни ошибки в лог ViewLocator.LocateForModelType = (modelType, displayLocation, context) => { var viewType = ViewLocator.LocateTypeForModelType(modelType, displayLocation, context); if (viewType == null) { if (failfast) { throw new Exception(String.Format("Не удалось найти вид для отображения {0}", modelType)); } else { log.ErrorFormat("Не удалось найти вид для отображения {0}", modelType); } } return(viewType == null ? new TextBlock() : ViewLocator.GetOrCreateViewType(viewType)); }; Conventions.Register(); SaneCheckboxEditor.Register(); NotifyValueSupport.Register(); var customPropertyBinders = new Action <IEnumerable <FrameworkElement>, Type>[] { EnabledBinder.Bind, VisibilityBinder.Bind, }; var customBinders = new Action <Type, IEnumerable <FrameworkElement>, List <FrameworkElement> >[] { //сначала должен обрабатываться поиск и только потом переход SearchBinder.Bind, NavBinder.Bind, EnterBinder.Bind, }; var defaultBindProperties = ViewModelBinder.BindProperties; var defaultBindActions = ViewModelBinder.BindActions; var defaultBind = ViewModelBinder.Bind; ViewModelBinder.Bind = (viewModel, view, context) => { if ((bool)view.GetValue(ViewModelBinder.ConventionsAppliedProperty)) { return; } defaultBind(viewModel, view, context); ContentElementBinder.Bind(viewModel, view, context); Commands.Bind(viewModel, view, context); var baseScreen = viewModel as BaseScreen; //этот метод будет вызваться каждый раз при активации\деактивации формы //что бы избежать множественных подписок if (baseScreen != null && !baseScreen.ResultsSink.HasObservers) { baseScreen.ResultsSink .CatchSubscribe(r => Coroutine.BeginExecute(new List <IResult> { r }.GetEnumerator(), new ActionExecutionContext { View = view }), baseScreen.CloseCancellation); } var baseShell = viewModel as BaseShell; if (baseShell != null && !baseShell.ResultsSink.HasObservers) { baseShell.ResultsSink .CatchSubscribe(r => Coroutine.BeginExecute(new List <IResult> { r }.GetEnumerator(), new ActionExecutionContext { View = view }), baseShell.CancelDisposable); } }; ViewModelBinder.BindProperties = (elements, type) => { foreach (var binder in customPropertyBinders) { binder(elements, type); } return(defaultBindProperties(elements, type)); }; ViewModelBinder.BindActions = (elements, type) => { var binded = defaultBindActions(elements, type).ToList(); foreach (var binder in customBinders) { binder(type, elements, binded); } return(elements); }; }