Search code examples
c#wpfbuttonclick

WPF: How to get the Current Object from a List when a Button is pressed


Problem I want to delete one article from a ObserableCollection when i Press the Red "X". But i don´t know how to get the Current Artcile. I have a class Artcile. and the list which saves the different Articles.

Xaml Code

 <ItemsControl ItemsSource="{Binding CartArticles}">
 <ItemsControl.ItemTemplate>
     <DataTemplate>
         <Border BorderBrush="Black" BorderThickness="2" CornerRadius="30,30,30,30" Margin="0,15,0,30" MinWidth="100" MaxWidth="600" >
             <Grid Margin="0,0,0,10" Grid.Row="0">
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition />
                     <ColumnDefinition />
                     <ColumnDefinition />
                 </Grid.ColumnDefinitions>


                 <Image Width="100" Height="70" Source="{Binding PathPic}" Margin="0,10,0,0"/>
                 <TextBlock FontWeight="Bold"  Grid.Column="1" FontSize="17" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Name}"/>

                 <StackPanel Grid.Column="2" HorizontalAlignment="Center" Orientation="Horizontal" VerticalAlignment="Center" >
                     <TextBlock FontWeight="Bold" FontSize="17" Foreground="DimGray" Text="{Binding Quantity}"/>
                     <TextBlock Text=" x " FontSize="16" Foreground="DimGray" />
                     <TextBlock FontWeight="Bold"  FontSize="17" Foreground="DimGray" Text="{Binding Price}" />
                     <TextBlock FontWeight="Bold"  FontSize="17" Foreground="DimGray" Text=" Euro" />
                 </StackPanel>

                 <StackPanel Grid.Column="2" HorizontalAlignment="Center" Orientation="Horizontal" VerticalAlignment="Bottom" >
                     <TextBlock FontWeight="Bold"  Grid.Column="2" FontSize="17" HorizontalAlignment="Center" VerticalAlignment="Bottom" Text="{Binding CompleteArticlePrice}"/>
                     <TextBlock FontWeight="Bold"  FontSize="17" Foreground="DimGray" Text=" Euro" />
                 </StackPanel>
                 <Button Name="DeleteCartArticle" Grid.Column="2" Width="20" Height="20" Margin="0,13,20,0" Content="X" FontSize="15" Foreground="Red" Background="Transparent" BorderThickness="0" HorizontalAlignment="Right" VerticalAlignment="Center" Click="DeleteCartArticle_Click"/>
             </Grid>
         </Border>
     </DataTemplate>
 </ItemsControl.ItemTemplate>

ObserableCollection

public ObservableCollection<Article> CartArticles { get; set; } = new ObservableCollection<Article>();

public void GetCartArticles()
{
    foreach (var article in _shoppingCartService.CartArticles.ToList())
    {
        CartArticles.Add(new Article
        {
            ArticleId = article.ArticleId,
            Name = article.Name,
            Price = article.Price,
            Quantity = article.Quantity,
            PathPic = article.PathPic,
            CompleteArticlePrice = article.Price * article.Quantity,
        });
    }
}

Output

enter image description here


Solution

  • Nice to see you're keep progressing from previous question :)

    As @Clemens suggested, for buttons in MVVM you should use bindings too, and commands, which will handle click actions. But in case you use ItemsControl (not ListView or ListBox, which has SelectedItem proeprty to simplify our life) there is a little trick with button binding (described in XAML example-part below).

    So, first of all you need to create a custom class which implements ICommand interface from System.Windows.Input namespace. Custom command class should accept some object as parameter, and that "object" is an item from ItemsControl. So, let's call it ParameterizedCommand:

    using System;
    using System.Windows.Input;
    
    namespace YourNamespace.Commands
    {
        public sealed class ParameterizedCommand : ICommand
        {
            private readonly Action<object> _action;
    
            public ParameterizedCommand(Action<object> action) => _action = action;
    
            public bool CanExecute(object parameter) => true;
            public void Execute(object parameter) => _action(parameter);
    
            public event EventHandler CanExecuteChanged
            {
                add => CommandManager.RequerySuggested += value;
                remove => CommandManager.RequerySuggested -= value;
            }
        }
    }
    

    Now in your ViewModel (which is CartViewModel from previous answer) add command to remove CartArticle from CartArticles collection:

    public class CartViewModel
    {
        // A collection, which stores articles in cart
        public ObservableCollection<CartArticle> CartArticles { get; } = new ObservableCollection<CartArticle>();
    
        private ICommand _removeCartArticle;
        public ICommand RemoveCartArticle => _removeCartArticle ?? (_removeCartArticle = new ParameterizedCommand(parameter =>
        {
            // Ensure that received parameter is CartArticle
            var cartArticle = parameter as CartArticle;
            if (cartArticle == null)
                return;
    
            CartArticles.Remove(cartArticle);
        }));
    
        // ...
    }
    

    And finally, in your View bind button to that command. To allow button in item template "find" ViewModel and stored command, you can use RelativeSource, which is parent ItemsControl, and as CommandParameter we passing the item itself:

    <ItemsControl ItemsSource="{Binding CartArticles}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>               
                <!-- ... -->                   
                <Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=DataContext.RemoveCartArticle}"
                        CommandParameter="{Binding}"/>
                <!-- ... -->   
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    Now "delete" button in each CartArticle on click will remove that item from a collection. Just don't forget again append some styling:

    enter image description here