CodeHulk
CodeHulk

Reputation: 111

ICommandSource and DelegateCommand

I am trying to make a usercontrol with some commands. If I wire up the commands in xaml using the approach shown here http://msdn.microsoft.com/en-us/library/vstudio/ms771361(v=vs.90).aspx it works but if I use the DelegateCommand from the Prism library CanExecuteChanged doesn't fire on the usercontrol and I cannot figure out why. I apologize I realize this is a lot of code. Execute fires correctly but CanExecute never does.

Thanks in advance.

Custom Control Xaml

<UserControl x:Class="Controls.LinkButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <TextBlock>
        <Hyperlink x:Name="hyperLink" Click="Hyperlink_Click">
            <Run x:Name="textRun"
                 Text="Click Me"/>
        </Hyperlink>
    </TextBlock>
</UserControl>

Custom Control Code Behind

public partial class LinkButton : UserControl, ICommandSource
{
    public LinkButton()
        : base()
    {
        InitializeComponent();
        textRun.DataContext = this;
        hyperLink.DataContext = this;
    }

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(LinkButton), new PropertyMetadata(null, new PropertyChangedCallback(CommandChanged)));

    public object CommandParameter
    {
        get { return (object)GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(LinkButton), new PropertyMetadata(null));

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

    public static readonly DependencyProperty CommandTargetProperty =
        DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(LinkButton), new PropertyMetadata(null));

    private void Hyperlink_Click(object sender, RoutedEventArgs e)
    {
        if (Command != null)
        {
            RoutedCommand command = Command as RoutedCommand;

            if (command != null)
            {
                command.Execute(CommandParameter, CommandTarget);
            }
            else
            {
                ((ICommand)Command).Execute(CommandParameter);
            }
        }
    }

    public static EventHandler canExecuteChangedHandler;

    private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        LinkButton lb = (LinkButton)d;
        lb.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
    }

    // Add a new command to the Command Property. 
    private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
    {
        // If oldCommand is not null, then we need to remove the handlers. 
        if (oldCommand != null)
        {
            RemoveCommand(oldCommand, newCommand);
        }
        AddCommand(oldCommand, newCommand);
    }

    // Remove an old command from the Command Property. 
    private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
    {
        EventHandler handler = CanExecuteChanged;
        oldCommand.CanExecuteChanged -= handler;
    }

    // Add the command. 
    private void AddCommand(ICommand oldCommand, ICommand newCommand)
    {
        EventHandler handler = new EventHandler(CanExecuteChanged);
        canExecuteChangedHandler = handler;
        if (newCommand != null)
        {
            newCommand.CanExecuteChanged += canExecuteChangedHandler;
        }
    }

    private void CanExecuteChanged(object sender, EventArgs e)
    {
        if (this.Command != null)
        {
            RoutedCommand command = this.Command as RoutedCommand;

            // If a RoutedCommand. 
            if (command != null)
            {
                if (command.CanExecute(CommandParameter, CommandTarget))
                {
                    this.IsEnabled = true;
                }
                else
                {
                    this.IsEnabled = false;
                }
            }
            // If a not RoutedCommand. 
            else
            {
                if (Command.CanExecute(CommandParameter))
                {
                    this.IsEnabled = true;
                }
                else
                {
                    this.IsEnabled = false;
                }
            }
        }
    }
}

Window

<ctrl:LinkButton Command="{Binding LinkClicked}"/>


public partial class MainWindow : Window
{
    public MainWindow()
    {
        LinkClicked = new DelegateCommand(DoSomething, CanDoSomething);

        InitializeComponent();
        DataContext = this;
        LinkClicked.RaiseCanExecuteChanged();
    }

    public DelegateCommand LinkClicked { get; set; }

    public void DoSomething()
    {
        MessageBox.Show("Did Something");
    }

    public bool CanDoSomething()
    {
        return false;
    }
}

Upvotes: 1

Views: 1306

Answers (1)

yo chauhan
yo chauhan

Reputation: 12295

The problem here is you are calling LinkClicked.RaiseCanExecuteChanged(); immediately after setting DataContext and till then the LinkedCommand is not binded and hence CanExecuteChanged event of the DelegateCommand is null and hence RaiseCanExecuteChanged() does not do anything. so to avoid this call LinkClicked.RaiseCanExecuteChanged() in the loaded event of Window because till then binding will be updated. Though this is a dirty solution because you will have to do this everywhere where you will use this LinkButton and bind its Command.

The implementation of RaiseCanExecuteChanged is something like this

    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)  //CanExecuteChanged is coming null in your case so the event is not fired.
            CanExecuteChanged(this, new EventArgs()); 
    }

Or the better solution is that you havent called CanExecute in AddCommand method, In actual Command implementation CanExecute is called

if (newCommand != null)
        {
            newCommand.CanExecuteChanged += CanExecuteChanged;
            newCommand.CanExecute(CommandParameter); //you are missing this.
        }

If you do this then there is no need to call RaiseCanExecuteChanged.

Upvotes: 1

Related Questions