Creating Command Clients - kaisu1986/ATF GitHub Wiki
A command client implements the ICommandClient
interface, which consists of these methods:
bool CanDoCommand(object commandTag);
void DoCommand(object commandTag);
void UpdateCommand(object commandTag, CommandState commandState);
Both WinForms and WPF use the same ICommandClient
interface.
The CanDoCommand()
method simply indicates whether or not the command can be performed, usually considering the current context. It is called in response to application events. When the method returns false, the command is disabled, which is indicated in the UI by graying out menu items and tool strip controls. The method's commandTag
argument is the same tag the command was registered with.
Here is an example from the ATF Circuit Editor Sample MasteringCommands
class. The main thing to notice is that the current context is used to determine whether or not the command is possible by checking if at least one item is selected. And if there is no current context, the command can't be done either.
public bool CanDoCommand(object commandTag)
{
bool enabled = false;
var context = m_contextRegistry.GetActiveContext<CircuitEditingContext>();
if (context != null)
{
ISelectionContext selectionContext = context.As<ISelectionContext>();
if (CommandTag.CreateMaster.Equals(commandTag))
{
enabled = selectionContext.GetLastSelected<Element>() != null; // at least one module selected
}
else if (CommandTag.ExpandMaster.Equals(commandTag))
{
enabled = selectionContext.GetLastSelected<SubCircuitInstance>() != null; // at least one mastered instance selected
}
}
return enabled;
}
UpdateCommand()
's purpose is to update the command's CommandState
, which contains two properties: the command name and menu item check mark. Application events trigger calling UpdateCommand()
.
This is the menu's name, initially specified in the command's CommandInfo
when the command was registered. You can change this to reflect the status of the command. For instance, the RecentDocumentCommands
component updates this text with a document name:
public virtual void UpdateCommand(object commandTag, CommandState state)
{
if (commandTag is RecentDocumentInfo)
{
RecentDocumentInfo info = (RecentDocumentInfo)commandTag;
state.Text = info.Uri.LocalPath;
}
}
The check mark indicates whether or not to display a check on the command's menu. For example, the ATF Timeline Editor Sample uses this feature to place a check on the Edit > Interval Splitting Mode menu item when this mode is active:
public void UpdateCommand(object commandTag, CommandState commandState)
{
TimelineDocument document = m_contextRegistry.GetActiveContext<TimelineDocument>();
if (document == null)
return;
if (commandTag is Command)
{
switch ((Command)commandTag)
{
case Command.ToggleSplitMode:
commandState.Check = document.SplitManipulator != null ? document.SplitManipulator.Active : false;
break;
}
}
}
This method performs the command when the command is triggered by the user selecting the command in the menu or tool strip. Its argument is the command tag with which the command was registered. It may simply call other methods, as in this example from the StandardEditCommands
component. This method uses commandTag
to determine which command is being done, and switches accordingly:
void ICommandClient.DoCommand(object commandTag)
{
switch ((StandardCommand)commandTag)
{
case StandardCommand.EditCut:
Cut();
break;
case StandardCommand.EditDelete:
Delete();
break;
case StandardCommand.EditCopy:
Copy();
break;
case StandardCommand.EditPaste:
Paste();
break;
}
}
The RecentDocumentCommands
component's DoCommand()
opens the document associated with the menu item, removing the menu item when the document is invalid:
public virtual void DoCommand(object commandTag)
{
if (commandTag is RecentDocumentInfo) // recently used file?
{
RecentDocumentInfo info = (RecentDocumentInfo)commandTag;
IDocumentClient client;
if (m_typeToClientMap.TryGetValue(info.Type, out client))
{
IDocument document = m_documentService.OpenExistingDocument(client, info.Uri);
if (document == null)
RemoveDocument(info);
}
}
}
Note that this example from the ATF Circuit Editor Sample MasteringCommands
class uses the current context to perform the command:
public void DoCommand(object commandTag)
{
var context = m_contextRegistry.GetActiveContext<CircuitEditingContext>();
if (CommandTag.CreateMaster.Equals(commandTag))
{
SubCircuitInstance subCircuitInstance = null;
var masterContext = context.DomNode.GetRoot().Cast<CircuitEditingContext>();
...