Toggling a Command contribution
Every once in a while something just doesn’t happen to be as intiutive as you would have liked it to be. Lately I was trying to contribute a simple command based toggle button to the workbench. Although it is simple to actually provide the menu contribution and to put the button in visual “toggle” mode, it was so straight forward to actually obtain the state of the button in the UI.
To make the menu contribution show up as a toggleable button you have to provide the style value “toggle” on your commands menu contribution:
<extension point="org.eclipse.ui.menus">
<menuContribution locationURI="...">
<command commandId="org.eclipse.example.command.toggle"
style="toggle" />
</menuContribution>
</extension>
Next we have too keep track of our actual toggle state. Since it is possible to have multiple menu contributions for the same command, we have to keep track of the state in a central place. Imagine a toggle button triggerable from the main menu and a views toolbar. The state of these buttons are keept in sync by storing the state directly in the command. The key to this is the org.eclipse.jface.commands.ToggleState. This implementation of org.eclipse.core.commands.State is a wrapper for a boolean. To attach such a State object to a command, it is provided during the command declaration:
<command id="org.eclipse.example.command.toggle"
name="Toggle Me">
<state class="org.eclipse.jface.commands.ToggleState"
id="org.eclipse.example.command.toggleState" />
</command>
The state element takes the ToggleState class as a state provider and attaches itself to the command. The id of the state has to be unique to identfy the state. With the state attached to our command, how can we access it? In the Handler, reacting to the invocation of the menu contribution, we have to access the current state keept in the ToggleState. The following code demonstrates just that:
ICommandService service =
(ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
Command command = service.getCommand("org.eclipse.example.command.toggle");
State state = command.getState("org.eclipse.example.command.toggleState");
state.setValue(!(Boolean) state.getValue());
We obtain our command from the ICommandService and ask it for our toggleState stored within the command. Next we just flip the boolean as to create the new state. Now we can just do whatever we want to do in our Handler using the new state. Great, isn’t it? Of course the ToggleState is only one possible implementation of State. One could also imagine multiple states, radio states, text states etc. Also the State class has a specialization (PersistableState), which can be persisted to the preferences as to keep track of the (toggle) states of your buttons.
But there is thing left to do: what happens to this other button which is also a menu contribution for our command? We need to toggle the pressed state of it, as to reflect the state of the first button. To do so, a Handler has to implement IElementUpdater providing the method updateElements(…). It gets an UIElement as a parementer, which can be used to trigger the toggle state:
public void updateElement(UIElement element, Map paramters) {
element.setChecked(isSelected);
}
To broadcast the refresh event to all menu contributions we use the ICommandService in our Handlers:
commandService.refreshElements(executionEvent.getCommand().getId(), null);
I hope you find the information here valuable and i am looking forward to your comments.




Very nice, I’ve been struggling with that capability. One question: in your example, where do you get the “isSelected” you pass to the element.setChecked()?
I was about to write a tip on this commands and state, and you wrote it first
Hi Craig
The isSelected boolean can be obtained from the ToggleState within the Command as shown in the example. The only thing different than when invoking the code directly from within the Handler is that you have to provide the command id yourself.
Greets Moritz
Ok I see now. Do you know if it’s possible to initialize the ToggleState to true?
To get/set an initial state i would simply set the initial state as described in the post. Of course this interferes with a possible lazy loading of the plugin but it would also ensure that all toggle buttons are in the same state. As far as i know, the initial state of a toggle button (aka when created via a command) is false so you could also rely on that (if you are brave enough).
I got it working by subclassing State and specifying my class as the state attribute in the command declaration. In iys constructor I call setValue(true) and it seems to work. I am concerned though that it might have caused the plugin to load as you suggest. Any ideas how I might determine if this is the case?
For another command whose state I need to be false initially, setting the state attribute to the provided ToggleState class seems to work perfectly. It’s javadoc does state its initialized to false.
I read somewhere that the state attribute could be declared as follows, with “:true” appended to the class name. I tried it but couldn’t get it to work. Have you seen this before?
Craig
Not sure why my last posting omitted my sample code, perhaps because of the angle brackets?
This is the state attribute declaration looked with the “:true” appended:
state class=”org.eclipse.jface.commands.ToggleState”
id=”org.eclipse.example.command.toggleState:true”
Hi Craig
Good to see that you have also brought that question over to the newsgroup. I have also heard of the “:true” flag on the command id but as for you, that didn’t work for me. As in the newsgroup post, i’ll also link to the relevant bug here.
Hi Moritz,
it seems that the ToggleState does NOT track the state of the “of the button in the UI”. At least it is never updated by itself. In your example you set the state yourself by toggling the state in parallel:
state.setValue(!(Boolean) state.getValue());
But this is completely unrelated to the actual “toggle” state of the command, isn’t it?
I found “IMenuStateIds.STYLE”, and according to the javadocs, you should get the widget state by naming the state “STYLE”. But this does not work as described.
Ralf
Hmm, there seems to be no good way to do this. Using the parts of your post I built a base Handler class hiding all the details so this can be easily switched for some other, cleaner solution. Works only if the initial state is unchecked though
package de.ralfebert.rcputil;
import java.util.Map;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.State;
import org.eclipse.jface.menus.IMenuStateIds;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.commands.IElementUpdater;
import org.eclipse.ui.menus.UIElement;
/**
* Use this handler for style=”toggle” command contributions. You need to
* declare a state like this to use this:
*
*
*
*
*
*
*
* The id=”STYLE” is chosen because of IMenuStateIds.STYLE – maybe this will
* work without any Handler Foo in later Eclipse versions.
*
* @author Ralf Ebert
*/
public abstract class ToggleHandler extends AbstractHandler implements IElementUpdater {
private String commandId;
public final Object execute(ExecutionEvent event) throws ExecutionException {
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
this.commandId = event.getCommand().getId();
State state = event.getCommand().getState(IMenuStateIds.STYLE);
if (state == null)
throw new ExecutionException(
“You need to declare a ToggleState with id=STYLE for your command to use ToggleHandler!”);
boolean currentState = (Boolean) state.getValue();
boolean newState = !currentState;
state.setValue(newState);
executeToggle(event, newState);
commandService.refreshElements(event.getCommand().getId(), null);
// return value is reserved for future apis
return null;
}
protected abstract void executeToggle(ExecutionEvent event, boolean newState);
public void updateElement(UIElement element, Map parameters) {
if (this.commandId != null) {
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(
ICommandService.class);
Command command = commandService.getCommand(commandId);
element.setChecked((Boolean) command.getState(IMenuStateIds.STYLE).getValue());
}
}
}
http://www.ralfebert.de/eclipse/2009_01_21_togglehandler/
Ralf Ebert code works only partially….
Suppose you have 2 editor instances, and each one has a handler for the same command, but the checked state differs in each one. Switching the active editor doesn’t update the check state.
It looks like that updateElement() and refreshElements() do nothing.
Good post. I did some digging to figure out how to synchronize the initial state of the widget with the initial value of the State.
There are a few places in the Eclipse code with comments about the state attribute and the IMenuStateIds.STYLE. From what I can tell these are old references. This is how things were done in the older IAction world, but it is no longer applicable in the new, declarative world. For more information see eclipse bug 154130 (esp. the part after hahaha in https://bugs.eclipse.org/bugs/show_bug.cgi?id=154130#c27).
The bottom line seems to be that the command state is not linked (and is not planned to be linked) to the UI widget’s state.
Comment 31 (in the same bug) has a pointer to the real solution. In short, make your Handler implement IElementUpdater and then provide an implementation of updateElement that calls #setChecked on the argument uiElement.
A pretty simple and clean solution. Unfortunately took me a few hours to find, hopefully this note will save other people some time.
??… +1
Thanks for good explanation, helped a lot.
Hello,
Can anyone tell, why it is so extremely complicated just to change state of the button and update others?
Why this simple thing can be an issue?
How to do the same thing in Eclipse E4
Parvez Ahmad Hakim
Srinagar Kashmir
http://www.abobjects.com
I tried below but it did’nt worked
Command command2 = commandService.getCommand(PasteHandler.commandId);
CopyHandler ah = (CopyHandler) command2.getHandler();
h.canExecute();