namespace Caliburn.Micro { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; #if WinRT81 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Markup; using EventTrigger = Microsoft.Xaml.Interactions.Core.EventTriggerBehavior; using Windows.UI.Xaml.Shapes; #else using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Markup; using System.Windows.Shapes; using EventTrigger = System.Windows.Interactivity.EventTrigger; #endif #if !SILVERLIGHT && !WinRT using System.Windows.Documents; using Caliburn.Micro.Core; #endif /// /// Used to configure the conventions used by the framework to apply bindings and create actions. /// public static class ConventionManager { static readonly ILog Log = LogManager.GetLog(typeof(ConventionManager)); /// /// Converters to/from . /// public static IValueConverter BooleanToVisibilityConverter = new BooleanToVisibilityConverter(); /// /// Indicates whether or not static properties should be included during convention name matching. /// /// False by default. public static bool IncludeStaticProperties = false; /// /// Indicates whether or not the Content of ContentControls should be overwritten by conventional bindings. /// /// False by default. public static bool OverwriteContent = false; /// /// The default DataTemplate used for ItemsControls when required. /// public static DataTemplate DefaultItemTemplate = (DataTemplate) #if SILVERLIGHT || WinRT XamlReader.Load( #else XamlReader.Parse( #endif #if WinRT "" + "" + "" #else " " + "" + "" #endif ); /// /// The default DataTemplate used for Headered controls when required. /// public static DataTemplate DefaultHeaderTemplate = (DataTemplate) #if SILVERLIGHT || WinRT XamlReader.Load( #else XamlReader.Parse( #endif "" ); static readonly Dictionary ElementConventions = new Dictionary(); /// /// Changes the provided word from a plural form to a singular form. /// public static Func Singularize = original => { return original.EndsWith("ies") ? original.TrimEnd('s').TrimEnd('e').TrimEnd('i') + "y" : original.TrimEnd('s'); }; /// /// Derives the SelectedItem property name. /// public static Func> DerivePotentialSelectionNames = name => { var singular = Singularize(name); return new[] { "Active" + singular, "Selected" + singular, "Current" + singular }; }; /// /// Creates a binding and sets it on the element, applying the appropriate conventions. /// /// /// /// /// /// /// public static Action SetBinding = (viewModelType, path, property, element, convention, bindableProperty) => { #if WinRT var binding = new Binding { Path = new PropertyPath(path) }; #else var binding = new Binding(path); #endif ApplyBindingMode(binding, property); ApplyValueConverter(binding, bindableProperty, property); ApplyStringFormat(binding, convention, property); ApplyValidation(binding, viewModelType, property); ApplyUpdateSourceTrigger(bindableProperty, element, binding, property); BindingOperations.SetBinding(element, bindableProperty, binding); }; /// /// Applies the appropriate binding mode to the binding. /// public static Action ApplyBindingMode = (binding, property) => { #if WinRT var setMethod = property.SetMethod; binding.Mode = (property.CanWrite && setMethod != null && setMethod.IsPublic) ? BindingMode.TwoWay : BindingMode.OneWay; #else var setMethod = property.GetSetMethod(); binding.Mode = (property.CanWrite && setMethod != null && setMethod.IsPublic) ? BindingMode.TwoWay : BindingMode.OneWay; #endif }; /// /// Determines whether or not and what type of validation to enable on the binding. /// public static Action ApplyValidation = (binding, viewModelType, property) => { #if SILVERLIGHT || NET45 if (typeof(INotifyDataErrorInfo).IsAssignableFrom(viewModelType)) { binding.ValidatesOnNotifyDataErrors = true; binding.ValidatesOnExceptions = true; } #endif #if !WinRT if (typeof(IDataErrorInfo).IsAssignableFrom(viewModelType)) { binding.ValidatesOnDataErrors = true; binding.ValidatesOnExceptions = true; } #endif }; /// /// Determines whether a value converter is is needed and applies one to the binding. /// public static Action ApplyValueConverter = (binding, bindableProperty, property) => { if (bindableProperty == UIElement.VisibilityProperty && typeof(bool).IsAssignableFrom(property.PropertyType)) binding.Converter = BooleanToVisibilityConverter; }; /// /// Determines whether a custom string format is needed and applies it to the binding. /// public static Action ApplyStringFormat = (binding, convention, property) => { #if !WinRT if(typeof(DateTime).IsAssignableFrom(property.PropertyType)) binding.StringFormat = "{0:d}"; #endif }; /// /// Determines whether a custom update source trigger should be applied to the binding. /// public static Action ApplyUpdateSourceTrigger = (bindableProperty, element, binding, info) => { #if SILVERLIGHT && !SL5 ApplySilverlightTriggers( element, bindableProperty, x => x.GetBindingExpression(bindableProperty), info, binding ); #elif WinRT81 || NET binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; #endif }; static ConventionManager() { #if WINDOWS_UWP AddElementConvention(SplitView.ContentProperty, "IsPaneOpen", "PaneClosing").GetBindableProperty = delegate (DependencyObject foundControl) { var element = (SplitView)foundControl; if (!OverwriteContent) return null; Log.Info("ViewModel bound on {0}.", element.Name); return View.ModelProperty; }; #endif #if !WINDOWS_PHONE && !WinRT AddElementConvention(DatePicker.SelectedDateProperty, "SelectedDate", "SelectedDateChanged"); #endif #if WinRT81 AddElementConvention(DatePicker.DateProperty, "Date", "DateChanged"); AddElementConvention(TimePicker.TimeProperty, "Time", "TimeChanged"); AddElementConvention(Hub.HeaderProperty, "Header", "Loaded"); AddElementConvention(HubSection.HeaderProperty, "Header", "SectionsInViewChanged"); AddElementConvention(MenuFlyoutItem.TextProperty, "Text", "Click"); AddElementConvention(ToggleMenuFlyoutItem.IsCheckedProperty, "IsChecked", "Click"); #endif #if WinRT81 && !WP81 AddElementConvention(SearchBox.QueryTextProperty, "QueryText", "QuerySubmitted"); #endif #if WinRT AddElementConvention(ToggleSwitch.IsOnProperty, "IsOn", "Toggled"); AddElementConvention(ProgressRing.IsActiveProperty, "IsActive", "Loaded"); AddElementConvention(Slider.ValueProperty, "Value", "ValueChanged"); AddElementConvention(RichEditBox.DataContextProperty, "DataContext", "TextChanged"); #endif #if WP81 || WINDOWS_UWP AddElementConvention(Pivot.ItemsSourceProperty, "SelectedItem", "SelectionChanged") .ApplyBinding = (viewModelType, path, property, element, convention) => { if (!SetBindingWithoutBindingOrValueOverwrite(viewModelType, path, property, element, convention, ItemsControl.ItemsSourceProperty)) { return false; } ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path); ApplyItemTemplate((ItemsControl)element, property); return true; }; #endif #if SILVERLIGHT || WinRT AddElementConvention(HyperlinkButton.ContentProperty, "DataContext", "Click"); AddElementConvention(PasswordBox.PasswordProperty, "Password", "PasswordChanged"); #else AddElementConvention(DocumentViewer.DocumentProperty, "DataContext", "Loaded"); AddElementConvention(null, "Password", "PasswordChanged"); AddElementConvention(Hyperlink.DataContextProperty, "DataContext", "Click"); AddElementConvention(RichTextBox.DataContextProperty, "DataContext", "TextChanged"); AddElementConvention(Menu.ItemsSourceProperty,"DataContext", "Click"); AddElementConvention(MenuItem.ItemsSourceProperty, "DataContext", "Click"); AddElementConvention