| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 | #if XFORMSnamespace Caliburn.Micro.Core.Xamarin.Forms#elsenamespace Caliburn.Micro#endif{    using System;    using System.Linq;    using System.Text.RegularExpressions;    using System.Windows;    using System.Collections.Generic;    using Caliburn.Micro.Core;#if WinRT    using Windows.UI.Xaml;#endif#if XFORMS    using UIElement = global::Xamarin.Forms.Element;#endif    /// <summary>    ///   A strategy for determining which view model to use for a given view.    /// </summary>    public static class ViewModelLocator    {#if ANDROID        const string DefaultViewSuffix = "Activity";#elif IOS        const string DefaultViewSuffix = "ViewController";#else        const string DefaultViewSuffix = "View";#endif        static readonly ILog Log = LogManager.GetLog(typeof(ViewModelLocator));        //These fields are used for configuring the default type mappings. They can be changed using ConfigureTypeMappings().        static string defaultSubNsViews;        static string defaultSubNsViewModels;        static bool useNameSuffixesInMappings;        static string nameFormat;        static string viewModelSuffix;        static readonly List<string> ViewSuffixList = new List<string>();        static bool includeViewSuffixInVmNames;        ///<summary>        /// Used to transform names.        ///</summary>        public static readonly NameTransformer NameTransformer = new NameTransformer();        /// <summary>        /// The name of the capture group used as a marker for rules that return interface types        /// </summary>        public static string InterfaceCaptureGroupName = "isinterface";        static ViewModelLocator() {            var configuration = new TypeMappingConfiguration();#if ANDROID            configuration.DefaultSubNamespaceForViews = "Activities";            configuration.ViewSuffixList.Add("Activity");            configuration.IncludeViewSuffixInViewModelNames = false;#elif IOS            configuration.DefaultSubNamespaceForViews = "ViewControllers";            configuration.ViewSuffixList.Add("ViewController");            configuration.IncludeViewSuffixInViewModelNames = false;#endif            ConfigureTypeMappings(configuration);        }        /// <summary>        /// Specifies how type mappings are created, including default type mappings. Calling this method will        /// clear all existing name transformation rules and create new default type mappings according to the        /// configuration.        /// </summary>        /// <param name="config">An instance of TypeMappingConfiguration that provides the settings for configuration</param>        public static void ConfigureTypeMappings(TypeMappingConfiguration config)        {            if (String.IsNullOrEmpty(config.DefaultSubNamespaceForViews))            {                throw new ArgumentException("DefaultSubNamespaceForViews field cannot be blank.");            }            if (String.IsNullOrEmpty(config.DefaultSubNamespaceForViewModels))            {                throw new ArgumentException("DefaultSubNamespaceForViewModels field cannot be blank.");            }            if (String.IsNullOrEmpty(config.NameFormat))            {                throw new ArgumentException("NameFormat field cannot be blank.");            }            NameTransformer.Clear();            ViewSuffixList.Clear();            defaultSubNsViews = config.DefaultSubNamespaceForViews;            defaultSubNsViewModels = config.DefaultSubNamespaceForViewModels;            nameFormat = config.NameFormat;            useNameSuffixesInMappings = config.UseNameSuffixesInMappings;            viewModelSuffix = config.ViewModelSuffix;            ViewSuffixList.AddRange(config.ViewSuffixList);            includeViewSuffixInVmNames = config.IncludeViewSuffixInViewModelNames;            SetAllDefaults();        }        private static void SetAllDefaults()        {            if (useNameSuffixesInMappings)            {                //Add support for all view suffixes                ViewSuffixList.Apply(AddDefaultTypeMapping);            }            else            {                AddSubNamespaceMapping(defaultSubNsViews, defaultSubNsViewModels);            }        }        /// <summary>        /// Adds a default type mapping using the standard namespace mapping convention        /// </summary>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddDefaultTypeMapping(string viewSuffix = DefaultViewSuffix)        {            if (!useNameSuffixesInMappings)            {                return;            }            //Check for <Namespace>.<BaseName><ViewSuffix> construct            AddNamespaceMapping(String.Empty, String.Empty, viewSuffix);            //Check for <Namespace>.Views.<NameSpace>.<BaseName><ViewSuffix> construct            AddSubNamespaceMapping(defaultSubNsViews, defaultSubNsViewModels, viewSuffix);        }        /// <summary>        /// Adds a standard type mapping based on namespace RegEx replace and filter patterns        /// </summary>        /// <param name="nsSourceReplaceRegEx">RegEx replace pattern for source namespace</param>        /// <param name="nsSourceFilterRegEx">RegEx filter pattern for source namespace</param>        /// <param name="nsTargetsRegEx">Array of RegEx replace values for target namespaces</param>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddTypeMapping(string nsSourceReplaceRegEx, string nsSourceFilterRegEx, string[] nsTargetsRegEx, string viewSuffix = DefaultViewSuffix)        {            var replist = new List<string>();            Action<string> func;            const string basegrp = "${basename}";            var interfacegrp = "${" + InterfaceCaptureGroupName + "}";            if (useNameSuffixesInMappings)            {                if (viewModelSuffix.Contains(viewSuffix) || !includeViewSuffixInVmNames)                {                    var nameregex = String.Format(nameFormat, basegrp, viewModelSuffix);                    func = t =>                    {                        replist.Add(t + "I" + nameregex + interfacegrp);                        replist.Add(t + "I" + basegrp + interfacegrp);                        replist.Add(t + nameregex);                        replist.Add(t + basegrp);                    };                }                else                {                    var nameregex = String.Format(nameFormat, basegrp, "${suffix}" + viewModelSuffix);                    func = t =>                    {                        replist.Add(t + "I" + nameregex + interfacegrp);                        replist.Add(t + nameregex);                    };                }            }            else            {                func = t =>                {                    replist.Add(t + "I" + basegrp + interfacegrp);                    replist.Add(t + basegrp);                };            }            nsTargetsRegEx.ToList().Apply(t => func(t));            string suffix = useNameSuffixesInMappings ? viewSuffix : String.Empty;            var srcfilterregx = String.IsNullOrEmpty(nsSourceFilterRegEx)                ? null                : String.Concat(nsSourceFilterRegEx, String.Format(nameFormat, RegExHelper.NameRegEx, suffix), "$");            var rxbase = RegExHelper.GetNameCaptureGroup("basename");            var rxsuffix = RegExHelper.GetCaptureGroup("suffix", suffix);            //Add a dummy capture group -- place after the "$" so it can never capture anything            var rxinterface = RegExHelper.GetCaptureGroup(InterfaceCaptureGroupName, String.Empty);            NameTransformer.AddRule(                String.Concat(nsSourceReplaceRegEx, String.Format(nameFormat, rxbase, rxsuffix), "$", rxinterface),                replist.ToArray(),                srcfilterregx            );        }        /// <summary>        /// Adds a standard type mapping based on namespace RegEx replace and filter patterns        /// </summary>        /// <param name="nsSourceReplaceRegEx">RegEx replace pattern for source namespace</param>        /// <param name="nsSourceFilterRegEx">RegEx filter pattern for source namespace</param>        /// <param name="nsTargetRegEx">RegEx replace value for target namespace</param>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddTypeMapping(string nsSourceReplaceRegEx, string nsSourceFilterRegEx, string nsTargetRegEx, string viewSuffix = DefaultViewSuffix)        {            AddTypeMapping(nsSourceReplaceRegEx, nsSourceFilterRegEx, new[] { nsTargetRegEx }, viewSuffix);        }        /// <summary>        /// Adds a standard type mapping based on simple namespace mapping        /// </summary>        /// <param name="nsSource">Namespace of source type</param>        /// <param name="nsTargets">Namespaces of target type as an array</param>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddNamespaceMapping(string nsSource, string[] nsTargets, string viewSuffix = DefaultViewSuffix)        {            //need to terminate with "." in order to concatenate with type name later            var nsencoded = RegExHelper.NamespaceToRegEx(nsSource + ".");            //Start pattern search from beginning of string ("^")            //unless original string was blank (i.e. special case to indicate "append target to source")            if (!String.IsNullOrEmpty(nsSource))            {                nsencoded = "^" + nsencoded;            }            //Capture namespace as "origns" in case we need to use it in the output in the future            var nsreplace = RegExHelper.GetCaptureGroup("origns", nsencoded);            var nsTargetsRegEx = nsTargets.Select(t => t + ".").ToArray();            AddTypeMapping(nsreplace, null, nsTargetsRegEx, viewSuffix);        }        /// <summary>        /// Adds a standard type mapping based on simple namespace mapping        /// </summary>        /// <param name="nsSource">Namespace of source type</param>        /// <param name="nsTarget">Namespace of target type</param>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddNamespaceMapping(string nsSource, string nsTarget, string viewSuffix = DefaultViewSuffix)        {            AddNamespaceMapping(nsSource, new[] { nsTarget }, viewSuffix);        }        /// <summary>        /// Adds a standard type mapping by substituting one subnamespace for another        /// </summary>        /// <param name="nsSource">Subnamespace of source type</param>        /// <param name="nsTargets">Subnamespaces of target type as an array</param>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddSubNamespaceMapping(string nsSource, string[] nsTargets, string viewSuffix = DefaultViewSuffix)        {            //need to terminate with "." in order to concatenate with type name later            var nsencoded = RegExHelper.NamespaceToRegEx(nsSource + ".");            string rxbeforetgt, rxaftersrc, rxaftertgt;            string rxbeforesrc = rxbeforetgt = rxaftersrc = rxaftertgt = String.Empty;            if (!String.IsNullOrEmpty(nsSource))            {                if (!nsSource.StartsWith("*"))                {                    rxbeforesrc = RegExHelper.GetNamespaceCaptureGroup("nsbefore");                    rxbeforetgt = @"${nsbefore}";                }                if (!nsSource.EndsWith("*"))                {                    rxaftersrc = RegExHelper.GetNamespaceCaptureGroup("nsafter");                    rxaftertgt = "${nsafter}";                }            }            var rxmid = RegExHelper.GetCaptureGroup("subns", nsencoded);            var nsreplace = String.Concat(rxbeforesrc, rxmid, rxaftersrc);            var nsTargetsRegEx = nsTargets.Select(t => String.Concat(rxbeforetgt, t, ".", rxaftertgt)).ToArray();            AddTypeMapping(nsreplace, null, nsTargetsRegEx, viewSuffix);        }        /// <summary>        /// Adds a standard type mapping by substituting one subnamespace for another        /// </summary>        /// <param name="nsSource">Subnamespace of source type</param>        /// <param name="nsTarget">Subnamespace of target type</param>        /// <param name="viewSuffix">Suffix for type name. Should  be "View" or synonym of "View". (Optional)</param>        public static void AddSubNamespaceMapping(string nsSource, string nsTarget, string viewSuffix = DefaultViewSuffix)        {            AddSubNamespaceMapping(nsSource, new[] { nsTarget }, viewSuffix);        }        /// <summary>        ///   Makes a type name into an interface name.        /// </summary>        /// <param name = "typeName">The part.</param>        /// <returns></returns>        public static string MakeInterface(string typeName)        {            var suffix = string.Empty;            if (typeName.Contains("[["))            {                //generic type                var genericParStart = typeName.IndexOf("[[");                suffix = typeName.Substring(genericParStart);                typeName = typeName.Remove(genericParStart);            }            var index = typeName.LastIndexOf(".");            return typeName.Insert(index + 1, "I") + suffix;        }        /// <summary>        /// Transforms a View type name into all of its possible ViewModel type names. Accepts a flag        /// to include or exclude interface types.        /// </summary>        /// <returns>Enumeration of transformed names</returns>        /// <remarks>Arguments:        /// typeName = The name of the View type being resolved to its companion ViewModel.        /// includeInterfaces = Flag to indicate if interface types are included        /// </remarks>        public static Func<string, bool, IEnumerable<string>> TransformName = (typeName, includeInterfaces) =>        {            Func<string, string> getReplaceString;            if (includeInterfaces)            {                getReplaceString = r => r;            }            else            {                var interfacegrpregex = @"\${" + InterfaceCaptureGroupName + @"}$";                getReplaceString = r => Regex.IsMatch(r, interfacegrpregex) ? String.Empty : r;            }            return NameTransformer.Transform(typeName, getReplaceString).Where(n => n != String.Empty);        };        /// <summary>        ///   Determines the view model type based on the specified view type.        /// </summary>        /// <returns>The view model type.</returns>        /// <remarks>        ///   Pass the view type and receive a view model type. Pass true for the second parameter to search for interfaces.        /// </remarks>        public static Func<Type, bool, Type> LocateTypeForViewType = (viewType, searchForInterface) =>        {            var typeName = viewType.FullName;            var viewModelTypeList = TransformName(typeName, searchForInterface).ToList();            var viewModelType = AssemblySource.FindTypeByNames(viewModelTypeList);            if (viewModelType == null)            {                Log.Warn("View Model not found. Searched: {0}.", string.Join(", ", viewModelTypeList.ToArray()));            }            return viewModelType;        };        /// <summary>        ///   Locates the view model for the specified view type.        /// </summary>        /// <returns>The view model.</returns>        /// <remarks>        ///   Pass the view type as a parameter and receive a view model instance.        /// </remarks>        public static Func<Type, object> LocateForViewType = viewType =>        {            var viewModelType = LocateTypeForViewType(viewType, false);            if (viewModelType != null)            {                var viewModel = IoC.GetInstance(viewModelType, null);                if (viewModel != null)                {                    return viewModel;                }            }            viewModelType = LocateTypeForViewType(viewType, true);            return viewModelType != null                       ? IoC.GetInstance(viewModelType, null)                       : null;        };        /// <summary>        ///   Locates the view model for the specified view instance.        /// </summary>        /// <returns>The view model.</returns>        /// <remarks>        ///   Pass the view instance as a parameters and receive a view model instance.        /// </remarks>        public static Func<object, object> LocateForView = view =>        {            if (view == null)            {                return null;            }#if ANDROID || IOS             return LocateForViewType(view.GetType());#elif XFORMS            var frameworkElement = view as UIElement;            if (frameworkElement != null && frameworkElement.BindingContext != null)            {                return frameworkElement.BindingContext;            }            return LocateForViewType(view.GetType());#else            var frameworkElement = view as FrameworkElement;            if (frameworkElement != null && frameworkElement.DataContext != null)            {                return frameworkElement.DataContext;            }            return LocateForViewType(view.GetType());#endif        };    }}
 |