ListBox Commands

Mar 15, 2011 at 8:39 AM

Hi,

How can use Commands in ListBox, TextBox etc...

Thanks...

Coordinator
Mar 15, 2011 at 11:36 AM
Edited Mar 15, 2011 at 11:37 AM

That really depends on where the command is defined. You have two options:

1) Command is defined in item template control view-model

An item template automatically gets a new datacontext, as the item. So, if you have a list of customers, each item template will have the customer it represents as datacontext. It is recommended to create a user control for every item template if you want to show some advanced stuff (such as commands). If you just want to show a label, this is enough:

 

<ListBox.ItemTemplate>
  <DataTemplate>
    <Label Content="{Binding Name}" />
  </DataTemplate>
</ListBox.ItemTemplate>

 

However, if you want to add commands, it is recommend that you create a user control that accepts a Customer as constructor argument. This way, a view-model is automatically constructed:

 

<ListBox.ItemTemplate>
  <DataTemplate>
    <Controls:MyCustomerControl DataContext="{Binding}" />
  </DataTemplate>
</ListBox.ItemTemplate>

 

2) Command is defined in the view-model above

It is also possible that you want to bind to a command that is part of a "higher" view-model. Then, you will have to use RelativeSource to find the container for the view-model and then bind it. Assuming that the ListBox is used in a window, something like this should be used:

 

<ListBox.ItemTemplate>
  <DataTemplate>
    <Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.MyCommand}" Content="Click me!" />
  </DataTemplate>
</ListBox.ItemTemplate>

 

I hope this answers your question.

Mar 15, 2011 at 1:57 PM

Thank you for your detailed reply.

another question;

if i want to catch "SelectedIndexChanged" event ( or other events ) with a ListBox , how should i do that? Button's Command property works fine when i press it and runs the command in ViewModel.

you have used a EventToCommand for MouseDoubleClick  in your SocialMedia Demo. But i couldnt implement for ListBox...

 

thanks.

Coordinator
Mar 15, 2011 at 2:04 PM
Edited Mar 15, 2011 at 2:04 PM

You should use the ListBox.SelectionChanged event. Another way to do this is creating a property SelectedCustomer on your view-model (you probably need this anyway). Then, you can simply subscribe to the changed event. The cool thing about Catel is that you can immediately subscribe to changes of a property on the view-model like this:

 

/// <summary>
/// Gets or sets the selected customer.
/// </summary>
public ICustomerInfo SelectedCustomer
{
    get { return GetValue<ICustomerInfo>(SelectedCustomerProperty); }
    set { SetValue(SelectedCustomerProperty, value); }
}

/// <summary>
/// Register the SelectedCustomer property so it is known in the class.
/// </summary>
public static readonly PropertyData SelectedCustomerProperty = RegisterProperty("SelectedCustomer", typeof(ICustomerInfo),
    (sender, e) => ((MyViewModel)sender).OnSelectedCustomerChanged());

private void OnSelectedCustomerChanged()
{
    if (SelectedCustomer != null)
    {
        // Do you thing with a customer here
    }
}

 

Soon, we will release a brand new example application with source code that includes lots of advanced features of Catel, but it's kind of a secret yet, so I can't provide any more details yet...

Mar 16, 2011 at 7:41 AM

thank you  for the reply. i will be waiting for your new Catel Demo ;)

if i try to use IPleaseWait servis in onPropertyChanged (OnSelectedCustomerChanged in your example above) and  change the property from VM, IpleaseWaitService gives

"The calling thread must be STA, because many UI components require this"

but if i change the property from View via button it works well.

thanks.

Coordinator
Mar 16, 2011 at 7:43 AM

Can you show the method where you call the service? And what property are you trying to change?

The PleaseWaitService uses the PleaseWaitWindow, and this window runs the data in the UI thread. The animation that you see is ran in another thread. So, make sure you are not manually creating a thread.

Mar 16, 2011 at 8:00 AM
        void OnCardSwiped(string cardNumber)
        {
            CardNumber = cardNumber;
        }
       

// TODO: Register view model properties with the vmprop or vmpropviewmodeltomodel codesnippets

        public string CardNumber
        {
            get { return GetValue<string>(CardNumberProperty); }
            set { SetValue(CardNumberProperty, value); }
        }

        public static readonly PropertyData CardNumberProperty = RegisterProperty("CardNumber"typeof(string), (sender, e) => ((MyViewViewModel)sender).OnCardNumberChanged(e));
        private void OnCardNumberChanged(EventArgs e)
        {
            
            var pw = GetService<IPleaseWaitService>();
                pw.Show(() => Thread.Sleep(2000), String.Format("Card Read : {0}", CardNumber));
        }
        #region Commands
        public Command<object> setCardNumber { getprivate set; }
        private void OnsetCardNumberExecute(object parameter)
        {
            CardNumber = CardNumber;
        }
        #endregion

when i set the property in OnCardSwiped event handling method it gives the error

but if i set it in View via setCardNumber Command , works fine.

Coordinator
Mar 16, 2011 at 8:05 AM
Edited Mar 16, 2011 at 8:07 AM

This code is pretty strange:

private void OnsetCardNumberExecute(object parameter)
{
CardNumber = CardNumber;
}

Or do you really want to apply the same value here?

 

Do you use any threading anywhere else? For example, what is causing the CardNumber property to change? Is it a thread? There is an escape here, but I don't want to recommend it. Anyway, here it is (but again, it's better to find out why to change is coming from another thread):

Every view-model has a Dispatcher property. This is the UI dispatcher in case you have to deal with several different threads. So, you can simply call Dispatcher.BeginInvoke() or Invoke() (depends on whether you want to wait for the action to complete).

Mar 16, 2011 at 8:35 AM

Actually i use KeyboardListener (here, and here) to fire CardSwiped Event. When CardSwiped Fired (OnCardSwiped), i set the CardNumber property. KeyBoardListener uses some threads using User32.dll.

Does it cause the problem?

private void OnsetCardNumberExecute(object parameter)
{
    CardNumber = CardNumber;

}
code piece is just a dummy code to try something :) you can think like CardNumber = "1234567"
Coordinator
Mar 16, 2011 at 8:40 AM

Strange, I see some threading going on there, but it uses a dispatcher. In your case, I recommend to use the Dispatcher of the view-model to dispatch to the right thread.

CardNumber = CardNumber might cause stackoverflow if you are not careful and call that in the OnCardNumberChanged handler.

 

Hope this helps you out.

Mar 16, 2011 at 8:54 AM

Thank you very much for your help