Question

In my WPF application there is a listbox with items. The listbox is populated via a xmldataprovider from XAML and then binding it to Itemssource property of the listbox.

Well, from XAML, I bind a comand to the listbox by doing:

                      <ListBox.CommandBindings>
                          <CommandBinding 
                              Command="{x:Static local:mainApp.MyCmd}" 
                              CanExecute="CanExecute"
                              Executed ="Executed" />
                      </ListBox.CommandBindings>

but I don't know how to programatically bind a command to each listboxitem. How to do it?

Thanks in advance.


First sorry by not posting it as a comment. I can't put all this in a comment.

Ok, yes I am not using the Executed and CanExecute properties of the ICommandSource despite I have registered and implemented them in custom class (in xaml they are commented too). I have specified them in routedCommand but not in custom class, I have done it in the constructor of the window by doing this:

WinMain code behind:

public WinMain()
{
   InitializeComponent();

   // Command binding. If I don't do this Executed and CanExecute are not executed
   CommandBindings.Add(new CommandBinding(rcmd, 
      CommandBinding_Executed, CommandBinding_CanExecute));
}

and then I implement these methods in WinMain code behind too as it:

// ExecutedRoutedEventHandler
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
   // Do stuff

}

// CanExecuteRoutedEventHandler
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{

    // cBgWorkers is a class that check if a background worker is running
    e.CanExecute = !cBgWorkers.isRunning;

    //if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
}

and In WinMain XAML I invoke the command like this:

<Classes:CommandListBox x:Name="LayoutListBox"
 Command="{x:Static local:WinMain.rcmd}"
  ... >

 <...>

 </Classes:CommandListBox>

And in my custom class CommandListBox I have a CanExecuteChanged in which you can see I enabled or disabled the control depending on whether the background worker is finished or not:

private void CanExecuteChanged(object sender, EventArgs e)
{
    this.Enabled = !cBgWorkers.isRunning;
}

but in custom class I haven't implement the event handler you say, OnSelected.

WIthout implementing it all goes ok, the custom control invoke command, and CanExecute method is reached, and CanExecute gets correct value, true or false depending on background worker is finished or not, and the CanExecuteChanged in custom control is raised when CanExecute changes its value. When background worker starts it gets disabled but when it finished it doesn't get enabled. I have debugged, and when background worker finishes I can see CanExecuteChanged is executed and this.Enabled is getting correct value (true) but for some reason in the UI the control continues disabled despite it gets its correct value and despite in RunWOrkerCompleted (in background worker) I force to update UI with CommandManager.InvalidateRequerySuggested().

I solve this by uncomment line:

if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;

in CanExecute method. I don't understand what happens.

Then If I do what you say It is not necessary to do it:

   CommandBindings.Add(new CommandBinding(rcmd, 
      CommandBinding_Executed, CommandBinding_CanExecute));

and CommandBinding_Executed & CommandBinding_CanExecute implementations. Am I right?

but if I remove these methods where can I set this.enabled = !cBgWorkers.isRunning ?

I would like WPF to set automatically isEnabled property for my custom control. How to do this?

Thanks in advance.


I am applying the article you say about attached behaviors with a few changes to adapt it to my ListBox. It doesn't work well or perhaps I am doing something wrong. What I want is avoid that ListBox members (listBoxItems) can be select when a long task (background worker) is running. So one of the method of the article that I have modified is:

    static void OnListBoxItemSelected(object sender, RoutedEventArgs e)
    {
        // Only react to the Selected event raised by the ListBoxItem
        // whose IsSelected property was modified.  Ignore all ancestors
        // who are merely reporting that a descendant's Selected fired.
        if (!Object.ReferenceEquals(sender, e.OriginalSource))
            return;

        ListBoxItem item = e.OriginalSource as ListBoxItem;
        if (item != null)
        {

            // (*) See comment under
            item.IsEnabled = !cBgWorkers.isRunning;
            if (!cBgWorkers.isRunning)
            {
                item.BringIntoView();
            }
        }
    }

(*) cBgWorkers is a public static Class that has some methods and properties. One of the properties is isRunning that indicates no background workers is currently running. Then If no background workers is running, listbox members have to be enabled otherwise they have to be disabled so when user clicks over one listbox item the current page don't change to another one because I disabled it before (each listBox items has attached one page in my main application).

When one of the background workers (bw) or all are running and I select listbox item all is ok: listbox item is disabled because there are bw running and it avoid tho change the current page to another one. Of course, If I disabled the listbox item (or listbox items) I can't select it again because it is disabled and that is my problem, because I want that when bw finish the listbox items that have been disabled while bw were running, they get enabled again. Unfortunately with attached behavior as I see it isn't done by WPF automatically and commands have this advantage (controls update automatically by WPF). so, how to disabled/re-enabled listbox items when bw is running or not respectively?

As far as I know and see, one advantage of attached behaviors is that I think it is more efficient because they are not invoking actions constantly (only when the action, for example, selection, is produced). Commands are constantly (not often) checking if actions binded to controls can be executed (so If they can be executed, WPF automatically enables controls otherwise they appear disabled), right?

Thanks.

Was it helpful?

Solution

you could try creating a custom control that derives from ListBoxItem and implementing the ICommandSource interface. I can't think of a more simple solution as of now.

OTHER TIPS

I have done your solution. I have done a custom user control deriving from listbox and implementing ISourceCommand as you said and it works now!!!! ;)

