views:

563

answers:

8

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.

A: 

Hi, 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.

Michael Detras
Yeah! I like your idea, this will be a simple solution as you say. The advantage of doing it is that I can reuse this user control for other ocassions. I'll try what you say and also Robert's solution that seems to be another great solution. Thanks very much for spending a little of your time helping me.
Hi Mike, I am implementing your solution and now I have a problem. I place the custom control into my window and now I have the possibility to indicate the command I want because I have registered it in the class as a dependency property but now How can I indicate Executed and CanExecute methods that I want? I would like the behavior of my custom control was like a button when you define command, executed and canExecute. thanks.
A: 

Take a look at attached behaviors.

Robert S.
Excellent article! I am watching this to try applying it to my listbox. I haven't used attached behaviors before but I think it is another alternative to commands. But viewing it I'm a little confused between using commands or attached behaviors. Which is the difference? (because they are quite similiar) and when to use one or another? Which is the most efficient?Thanks for your great answer.
I think attached behavior is not valid for this case.For example, I have register an event listener for the MouseEnter and first time, when background worker is running,it works,listbox items appears disabled when mouseEnter event raises for first time. But once the listbox item (or items) appears disabled there is no way to return to enabled state when background worker finishes.Of course, this is because they are disabled and MouseEnter event is not raising.So I think attached behavior is not suitable to re-enable/disable controls.Once you disable them there is no way to enable by themselves
A: 

Hey,

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.

A: 

Hi,

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.

A: 

Hi Michael,

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.

A: 

Hi again!

I was a little confused.

CanExecute is used to determine whether a command can execute on an element but does not update enabled state of the Controls that are associated with that command.

When the background worker finishes, in RunWorkerCompleted method I use CommandManager.InvalidateRequerySuggested() method to force to update the listbox's state (property isEnabled) immediatelly.

The problem is that after doing it, despite of in the custom class I have implemented CanExecuteChanged and do into it this.IsEnabled=true, the listbox doesn't enabled on background worker finishes. I continue having the problem, if I don't place the last line in CommandBinding_CanExecute method, the listbox doesn't return to true state on background worker finishes.

Any ideas about this behavior?

Thanks a lot!

A: 

Hi,

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);
        }
    }
}
Michael Detras
Please see my last post I have just put. It is so extensive to put here. Thanks.
Ok, I got accustomed with Josh Smith's RelayCommand that I haven't been able to use RoutedCommand for a while. You could use the RelayCommand if you want to. It is easier to use for this scenario in my opinion. Anyway, have you called the CommandManager.InvalidateRequerySuggested() method on the UI dispatcher? Well, is it possible to call it in other dispatchers?
Michael Detras
I'm not sure if I'm understanding you.I believe you refer to Dispatcher.Invoke of the control.I don't call CommandManager.InvalidateRequerySuggested using Dispatcher.Invoke,simply I call it directly.I haven't tried this.As far as I know you only have to call Dispatcher.Invoke in case you want to update any UI control from a thread that doesn't own it.In my case it's not necessary to use it because I always call cmd Manager from the owner of that control.I've heard in another thread that you have to call InvalidateRequerySuggested from main thread,otherwise sometimes works and sometimes not.
The overrided method seems to be not valid over a Listbox because I receive an error on it. OnSelected is not a member and base.OnSelected(e) is not valid too.
I thought you were going to inherit from ListBoxItem so I gave you a code snippet for that. Since you inherited from ListBox, the code snippet I gave you does not apply.
Michael Detras
A: 

Hi Michael,

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.