Menu/toolbar UI state handling

Thread ID:

Created:

Updated:

Platform:

Replies:

I was just wondering if there is a prescribed method of handling UI state of menu/toolbar items in the XP Menus architecture (enabled/checked etc)? MFC handles this sort of thing in OnIdle, and I know it is one of the things lacking in the WinForms architecture, so I was wondering if XPMenus tackles this deficiency (or will in a future release)?

MFC has two types of handlers - ON_COMMAND and ON_UPDATE_COMMAND_UI. I guess my question is why XPMenus only really has the first one of these (ON_COMMAND, which is replicated via the Click event).I could do it manually, I was just wondering why the support for it wasn't hooked into the architecture at a lower level. For example, if the MainFrameBarManager handled the Application.Idle, then fired say an UpdateCommandUI event on all BarItems that it currently has in its merged menus, the BarItem.UpdateCommandUI handler could modify the Enabled and Checked properties of the BarItem if it wanted to.
A good article on this is located at http://msdn.microsoft.com/msdnmag/issues/02/10/CommandManagement/default.aspx

Ian,
I suppose we were originally concerned about the performance hit of parsing through all the bar items looking for event subscribers. I suppose MFC had a very optimized way of dealing with this.
I will log this as a feature request for our developers to implement in a future version.
Regards,
Praveen Ramesh

Praveen,
I understand your concern - doing this every time you get an OnIdle is not something you would really want to do. It may be that the solution would be a compromise, where by the forms need to notify their BarManager if the user has done something to invalidate the state of one specific command, a range of commands, or all commands, and the update is done during the next OnIdle. In our current MFC application we do this kind of optimization so that we only update when told to, and we cache requests to update by using OnIdle.
One solution would be to make this UI state handling optional - you specify on the main BarManager whether you want no UI state handling, "optimized" state handling (as I described above), or the slower state handling where every command is polled for handlers you described above.
Thanks for considering this.
Ian

Ian,
Optional UI state handling sounds like a good idea.
However, regarding letting the user to make one or more commands "dirty" (so that thier updateUI will be called in next Idle message), I was wondering why would anyone want to set the command dirty when they could as well update the command's state right there (I would think this would be a pretty fast op.)
Regards,
Praveen Ramesh

Praveen,
One good reason for not always doing the UI state handling immediately is that our application can be driven by both automation, macros, and the user. One of the main events which would make most of our commands "dirty" is a change in selection (our app is a drawing package). However, if the app is being driven by a macro, the macro can make hundreds of selection changes in one function call. Now the call to actually set the UI state is not intensive - you are just calling Enabled or Checked - but the processing to work out what the UI state should be can be intensive - if a user does a Select All, and we have 1000 elements, then to work out the state of say our "Ungroup" command, we need to process every selected element to work out if at least one of them is a group. The worst case scenario is obviously if none of them are, as then you iterate over every object only to find there are no groups.
Sorry if this doesn't make much sense to you, but believe me, it can make a big difference to performance of a complex application like a drawing package.
Regards
Ian

Ian,
So, it seems that determining the command state is an intensive op. in your case. Then how about an approach like this:
We provide a "ui dirty flag" for each BarItem, which when true will cause the BarItem's UpdateCommand event to be fired on Idle, and set to false by default.
We will then subscribe to the App's OnIdle from within the BarItem itself whenever set to dirty.
Then the user can choose to "dirty" one or more BarItems temporarily (like in your case) or keep them "dirty" all the time.
Let me know if this will work for you.
Regards,
Praveen Ramesh

Praveen,
Thanks for trying to accomodate us - we are getting close to a great solution.
In our current application, we actually have two optimizations - the caching of requests to update the UI (which you have covered in your proposal) and another one, which I didn't mention before - sorry!!!
We use the IOleCommandTarget::QueryStatus method in our application - it allows the state of multiple commands to be updated in one hit. This optimization is just as important as the caching of requests for UI updates.
For example, quite often we disable a lot of our commands if one of the selected objects is of a certain type. We also disable other commands if the number of selected objects is > 1, etc, etc. We also disable commands based on combinations of these and other "indicators". So to improve performance, we work out once, at the start of our implementation of QueryStatus, all the indicators - if any of the selected objects are of a certain type, the number of selected objects etc, etc, and then we run through the commands and enable/disable based on the "indicators".
The only problem with the implementation you propose is that each BarItem would have to determine its state independently, in the UpdateCommand event for that BarItem, so we could not implement this type of optimization.
To achieve this second optimization, we as application developers would need some sort of hook into the OnIdle, to allow us to do some sort of processing before the UpdateCommand is called on each BarItem. The BarItem could then use this "local" array of state to update its command state.
Hope this makes sense.
Regards
Ian

Ian,
Ok, so I understand that there might be ONE single "UpdateCommands" operation, that will update all the dirty commands in one shot. And you are worried about having to redundantly do this for all BarItem's UpdateCommand events, is that right?
This is how I see this scenario handled:
1) Dirty all the commands.
2) On next Idle the first command will fire the UpdateCommand event. In which you would go ahead and update ALL the commands. In the process you will also reset the dirty flags for all those commands, there by preventing further UpdateCommand events.
Does this make sense?
Regards,
Praveen Ramesh

Praveen,
Yes, this would work, as long as the first BarItem can access the dirty flag of all the other commands.
Thanks for taking the time to consider this enhancement - it is much appreciated. If you do decide to implement it (and I really hope you do), and would like a sanity check on your design, I would be happy to oblige.
Regards
Ian