| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 | #if XFORMSnamespace Caliburn.Micro.Core.Xamarin.Forms#elsenamespace Caliburn.Micro#endif{    using System;    using System.Collections.Generic;    using System.Linq;#if WinRT81    using System.Reflection;    using Windows.UI.Xaml;    using Microsoft.Xaml.Interactivity;    using TriggerBase = Microsoft.Xaml.Interactivity.IBehavior;    using EventTrigger = Microsoft.Xaml.Interactions.Core.EventTriggerBehavior;    using TriggerAction = Microsoft.Xaml.Interactivity.IAction;    using System.Text;    using System.Text.RegularExpressions;    using Windows.UI.Xaml.Data;#elif XFORMS    using System.Reflection;    using System.Text.RegularExpressions;    using global::Xamarin.Forms;    using DependencyObject = global::Xamarin.Forms.BindableObject;    using FrameworkElement = global::Xamarin.Forms.VisualElement;#else    using System.Reflection;    using System.Text.RegularExpressions;    using System.Windows;    using System.Windows.Data;    using EventTrigger = System.Windows.Interactivity.EventTrigger;    using TriggerBase = System.Windows.Interactivity.TriggerBase;    using TriggerAction = System.Windows.Interactivity.TriggerAction;    using System.Text;    using Caliburn.Micro.Core;#endif    /// <summary>    /// Parses text into a fully functional set of <see cref="TriggerBase"/> instances with <see cref="ActionMessage"/>.    /// </summary>    public static class Parser    {        static readonly Regex LongFormatRegularExpression = new Regex(@"^[\s]*\[[^\]]*\][\s]*=[\s]*\[[^\]]*\][\s]*$");        static readonly ILog Log = LogManager.GetLog(typeof(Parser));        /// <summary>        /// Parses the specified message text.        /// </summary>        /// <param name="target">The target.</param>        /// <param name="text">The message text.</param>        /// <returns>The triggers parsed from the text.</returns>        public static IEnumerable<TriggerBase> Parse(DependencyObject target, string text)        {            if (string.IsNullOrEmpty(text))            {                return new TriggerBase[0];            }            var triggers = new List<TriggerBase>();            var messageTexts = StringSplitter.Split(text, ';');            foreach (var messageText in messageTexts)            {                var triggerPlusMessage = LongFormatRegularExpression.IsMatch(messageText)                                             ? StringSplitter.Split(messageText, '=')                                             : new[] { null, messageText };                var messageDetail = triggerPlusMessage.Last()                    .Replace("[", string.Empty)                    .Replace("]", string.Empty)                    .Trim();                var trigger = CreateTrigger(target, triggerPlusMessage.Length == 1 ? null : triggerPlusMessage[0]);                var message = CreateMessage(target, messageDetail);#if WinRT81 || XFORMS                AddActionToTrigger(target, message, trigger);#else                trigger.Actions.Add(message);#endif                triggers.Add(trigger);            }            return triggers;        }#if XFORMS        private static void AddActionToTrigger(DependencyObject target, TriggerAction message, TriggerBase trigger) {            if (trigger is EventTrigger) {                var eventTrigger = (EventTrigger) trigger;                eventTrigger.Actions.Add(message);            }            trigger.EnterActions.Add(message);            // TriggerAction doesn't have an associated object property so we have            // to create it ourselves, could be potential issues here with leaking the associated             // object and not correctly detaching, this may depend if the trigger implements it's            // AssociatedObject as a DependencyProperty.            var actionMessage = message as ActionMessage;            var targetElement = target as FrameworkElement;            if (actionMessage != null && targetElement != null)            {                actionMessage.AssociatedObject = targetElement;            }        }#endif#if WinRT81        private static void AddActionToTrigger(DependencyObject target, TriggerAction message, TriggerBase trigger)        {            // This is stupid, but there a number of limitiations in the 8.1 Behaviours SDK            // The first is that there is no base class for a Trigger, just IBehaviour. Which            // means there's no strongly typed way to add an action to a trigger. Every trigger            // in the SDK implements the same pattern but no interface, we're going to have to            // use reflection to set it.            // More stupidity, ActionCollection doesn't care about IAction, but DependencyObject            // and there's no actual implementation of             var messageDependencyObject = message as DependencyObject;            if (messageDependencyObject == null)            {                Log.Warn("{0} doesn't inherit DependencyObject and can't be added to ActionCollection", trigger.GetType().FullName);                return;            }            // 95% of the time the trigger will be an EventTrigger, let's optimise for that case            if (trigger is EventTrigger)            {                var eventTrigger = (EventTrigger) trigger;                eventTrigger.Actions.Add(messageDependencyObject);            }            else            {                var actionsProperty = trigger.GetType().GetRuntimeProperty("Actions");                if (actionsProperty == null)                {                    Log.Warn("Could not find Actions collection on trigger {0}.", trigger.GetType().FullName);                    return;                }                var actionCollection = actionsProperty.GetValue(trigger) as ActionCollection;                if (actionCollection == null)                {                    Log.Warn("{0}.Actions is either not an ActionCollection or is null.", trigger.GetType().FullName);                    return;                }                actionCollection.Add(messageDependencyObject);            }            // The second is the IAction doesn't have an associated object property so we have            // to create it ourselves, could be potential issues here with leaking the associated             // object and not correctly detaching, this may depend if the trigger implements it's            // AssociatedObject as a DependencyProperty.            // Turns out trying to a binding won't work because the trigger doesn't notify the             // binding of changes, so we just need to set it, yay.            var actionMessage = message as ActionMessage;            var targetElement = target as FrameworkElement;            if (actionMessage != null && targetElement != null)            {                //var binding = new Binding { Source = trigger, Path = new PropertyPath("AssociatedObject") };                //BindingOperations.SetBinding(actionMessage, ActionMessage.AssociatedObjectProperty, binding);                actionMessage.AssociatedObject = targetElement;            }                    }#endif        /// <summary>        /// The function used to generate a trigger.        /// </summary>        /// <remarks>The parameters passed to the method are the the target of the trigger and string representing the trigger.</remarks>        public static Func<DependencyObject, string, TriggerBase> CreateTrigger = (target, triggerText) =>        {            if (triggerText == null)            {                var defaults = ConventionManager.GetElementConvention(target.GetType());                return defaults.CreateTrigger();            }            var triggerDetail = triggerText                .Replace("[", string.Empty)                .Replace("]", string.Empty)                .Replace("Event", string.Empty)                .Trim();#if XFORMS            return new EventTrigger { Event = triggerDetail };#else            return new EventTrigger { EventName = triggerDetail };#endif        };        /// <summary>        /// Creates an instance of <see cref="ActionMessage"/> by parsing out the textual dsl.        /// </summary>        /// <param name="target">The target of the message.</param>        /// <param name="messageText">The textual message dsl.</param>        /// <returns>The created message.</returns>        public static TriggerAction CreateMessage(DependencyObject target, string messageText)        {            var openingParenthesisIndex = messageText.IndexOf('(');            if (openingParenthesisIndex < 0)            {                openingParenthesisIndex = messageText.Length;            }            var closingParenthesisIndex = messageText.LastIndexOf(')');            if (closingParenthesisIndex < 0)            {                closingParenthesisIndex = messageText.Length;            }            var core = messageText.Substring(0, openingParenthesisIndex).Trim();            var message = InterpretMessageText(target, core);            var withParameters = message as IHaveParameters;            if (withParameters != null)            {                if (closingParenthesisIndex - openingParenthesisIndex > 1)                {                    var paramString = messageText.Substring(openingParenthesisIndex + 1, closingParenthesisIndex - openingParenthesisIndex - 1);                    var parameters = StringSplitter.SplitParameters(paramString);                    foreach (var parameter in parameters)                        withParameters.Parameters.Add(CreateParameter(target, parameter.Trim()));                }            }            return message;        }        /// <summary>        /// Function used to parse a string identified as a message.        /// </summary>        public static Func<DependencyObject, string, TriggerAction> InterpretMessageText = (target, text) =>        {            return new ActionMessage { MethodName = Regex.Replace(text, "^Action", string.Empty).Trim() };        };        /// <summary>        /// Function used to parse a string identified as a message parameter.        /// </summary>        public static Func<DependencyObject, string, Parameter> CreateParameter = (target, parameterText) =>        {            var actualParameter = new Parameter();            if (parameterText.StartsWith("'") && parameterText.EndsWith("'"))            {                actualParameter.Value = parameterText.Substring(1, parameterText.Length - 2);            }            else if (MessageBinder.SpecialValues.ContainsKey(parameterText.ToLower()) || char.IsNumber(parameterText[0]))            {                actualParameter.Value = parameterText;            }            else if (target is FrameworkElement)            {                var fe = (FrameworkElement)target;                var nameAndBindingMode = parameterText.Split(':').Select(x => x.Trim()).ToArray();                var index = nameAndBindingMode[0].IndexOf('.');                View.ExecuteOnLoad(fe, delegate                {                    BindParameter(                        fe,                        actualParameter,                        index == -1 ? nameAndBindingMode[0] : nameAndBindingMode[0].Substring(0, index),                        index == -1 ? null : nameAndBindingMode[0].Substring(index + 1),                        nameAndBindingMode.Length == 2 ? (BindingMode)Enum.Parse(typeof(BindingMode), nameAndBindingMode[1], true) : BindingMode.OneWay                        );                });            }            return actualParameter;        };        /// <summary>        /// Creates a binding on a <see cref="Parameter"/>.        /// </summary>        /// <param name="target">The target to which the message is applied.</param>        /// <param name="parameter">The parameter object.</param>        /// <param name="elementName">The name of the element to bind to.</param>        /// <param name="path">The path of the element to bind to.</param>        /// <param name="bindingMode">The binding mode to use.</param>        public static void BindParameter(FrameworkElement target, Parameter parameter, string elementName, string path, BindingMode bindingMode)        {#if XFORMS            var element = elementName == "$this" ? target : null;            if (element == null)            {                return;            }            if (string.IsNullOrEmpty(path))            {                path = ConventionManager.GetElementConvention(element.GetType()).ParameterProperty;            }            var binding = new Binding(path) {                Source = element,                Mode = bindingMode            };            parameter.SetBinding(Parameter.ValueProperty, binding);#else            var element = elementName == "$this"                ? target                : BindingScope.GetNamedElements(target).FindName(elementName);            if (element == null)            {                return;            }            if (string.IsNullOrEmpty(path))            {                path = ConventionManager.GetElementConvention(element.GetType()).ParameterProperty;            }#if WinRT            var binding = new Binding            {                Path = new PropertyPath(path),                Source = element,                Mode = bindingMode            };#else            var binding = new Binding(path) {                Source = element,                Mode = bindingMode            };#endif#if (SILVERLIGHT && !SL5)            var expression = (BindingExpression)BindingOperations.SetBinding(parameter, Parameter.ValueProperty, binding);            var field = element.GetType().GetField(path + "Property", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);            if (field == null) {                return;            }            ConventionManager.ApplySilverlightTriggers(element, (DependencyProperty)field.GetValue(null), x => expression, null, null);#else#if !WinRT            binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;#endif            BindingOperations.SetBinding(parameter, Parameter.ValueProperty, binding);#endif#endif        }    }}
 |