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