evaluating action guards in caliburn micro

Topics: Actions & Coroutines
Aug 31, 2013 at 3:57 PM
Do I have to notify property changed in every function/property in view model for updating tool-bar's button status according to caliburn micro? That doesn't sound good.

Can I have my reflective command back ? or I missed something?
public void New()
 {
    // do something
    NotifyOfPropertyChange(() => CanFirst);
    NotifyOfPropertyChange(() => CanLast);
    NotifyOfPropertyChange(() => CanPrevious);
    NotifyOfPropertyChange(() => CanNext);
    NotifyOfPropertyChange(() => CanNew);
    NotifyOfPropertyChange(() => CanUpdate);
    NotifyOfPropertyChange(() => CanDelete);
    NotifyOfPropertyChange(() => CanSave);
    NotifyOfPropertyChange(() => CanActive);
    .
    .
    .
 }
Aug 31, 2013 at 10:04 PM
Edited Aug 31, 2013 at 10:05 PM
how about a single void method containing those instead of recreating the wheel in each method you need the notifications to fire?

like UpdateToolBar()?

private void UpdateToolBar(){
//send out notifications... etc..
NotifyOfPropertyChange(() => CanFirst);
NotifyOfPropertyChange(() => CanLast);
NotifyOfPropertyChange(() => CanPrevious);
NotifyOfPropertyChange(() => CanNext);
NotifyOfPropertyChange(() => CanNew);
NotifyOfPropertyChange(() => CanUpdate);
NotifyOfPropertyChange(() => CanDelete);
NotifyOfPropertyChange(() => CanSave);
NotifyOfPropertyChange(() => CanActive);
}
Sep 1, 2013 at 6:17 AM
mvermef wrote:
how about a single void method containing those instead of recreating the wheel in each method you need the notifications to fire?

like UpdateToolBar()?

private void UpdateToolBar(){
//send out notifications... etc..
NotifyOfPropertyChange(() => CanFirst);
NotifyOfPropertyChange(() => CanLast);
NotifyOfPropertyChange(() => CanPrevious);
NotifyOfPropertyChange(() => CanNext);
NotifyOfPropertyChange(() => CanNew);
NotifyOfPropertyChange(() => CanUpdate);
NotifyOfPropertyChange(() => CanDelete);
NotifyOfPropertyChange(() => CanSave);
NotifyOfPropertyChange(() => CanActive);
}
Unfortunately they are not in same view model and every action have a dozen of different notify property changed. they can't grouped in a function
We have three hundred view model, we can't manually update them!
Sep 1, 2013 at 8:12 AM
using System;
using System.Linq;
using System.Reflection;
using Caliburn.Micro;
using Action = System.Action;

namespace Framework.Wpf
{

    public class ActionManager
    {
        public event EventHandler ReEvaluateCanExecuteChanged;

        [ThreadStatic]
        private static ActionManager _actionManager;

        public static ActionManager Current
        {
            get
            {
                if (_actionManager == null)
                    _actionManager = new ActionManager();

                return _actionManager;
            }
        }

        protected virtual void OnReEvaluateCanExecuteChanged()
        {
            if (ReEvaluateCanExecuteChanged != null)
                ReEvaluateCanExecuteChanged(this, new EventArgs());
        }



        public void SuggestPropertyChanged()
        {
            OnReEvaluateCanExecuteChanged();
        }
    }

    public static class CaliburnMicroConfig
    {
        /// <summary>
        ///     Prepares the context.
        /// </summary>
        /// <param name="context">The context.</param>
        private static void PrepareContext(ActionExecutionContext context)
        {
            ActionMessage.SetMethodBinding(context);
            if (context.Target != null && context.Method != null)
            {

                MethodInfo guard = TryFindGuardMethod(context);
                if (guard != null)
                {
                    EventHandler handler = null;

                    handler = (s, e) => ((Action)(() =>
                    {
                        var message = context.Message;
                        if (message == null)
                            ActionManager.Current.ReEvaluateCanExecuteChanged -= handler;
                        else
                            message.UpdateAvailability();
                    })).OnUIThread();

                    ActionManager.Current.ReEvaluateCanExecuteChanged += handler;

                    context.Disposing += (s, e) => ActionManager.Current.ReEvaluateCanExecuteChanged -= handler;
                    context.Message.Detaching += (s, e) => ActionManager.Current.ReEvaluateCanExecuteChanged -= handler;


                    context.CanExecute = () =>
                    {
                        return (bool)guard.Invoke(context.Target, MessageBinder.DetermineParameters(context, guard.GetParameters()));
                    };
                }
            }
        }


        private static MethodInfo TryFindGuardMethod(ActionExecutionContext context)
        {
            string name = string.Format("Can{0}", context.Method.Name);
            MethodInfo method = context.Target.GetType().GetMethod(name);

            if (method == null)
                return null;

            if (method.ContainsGenericParameters)
                return null;

            if (typeof(bool) != method.ReturnType)
                return null;

            ParameterInfo[] methodParameters = method.GetParameters();
            ParameterInfo[] contextMethodParameters = context.Method.GetParameters();

            if (methodParameters.Length == 0)
                return method;

            if (methodParameters.Length != contextMethodParameters.Length)
                return null;

            return methodParameters.Zip(contextMethodParameters, (x, y) => x.ParameterType == y.ParameterType).Any(x => !x) ? null : method;
        }

        public static void EnableAutoActionGuardMethodReEvaluate()
        {
            ActionMessage.PrepareContext = PrepareContext;

            Action<ActionExecutionContext> old = ActionMessage.InvokeAction;

            ActionMessage.InvokeAction = context =>
            {
                old(context);
                ActionManager.Current.SuggestPropertyChanged();
            };
        }
    }
}
Sep 1, 2013 at 9:09 AM
Edited Sep 1, 2013 at 9:09 AM
that will work. Another thought was IEventAggregator...