Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event Handlers on DataTemplate inside ItemsControl

Tags:

c#

mvvm

wpf

I have an ItemsControl so that I can display multiple instance of the same template. I need to be able to execute code on event handlers so that I can tell controls apart.

For example: I have a list of groceries, so my DataTemplate contains a "buy" Button for each food. I want to bind said button to code and tell which button was pressed.

How can I accomplish that, considering I'm using MVVM design pattern

** XAML :**

<ItemsControl ItemsSource="{Binding MyItemList}">
     <ItemsControl.ItemsTemplate>
          <DataTemplate>
              <Button Content="Buy" />
          </DataTemplate> 
     </ItemsControl.ItemsTemplate>
</ItemsControl>

So, MyItemList is a List<MyItem> instance. The DataTemplate contains controls that modify values or execute code not present in MyItem:

I have read a lot of articles on biding templates to commands, but I cant find one that uses a list of items.

like image 310
Christopher Francisco Avatar asked Jan 19 '15 22:01

Christopher Francisco


2 Answers

You need to bind the Button to a Command your ItemsControl's DataContext.

Search for Command in WPF : ( A Common implementation ) :

public class RelayCommand<T> : IRelayCommand
{
    private Predicate<T> _canExecute;
    private Action<T> _execute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    private void Execute(T parameter)
    {
        _execute(parameter);
    }

    private bool CanExecute(T parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public bool CanExecute(object parameter)
    {
        return parameter == null ? false : CanExecute((T)parameter);
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var temp = Volatile.Read(ref CanExecuteChanged);

        if (temp != null)
            temp(this, new EventArgs());
    }
}

In your ViewModel ( The ItemsControl's DataContext , I Hope :) )

   private RelayCommand<FoodItem> _addToGroceriesCommand;
   public ICommand AddToGroceriesCommand
   {
        get
        {
            if (_addToGroceriesCommand == null)
            {
                _addToGroceriesCommand = new RelayCommand<FoodItem>(OnAddToGroceries);                    
            }
            return _addToGroceriesCommand;
        }
    }

   public void OnAddToGroceries(FoodItem newItem)
   {

   }

XAML :

   <ItemsControl ItemsSource="{Binding MyItemList}">
      <ItemsControl.ItemsTemplate>
         <DataTemplate>
             <Button Content="Buy" 
                     Command="{Binding Path=DataContext.AddToGroceriesCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                     CommandParameter="{Binding}" />
         </DataTemplate> 
      </ItemsControl.ItemsTemplate>
   </ItemsControl> 
like image 108
eran otzap Avatar answered Nov 09 '22 10:11

eran otzap


You should never use events in DataTemplates this will make you use casting and then blow a hole in the whole MVVM pattern. A button has the Command property and you should Bind that property to a command inside your MyItem ViewModel.

If you still need to use an event (for instance you cant bind MouseDown to a command) you shoudl use the EventToCommadn Behaviour which allows you to bind an event to a command. You can read about it here: http://msdn.microsoft.com/en-us/magazine/dn237302.aspx

like image 3
Amit Raz Avatar answered Nov 09 '22 11:11

Amit Raz