My custom class:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Input;

namespace GParts.Classes
{
public class CommandListBox : ListBox, ICommandSource
{
    public CommandListBox() : base()
    {

    }

    // ICommand Interface Members
    // Make Command a dependency property so it can use databinding.
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register(
            "Command",
            typeof(ICommand),
            typeof(CommandListBox),
            new PropertyMetadata((ICommand)null,
            new PropertyChangedCallback(CommandChanged)));

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

    // Make Command a dependency property so it can use databinding.
    public static readonly DependencyProperty ExecutedProperty =
        DependencyProperty.Register(
            "Executed",
            typeof(object),
            typeof(CommandListBox),
            new PropertyMetadata((object)null));

    public object Executed
    {
        get
        {
            return (object)GetValue(ExecutedProperty);
        }
        set
        {
            SetValue(ExecutedProperty, value);
        }
    }

    // Make Command a dependency property so it can use databinding.
    public static readonly DependencyProperty CanExecuteProperty =
        DependencyProperty.Register(
            "CanExecute",
            typeof(object),
            typeof(CommandListBox),
            new PropertyMetadata((object)null));

    public object CanExecute
    {
        get
        {
            return (object)GetValue(CanExecuteProperty);
        }
        set
        {
            SetValue(CanExecuteProperty, value);
        }
    }

    // Make CommandTarget a dependency property so it can use databinding.
    public static readonly DependencyProperty CommandTargetProperty =
        DependencyProperty.Register(
            "CommandTarget",
            typeof(IInputElement),
            typeof(CommandListBox),
            new PropertyMetadata((IInputElement)null));

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

    // Make CommandParameter a dependency property so it can use databinding.
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register(
            "CommandParameter",
            typeof(object),
            typeof(CommandListBox),
            new PropertyMetadata((object)null));

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

    // Command dependency property change callback.
    private static void CommandChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        CommandListBox clb = (CommandListBox)d;
        clb.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;

        //newCommand.Execute(null);
        //newCommand.CanExecute(null);

    }

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

            //newCommand.Execute(Executed);
            //newCommand.CanExecute(CanExecute);
        }
    }
    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;
                }
            }
        }
    }

    // Keep a copy of the handler so it doesn't get garbage collected.
    private static EventHandler canExecuteChangedHandler;
}
}

and in my WinMain.xaml:

    <Classes:CommandListBox x:Name="LayoutListBox"
     Command="{x:Static local:WinMain.rcmd}"

     <!-- These lines doesn't work I explain it following
     Executed="CommandBinding_Executed"
     CanExecute="CommandBinding_CanExecute" 
     -->

      ... >

     <...>

     </Classes:CommandListBox>

and window code behind:

    public WinMain()
    {
       InitializeComponent();

       // Command binding. If I don't do this Executed and CanExecute are not executed
       CommandBindings.Add(new CommandBinding(rcmd, 
          CommandBinding_Executed, CommandBinding_CanExecute));
    }

    public static RoutedCommand rcmd = new RoutedCommand();

    // ExecutedRoutedEventHandler
    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
       // Do stuff

    }

    // CanExecuteRoutedEventHandler
    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {

        e.CanExecute = !cBgWorkers.isRunning;

        //if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
    }

but I have the same problem as the another solution. If I don't place the last line (here appears commented in CommandBinding_CanExecute) the listbox doesn't enable automatically by wpf when background worker finishes. If I put this line it works. What's happens?

Another thing, as you can see in my code snippet I would like to do the same as I do with a button where you can indicate command, executed and canexecute. I have registered them in the class, and in listbox I checked to pass the methods but it didn't work. How can I do this?

Thanks very much.

Take a look at attached behaviors.

According to the first question I posted, using CommandBindings in listbox it doesn't work. The implementation of CanExecute was:

    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {

        e.CanExecute = !cBgWorkers.isRunning;         

    }

By doing WPF doesn't enable/disabled the listbox control automatically depending on the background worker state (Running or not) and I don't understand why because I have other controls like buttons with commands binded and WPF automatically enable/disable them.

So I have done the following modification:

    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {

        e.CanExecute = !cBgWorkers.isRunning;

        if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
    }

Now, it works. Listbox is enable when no background worker is running and disabled otherwise bu what I don't like is the last line placed into the method in where I enable/disable manually the property isEnabled of the listbox. It is inefficient so I would like to change isEnabled property of the listbox only when CanExecute changes its value. As far as I know there is a event to do this, it is CanExecuteChanged but I don't know how to implement it. Any ideas?

Now, after trying several solutions, I am implementing the Mike's solution because I think it is easier and clearer and it can be re-used for other controls with only a few changes.

I haven't been able the whole thread. It's quite long. Anyway, I thought you want to put a command on a ListBoxItem? From what I see, you inherited from ListBox. You do not need to specify the Executed and CanExecute properties of the ICommandSource. This should be specified in your RoutedCommand, not in your custom control. To get your command executed, you need to provide an event handler in your custom control. As an example, if an item gets selected, then you execute the command. Here's an example.

protected override void OnSelected(RoutedEventArgs e)   
{
    base.OnSelected(e);

    if (this.Command != null)
    {
        RoutedCommand command = Command as RoutedCommand;

        if (command != null)
        {
            command.Execute(CommandParameter, CommandTarget);
        }
        else
        {
            ((ICommand)Command).Execute(CommandParameter);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top