CustomCommandHandler and ApplicationCommands

It seems that a custom CommandHandler is not called for ApplicationCommands.
What we did:
We bound our ToolBar buttons and main MenuItems for e.g. Cut/Copy/Paste to the corresponding ApplicationCommands. No Problems so far. But now we need a custom CommandHandler implementation.

          <go:Diagram.CommandHandler>
            <handler:CustomHandler/>
          </go:Diagram.CommandHandler>

(taken from your examples) where CusytomHandler inherits from CommandHandler and overrides the methods CanCut, Cut etc.
The CustomHandler is instantiated but the overridden methods are never called.

We cannot bind the button or menu item directly to the CommandHandler.CutCommand etc, because they are application wide controls and we would disable the default behavior for cut/copy/paste in all other framework elements in that case.

What do we have to do?

Thanx in advance

It seems to work for me. I tried modifying the Gantt sample, which already has a custom CommandHandler, and adding both ways of defining commands for buttons:

<Button Command="{x:Static go:Commands.IncreaseZoom}" CommandTarget="{Binding ElementName=myDiagram}">Zoom In</Button> <Button Command="{Binding ElementName=myDiagram, Path=CommandHandler.DecreaseZoomCommand}">Zoom Out</Button>
The declaration of the go:Diagram.CommandHandler is already in that sample. Clicking either button works as you would expect, and if you set a breakpoint in the CommandHandler-derived subclass, it stops there.

I’m wondering what is different between your situation and this sample?

Oh, I forgot to ask if this was for WPF or Silverlight. I was assuming WPF. Obviously the first button cannot be defined in Silverlight that way.

    <ToolBar Grid.Row="1" DockPanel.Dock="Top" AutomationProperties.AutomationId="Editor.ToolBar">
      <Button AutomationProperties.AutomationId="Editor.ToolBar.Cut" Command="ApplicationCommands.Cut">
        <Image Source="../Images/Cut.png"></Image>
      </Button>

      <Button AutomationProperties.AutomationId="Editor.ToolBar.Copy" Command="ApplicationCommands.Copy">
        <Image Source="../Images/Copy.png"></Image>
      </Button>

      <Button AutomationProperties.AutomationId="Editor.ToolBar.Paste" Command="ApplicationCommands.Paste">
        <Image Source="../Images/Paste.png"></Image>
      </Button>

      <Separator/>
      <ToggleButton AutomationProperties.AutomationId="Editor.ToolBar.Grid" IsChecked="{Binding GridEnabled, Source={StaticResource EditorSettings}}">
        <Image Source="../Images/Grid.png"></Image>
      </ToggleButton>
      
    </ToolBar>

That is the command linkage between our toolbar buttons and the ApplicationCommands.
These commands are handled by the Diagram, but the custom handler is not called. That is the problem we have. You bound buttons directly to the Digram’s CommandHandler (different scenario I think ;-)).

The first button in my previous post did not bind directly to the Diagram.CommandHandler.

So let’s try again.

First I add these overrides to the Gantt sample custom CommandHandler:

public override bool CanCopy() { return base.CanCopy(); } public override void Copy() { base.Copy(); }
Then I add a ToolBar to the sample:

<StackPanel> <ToolBarTray> <ToolBar> <Button Command="ApplicationCommands.Copy">Copy</Button> </ToolBar> </ToolBarTray> <Grid> . . . the rest of the Gantt sample XAML body, including the go:Diagram with the custom go:Diagram.CommandHandler . . . </Grid> </StackPanel>
I set a breakpoint in the overridden Copy() method, select a Node – now the Copy button in the ToolBar is enabled. Click that button – it stops at the breakpoint in my override method.

Could you help me by showing what I need to do to modify the Gantt sample to reproduce the problem that you have?

Hmmm, thats strange. It seems that it works fine until I put the Diagram back into our TabControl’s DataTemplate.

I wrapped the original XAML body with:

<TabControl> <TabItem Header="Diagram"> . . . </TabItem> </TabControl>
and everything continued to work just fine.

Even after adding some other tabs and switching tabs it worked.

    <TabControl ItemsSource="{Binding WorkSheetItems}" SelectedItem="{Binding CurrentWorksheet}" ContentTemplate="{StaticResource TabItemContentTemplate}" >

and the TabItemContentTemplate contains the diagram.

      <DataTemplate x:Key="TabItemContentTemplate">
        <go:Diagram  Name="Diagram" NodesSource="{Binding Elements}"
          GridVisible="{Binding GridEnabled, Source={StaticResource EditorSettings}}" 
          NodeTemplate="{StaticResource 'Node.Template'}"
          LinkTemplate="{StaticResource 'Link.Template'}"
                     
          AutomationProperties.AutomationId="{Binding Name, StringFormat=Editor.Diagram.{0}}">
          <go:Diagram.CommandHandler>
            <local:CustomHandler/>
          </go:Diagram.CommandHandler>
        </go:Diagram>
      </DataTemplate>

The problem with what you write there is that you cannot share a single local:CustomHandler amongst multiple go:Diagrams. You will need to assign diagram.CommandHandler = new CustomHandler() when each Diagram is instantiated.

It does not work at all. I made a little demo VS2012 Solution containing three different ‘tests’ were you easily cann switch between the cases by commenting/uncommenting the XAML code. I create a new instance of the command handler in my view model and not in the code beside (we cannot acces the diagram because it is defined in the data template).

  1. Scenario: TabControl with data template -> handler not called, but default handler used
  2. Scenario: TabControl with fixed TabItems -> Error: Handler cannot be shared between different diagram
  3. Scenario: StackPanel containing two diagrams-> Error: Handler cannot be shared between different diagram (remember, new handler instance is created in each view model)

How do I upload the demo?

Send email to GoXam at our domain.

If you cannot make sure each Diagram has its own CustomHandler, you can define:

public class CustomDiagram : Diagram { public CustomDiagram() { this.CommandHandler = new CustomHandler(); } }
and use this CustomDiagram in your XAML instead of a regular Diagram with a setting of go:Diagram.CommandHandler

Thanx,
that works, but it seems to be a bug for me that the dependency property is ignored if the diagram is defined inside a data template.

    <ResourceDictionary>
      <goXamAppCommandTest:MyHandler x:Key="Handler"/>
      <DataTemplate x:Key="TabItemContentTemplate">
        <go:Diagram NodesSource="{Binding Elements}" CommandHandler="{StaticResource Handler}">
        </go:Diagram>
      </DataTemplate>
      <goXamAppCommandTest:MyViewModel x:Key="VM"/>
    </ResourceDictionary>

Each CommandHandler has diagram-specific state in it, so a CommandHandler cannot be shared by two or more Diagrams. Your most recent XAML, quoted above, would cause a single (static) CommandHandler instance to be shared by each Diagram created from the DataTemplate. This is why you would get those errors.

When you put a Diagram in a DataTemplate, WPF does not know about copying the CommandHandler when it applies the template. You’ll note that if you put a breakpoint in your CustomHandler’s constructor, it only breaks there once, because only one instance of your class is created.

So you need to make a copy of your custom CommandHandler in some other manner. I just gave you a trivial way to do so, but there might be others that would be suitable for your purposes.