1

I have a situation where I want to write custom command for framework element. Which I have done like below:

public class UndoRedoManager
    {
        private static FrameworkElement frameworkElement;

        /// <summary>
        /// UndoVMCommand Attached properrty.
        /// </summary>
        public static readonly DependencyProperty UndoVMCommandProperty =
            DependencyProperty.RegisterAttached("UndoVMCommand", typeof(ICommand), typeof(UndoRedoManager), new FrameworkPropertyMetadata(UndoVmCommand, UndoVMCommand_PropertyChanged));

        /// <summary>
        /// UndoVMCommandProperty getter.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        [AttachedPropertyBrowsableForChildren]
        public static ICommand GetUndoVMCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(UndoVMCommandProperty);
        }

        /// <summary>
        /// UndoVMCommandProperty setter.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="value"></param>
        public static void SetUndoVMCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(UndoVMCommandProperty, value);
        }

        protected static void UndoVMCommand_PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = obj as FrameworkElement;
            if (control != null)
            {
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    frameworkElement = control;
                }
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    frameworkElement = null;
                }
            }
        }
}

This I am attaching in the xaml thus:

<ItemsControl x:Name="graphControl" local:UndoRedoManager.UndoVMCommand="{Binding UndoCommand}">
......
</ItemsControl>

However, I want to fire this command on a button click.

<Button Content="Undo" CommandTarget="{Binding ElementName=graphControl}" Command="TheCommand" Margin="5"/>

Very much like

<Button Command="Copy" CommandTarget="{Binding ElementName=MyTextBox1}">Copy</Button>

So I have written the following:

public static RoutedUICommand undoVmCommand = new RoutedUICommand("TheCommand", "TheCommand", typeof(UndoRedoManager));
        public static RoutedUICommand UndoVmCommand
        {
            get { return undoVmCommand; }
        }
        static UndoRedoManager()
        {
            CommandManager.RegisterClassCommandBinding(typeof(UndoRedoManager), new CommandBinding(undoVmCommand, ExecutedEventHandler_UndoVM, CanExecuteEventHandler_IfCanUndoVM));
        }

        private static void CanExecuteEventHandler_IfCanUndoVM(Object sender, CanExecuteRoutedEventArgs e)
        {
            FrameworkElement frmEle = sender as FrameworkElement;
            e.CanExecute = false;
            if (null != frmEle && frmEle.DataContext is ViewModelBase)
            {
                e.CanExecute = true;
                    //(frmEle.DataContext as GraphViewModel).CanUndo;
            }
            e.Handled = true;
        }

        public static void ExecutedEventHandler_UndoVM(Object sender, ExecutedRoutedEventArgs e)
        {
            FrameworkElement frmEle = sender as FrameworkElement;

            if (null != frmEle && frmEle.DataContext is ViewModelBase)
            {
                (frmEle.DataContext as GraphViewModel).UndoCommand.Execute(null);
            }
        }

I am not getting it how wire that. Where should I declare the above routed command? Definitely I cannot do that in the FrameworkElement Class. IS there a way to attach that? I am sorry, if I havent been able to state the problem clearly. To put it in short simple way: If I were to write a "Copy" command for textbox in an attached manner, how can I do that?

EDIT: After @Erti's comment:

Now I have two classes, UndoRedoManager

public class UndoRedoManager
    {
        private static FrameworkElement frameworkElement;

        /// <summary>
        /// UndoVMCommand Attached properrty.
        /// </summary>
        public static readonly DependencyProperty UndoVMCommandProperty =
            DependencyProperty.RegisterAttached("UndoVMCommand", typeof(ICommand), typeof(UndoRedoManager), new FrameworkPropertyMetadata(StaticCommand.UndoVmCommand, UndoVMCommand_PropertyChanged));

        /// <summary>
        /// UndoVMCommandProperty getter.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        [AttachedPropertyBrowsableForChildren]
        public static ICommand GetUndoVMCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(UndoVMCommandProperty);
        }

        /// <summary>
        /// UndoVMCommandProperty setter.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="value"></param>
        public static void SetUndoVMCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(UndoVMCommandProperty, value);
        }

        protected static void UndoVMCommand_PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = obj as FrameworkElement;
            if (control != null)
            {
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    frameworkElement = control;
                }
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    frameworkElement = null;
                }
            }
        }
     }

Please note the default value passed in the PropertyMetadata while registering the dependency property. In xaml I am using it as:

<ItemsControl x:Name="graphControl" local:UndoRedoManager.UndoVMCommand="{Binding UndoCommand}"/>

Another is the StaticCommand class:

public class StaticCommand
    {
        public static RoutedUICommand undoVmCommand = new RoutedUICommand("TheCommand", "TheCommand", typeof(UndoRedoManager));
        public static RoutedUICommand UndoVmCommand
        {
            get { return undoVmCommand; }
        }
        static StaticCommand()
        {
            CommandManager.RegisterClassCommandBinding(typeof(StaticCommand), new CommandBinding(undoVmCommand, ExecutedEventHandler_UndoVM, CanExecuteEventHandler_IfCanUndoVM));
        }

        private static void CanExecuteEventHandler_IfCanUndoVM(Object sender, CanExecuteRoutedEventArgs e)
        {
            //This is not getting hit.
            e.CanExecute = true;
            e.Handled = true;
        }

        public static void ExecutedEventHandler_UndoVM(Object sender, ExecutedRoutedEventArgs e)
        {
            //I will do something here
        }
    }

In xaml I am using it as:

 <Button Content="Undo" CommandTarget="{Binding ElementName=graphControl}" Command="{Binding Source={x:Static local:StaticCommand.UndoVmCommand}}" Margin="5"/>

But now the above button is not getting activated at all.

James
  • 1,213
  • 2
  • 15
  • 26

1 Answers1

1

You basically have it. Just create new class StaticCommands.cs.

All you have to do is, now bind against the static command, as such:

<Button Command="{x:Static local:StaticCommands.UndoVmCommand}"
  CommandTarget="{Binding WHatever you want}" />

Now in your ExecutedEventHandler_UndoVM, you have access to Button element, which means you can also access CommandTarget property, if needed.

Check out this too: WPF Commands, How to declare Application level commands?

Community
  • 1
  • 1
Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78
  • Hi @Erti thanks. Now the Button is never enabled. And to my dismay, CanExecuteEventHandler_IfCanUndoVM is never getting hit. I have edited the question for the complete picture. – James Apr 05 '13 at 08:57
  • try Command="{x:Static local:StaticCommand.UndoVmCommand}" instead of binding it. I Updated my post. – Erti-Chris Eelmaa Apr 05 '13 at 18:20