About IActiveAware interface

Oct 9, 2009 at 3:09 PM

Hi, Admin,

I am sorry to say, how bad the IActiveAware interface is designed. The reason is as follows:

I find IActiveAware occurs in 2 places, one is CompositeCommand, the other is RegionActiveAwareBehavior. Let's talk about it one by one.

1) In CompositeCommand, IsActive has the same function as ICommand's CanExecute method. So it's duplicated, I have successed to use CanExecute method to silulate the same function as IsActive.

But I found there is no mechnism to judge whether the DelegateCommand in the CompositeCommand will be executed. So I must use UnregisterCommand method to remove this command from the CompositeCommand, if I still want to use the CompositeCommand with this command removed before, I should use RegisterCommand method to add this command again. It's not convenient to deal with a CompositeCommand that always updated.

Why don't we use IActiveAware's IsActive property to judge that?

2) In RegionActiveAwareBehavior, each item of the NotifyCollectionChangedEventArgs is an IActiveAware object, it is true for NewItems' IsActive, but false for OldItems' IsActive. But I found no use in the Prism of that. I look through the forum, and find 2 posts to describe its use, which need us to implement it ourselves:

http://compositewpf.codeplex.com/Thread/View.aspx?ThreadId=66791

http://compositewpf.codeplex.com/Thread/View.aspx?ThreadId=54953

Both of the issues can be implemented in AttachedBehavior. So no need to use IActiveAware insteadly.

FYI

 

Regards

Jianqiang

Oct 9, 2009 at 4:41 PM

Hi, Admin,

I check the CompositeCommand class again, look at these two methods:

        public virtual void Execute(object parameter)
        {
            Queue commands;
            lock (this.registeredCommands)
            {
                commands = new Queue(this.registeredCommands.Where(this.ShouldExecute).ToList());
            }

            while (commands.Count > 0)
            {
                ICommand command = commands.Dequeue();
                command.Execute(parameter);
            }
        }

        protected virtual bool ShouldExecute(ICommand command)
        {
            var activeAwareCommand = command as IActiveAware;

            if (this.monitorCommandActivity && activeAwareCommand != null)
            {
                return activeAwareCommand.IsActive;
            }

            return true;
        }

I am sure at the beginning, you want to only execute the commands whose IsActive is true. But unfortunately, in the CanExecute method, you doubt, or maybe it is a mistake.

In my option, CanExecute method in the CompositeCommand should be like this:

        public virtual bool CanExecute(object parameter)
        {
            ICommand[] commandList;
            lock (this.registeredCommands)
            {
                commandList = this.registeredCommands.ToArray();
            }

            foreach (ICommand command in commandList)
            {
                if (!command.CanExecute(parameter))
                    return false;
            }

            return true;
        }

Do you undersatnd my code?

I think, in the CompositeCommand, CanExecute is responsible for checking whether all the command in the CompositeCommand can be executed, if one of them cannot, the CompositeCommand can not be executed either.

IsActive is responsible for checking which commands can be executed. All these commands' CanExecute method return true, but I only want get part of them to be executed. I know you can use UnregisterCommand to judge which command to execute. But next time when I want to execute all the command, including the unregister one, I must use RegisterCommand method to add it again. But now if we have a bool property to record which one is executed this time and not executed next time, it is convienet, I think IsActive is the best one to do it.

 

FYI

Regards

Jianqiang

 

 

 

 

Oct 9, 2009 at 6:17 PM

Hi, Admin,

I write a demo to demonsrate my viewpoint.

Please visit this url to download the demo: http://files.cnblogs.com/Jax/CompositeCommandDemo.zip

In this demo, please notice "Common" project, where I rewrite CompositeCommand class's CanExecute method as follows:

        public virtual bool CanExecute(object parameter)
        {
            ICommand[] commandList;
            lock (this.registeredCommands)
            {
                commandList = this.registeredCommands.ToArray();
            }

            foreach (ICommand command in commandList)
            {
                if (!command.CanExecute(parameter))
                    return false;
            }

            return true;
        }

The demo is to sinulate the visual studio "Save All" function. When you modify the text, a star will show on the left-top corner of the TabItem Header, and when you click "Save All" button, all the stars will not appear agian.

There are 3 "files", if two of them are modified, there will be 2 stars. When you click "Save All" button, the CompositeCommand will only execute 2 command with stars, not execute the "file" without star. In your old CompositeCommand, you could only use UnregisterCommand method to remove the command not execute.

In my demo, you can click SaveAll again and again, if no change, no command will be executed. But in your old code, you must invoke RegisterCommand and UnregisterCommand again and again to execute the same commands.

 

FYI

Regards

Jianqiang

Oct 14, 2009 at 8:25 AM

Hi, guys,

Anybody else can help me to analyze and resolve it ?

Thank you.

Oct 15, 2009 at 10:37 PM

Hi Jianqiang,

The IActiveAware interface is used in the CompositeCommand simply to determine if each of the child commands that implement IActiveAware should be executed (this is the ShouldExecuteMethod). On the other side, the CanExecute method of the CompositeCommand goes over all its registered commands to determine if it can be executed. You can find the method below:

public virtual bool CanExecute(object parameter)
        {
            bool hasEnabledCommandsThatShouldBeExecuted = false;

            ICommand[] commandList;
            lock (this.registeredCommands)
            {
                commandList = this.registeredCommands.ToArray();
            }
            foreach (ICommand command in commandList)
            {
                if (this.ShouldExecute(command)) //this is only for IActiveAware commands, otherwise it’s always true
                {
                    if (!command.CanExecute(parameter)) //this is for all commands
                    {
                        return false;
                    }

                    hasEnabledCommandsThatShouldBeExecuted = true;
                }
            }

            return hasEnabledCommandsThatShouldBeExecuted;
        }

To determine if a child Delegate or Composite command is to be executed, that should be done in their own CanExecute method (without having to register/unregister them, which is not the idea). As the above explains, a CompositeCommand will be executed only if all its child Commands can.

You can read more about this topic here:

If you believe the current use/implementation of the Prism CompositeCommand with the IActiveAware interface is not best suited for you, you can inherit from the CompositeCommand class and override the functionality you require to be different. You will notice that this is really straight forward as most CompositeCommand methods are virtual.

A possible approach to save a Tab's content, could be adding a statement to check if the Tab's content has changed in the Delegate's command Execute method that is in charge of saving. If the content was modified, the command should save, and it shouldn't otherwise.

I hope the above helps better understand the scenario.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

Oct 16, 2009 at 2:49 AM

Hi, dschenkelman,

      Thank you for your reply.

      All you said in the feedback, including the Prism source code, I had read it before I submit this issue. But I still cannot agree with you.

      Could you please have a look at my demo? I think these two demo illustrate my viewpoint. I strongly suggest you compare which mechanism is better.

      Thank you.

Regards

Jianqiang