views:

2563

answers:

4

I am having a hard time understanding the CommandTarget property for a RoutedCommand.

Basically, I have some static commands that have implementations in a user control (not the window). I create a commandbinding in the user control. If I declare the button in the usercontrol, then I am able to use my routed event. However, when the button is outside of the usercontrol, then I cannot use my routed event. I think the command target will solve my issue.

So how do I set the commandtarget for the toolbar usercontrol's button, so that the Container's Executed and CanExecuted is called?

Edited Code with changes from micahtan changes, but I still can't get it to CanExecute or Execute.

Window XAML:

<Window x:Class="RoutedCommands.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RoutedCommands"
    xmlns:toolbar="clr-namespace:RoutedCommands.Toolbar"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <local:Container Width="100" Height="25" x:Name="MyContainer" />
        <toolbar:Toolbar Width="100" Height="25" CommandTarget="{Binding MyContainer}" />
    </StackPanel>
</Window>

Toolbar XAML:

<UserControl x:Class="RoutedCommands.Toolbar.Toolbar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RoutedCommands"
    x:Name="MyToolbar"
    Height="300" Width="300">
    <Grid>
        <Button Command="{x:Static local:Commands.MyCommand}" Content="Try Me" CommandTarget="{Binding ElementName=MyToolbar, Path=CommandTarget, Mode=OneWay}" />
    </Grid>
</UserControl>

Toolbar CS:

    public partial class Toolbar : UserControl
    {
        public Toolbar()
        {
            InitializeComponent();
        }

        // Using a DependencyProperty as the backing store for CommandTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandTargetProperty =
                DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(Toolbar), new UIPropertyMetadata(null));

        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }
    }

Container XAML:

<UserControl x:Class="RoutedCommands.Container"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RoutedCommands"
    Height="300" Width="300">
    <UserControl.CommandBindings>
        <CommandBinding Command="{x:Static local:Commands.MyCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed" />
    </UserControl.CommandBindings>
    <Grid>
        <Button Command="{x:Static local:Commands.MyCommand}" Content="Click Me" />
    </Grid>
</UserControl>

Container CS:

public partial class Container : UserControl
{
    public Container()
    {
        InitializeComponent();
    }

    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        Console.WriteLine("My Command Executed");
    }

    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        Console.WriteLine("My Command Can Execute");
        e.CanExecute = true;
    }
}

RoutedCommands:

namespace RoutedCommands
{
    public static class Commands
    {
        public static readonly RoutedUICommand MyCommand = new RoutedUICommand(); 
    }
}
A: 

If you want to use CommandTargets, I would create a CommandTarget DependencyProperty on your custom UserControl, similar to the way it's defined on ButtonBase.

After doing that, set your Button's CommandTarget to your custom UserControl's CommandTarget.

EDIT: Code Sample

Rudi's comments are valid if you're doing an MVVM architecture -- RelayCommands or some other form of wrapped delegates work well in that case. Based on your code sample, it didn't look like you were using that approach, hence my original comment.

As for the code, you only need to change your ToolBar class. This assumes your MyCommand class inherits from RoutedUICommand. Here's the XAML:

<UserControl
    x:Class="WPFCommandTarget.CustomToolBar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPFCommandTarget"
    x:Name="theControl">
    <Grid>
     <Button
      x:Name="theButton"
      Command="{x:Static local:Commands.MyCommand}"
      CommandTarget="{Binding ElementName=theControl, Path=CommandTarget, Mode=OneWay}"
      Content="Try Me" />
    </Grid>
</UserControl>

And here's the code-behind:

using System.Windows; using System.Windows.Controls;

namespace WPFCommandTarget
{
    /// <summary>
    /// Interaction logic for CustomToolBar.xaml
    /// </summary>
    public partial class CustomToolBar : UserControl
    {
     public CustomToolBar()
     {
      InitializeComponent();
     }

     public IInputElement CommandTarget
     {
      get { return (IInputElement)GetValue(CommandTargetProperty); }
      set { SetValue(CommandTargetProperty, value); }
     }

     // Using a DependencyProperty as the backing store for CommandTarget.  This enables animation, styling, binding, etc...
     public static readonly DependencyProperty CommandTargetProperty =
      DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CustomToolBar), new UIPropertyMetadata(null));
    }
}

Please note that I've changed some of the class names/namespaces in my test project. You'll have to change them to suit your needs.

micahtan
Could you provide a small code sample or some direction?
daub815
A: 

Have you concidered to rather use the RelayCommand or DelegateCommand! Thy might be more suited to what you need?

For a example of using RelayCommand, read this article by Josh

Brian Noyes also have a excellent article available here

rudigrobler
DelegateCommand? Is that part of .Net or an add on?
daub815
@rudigrobler - are you sure that RelayCommands would work in this instance? His code example has the Container and ToolBar as siblings without any refs between them.
micahtan
Relay/DelegateCommands are not part of the framework... Get it from Josh's article or download Prism! Relay/DelegateCommand do not rely on the Visual tree at all... You give each command a delegate to execute and that is it...
rudigrobler
A: 

micatan nailed it for me. I just had to modify the binding in the Window.xaml to use the ElementName syntax:

   <toolbar:Toolbar Width="100" Height="25" CommandTarget="{Binding ElementName=MyContainer}" />

because, in my case, the "MyContainter" element has a different DataContext.

cosmo
A: 

you can dowm load csla 3.7.0-090721 version and csla netsamples-3.7.0-090721 ,them you open path:‘cslanetsamples-3.7.0-090721\CslaNet\cs\ProjectTrackercs’ ProjectTrackercs.sln you will see something you want

laolaowhn