WPF Bindowanie, odwołanie do metody

0

Witam serdecznie,

zaczynam przygodę z WPF w C# i napotkałem pewien problem.
Sprawa wygląda następująco:
Mam listę obiektów, powiedzmy listę pracowników danej firmy.
Wyświetlana jest ona następująco:

    <UserControl.Resources>
        <DataTemplate DataType="{x:Type data:Employees}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text = "{Binding Path = Id, Mode = OneWay}" 
                                     Width = "100" Margin = "3 5 3 5"/>

                <TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}" 
                                    Width = "100" Margin = "0 5 3 5"/>

                <TextBox Text = "{Binding Path = LastName, Mode = TwoWay}" 
                                    Width = "100" Margin = "0 5 3 5"/>

                <Button x:Name="btnDeleteEmployee"
                        Command = "{Binding DeleteCommand}"
                        Width="30" Height="30" HorizontalContentAlignment="Center"
                            FontWeight="Bold" Foreground="#FFFFFFFF" Background="#FF990000" 
                            BorderBrush="{x:Null}">
                    <Button.Template>
                        <ControlTemplate TargetType="{x:Type Button}">
                            <Border x:Name="bdrButtonHover" BorderThickness="1" Background="#FF990000" Padding="0 0 0 0">
                                <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" 
                                                      ContentSource="Content" Content="X" />
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter TargetName="bdrButtonHover" Property="Background" Value="#FFFF0000"/>
                                </Trigger>
                                <Trigger Property="IsPressed" Value="True">
                                    <Setter TargetName="bdrButtonHover" Property="Background" Value="#FFCC0000"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>

Poniżej ViewModel:

class EmployeeViewModel
{
        private Employee _selectedEmployee;

        public Helper.CommandHandler DeleteCommand { get; set; }
        public ObservableCollection<Employee> Employees
        {
            get;
            set;
        }

        public Employee SelectedEmployee
        {
            get { return _selectedEmployee; }
            set
            {
                _selectedEmployee= value;
                DeleteCommand.RaiseCanExecuteChanged();
            }
        }

        public EmployeeViewModel()
        {
            LoadEmployees();
            DeleteCommand = new Helper.CommandHandler(OnDelete, CanDelete);
        }

        public void LoadEmployees()
        {
            ObservableCollection<Employee> employees= new ObservableCollection<Employee>();

            employees.Add(new Employee{ Id = 1, FirstName = "Adam", LastName= "Nowak" });
            employees.Add(new Employee{ Id = 1, FirstName = "Jan", LastName= "Kowalski" });

            Employees = employees;
        }

        private void OnDelete()
        {
            Employees .Remove(SelectedEmployees);
        }

        private bool CanDelete()
        {
            return SelectedEmployees!= null;
        }
}

Klasa implementująca ICommand:

 public class CommandHandler : ICommand
    {
        Action _TargetExecuteMethod;
        Func<bool> _TargetCanExecuteMethod;

        public CommandHandler(Action executeMethod)
        {
            _TargetExecuteMethod = executeMethod;
        }

        public CommandHandler(Action executeMethod, Func<bool> canExecuteMethod)
        {
            _TargetExecuteMethod = executeMethod;
            _TargetCanExecuteMethod = canExecuteMethod;
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }

        bool ICommand.CanExecute(object parameter)
        {

            if (_TargetCanExecuteMethod != null)
            {
                return _TargetCanExecuteMethod();
            }

            if (_TargetExecuteMethod != null)
            {
                return true;
            }

            return false;
        }
		
        public event EventHandler CanExecuteChanged = delegate { };

        void ICommand.Execute(object parameter)
        {
            if (_TargetExecuteMethod != null)
            {
                _TargetExecuteMethod();
            }
        }
    }

screenshot-20200109103728.png

Każdy element listy zawiera pole TextBlock wyświetlające ID, TextBox wyświetlający imię i drugi wyświetlający nazwisko.
Ostatnim elementem jest przycisk, który po naciśnięciu powinien usuwać dany element z listy.

Usuwanie próbowałem robić według poniższego tutorialu:
https://www.tutorialspoint.com/mvvm/mvvm_view_viewmodel_communication.htm

Oczywiście jeśli zrobię jeden przycisk, który usuwa zaznaczony element z listy to wszystko działa, jednak ja chciałbym zrobić to w taki sposób, aby każdy element miał swój przycisk do usuwania.

Jeśli dobrze to rozumiem, to przycisk w liście nie działa, bo próbuje odwołać się do metody obiektu Employee z listy Employees, którego tam nie ma, da się to zrobić w taki sposób, aby odwoływać się do innego pliku, w którym jest klasa z tą metodą?

Prosiłbym o nakierowanie czego szukać, aby zrobić to w sposób jaki opisałem.

Pozdrawiam

2

Możesz to zrobić przekazujac, metode delete to pojedyczych elementów, lub mieć jeden guzik, usun zaznaczone na dole formy. Lub propagować informacje o zmianie do modelu, a potem model, uaktualni viewModel.
mvvm

Wpf, ma pełno magicznych mechanizmów, i możliwe jest w xaml'u zapisać kontekts w resourcach a potem blindować do tego, ale już jesteś w resoucach wiec nie wiem czy zadziała plus nie mam freezable gotowaca pod reką.

1

Przycisk w który kliknąłeś dziedziczy DataContext po zaznaczonym elemencie w datagridzie, czyli do pojedyńczej komórki danych. Wyciągnij z niego identyfikator lub cokolwiek a potem przeszukaj listę i usuń element

0

Dziękuję wszystkim za porady, ostatecznie zrobiłem to w taki sposób:

Fragment View:

//...
<Button x:Name="btnDeleteEmployee" Tag="{Binding}"
                        Command = "{Binding Path=DataContext.DeleteCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
                        CommandParameter="{Binding Id}"
                        Width="30" Height="30" HorizontalContentAlignment="Center"
                            FontWeight="Bold" Foreground="#FFFFFFFF" Background="#FF990000" 
                            BorderBrush="{x:Null}">
                    <Button.Template>
                        <ControlTemplate TargetType="{x:Type Button}">
                            <Border x:Name="bdrButtonHover" BorderThickness="1" Background="#FF990000" Padding="0 0 0 0">
                                <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" 
                                                      ContentSource="Content" Content="X" />
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter TargetName="bdrButtonHover" Property="Background" Value="#FFFF0000"/>
                                </Trigger>
                                <Trigger Property="IsPressed" Value="True">
                                    <Setter TargetName="bdrButtonHover" Property="Background" Value="#FFCC0000"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
//...

Fragment ViewModel:

//...
        public EmployeeViewModel()
        {
            LoadEmployees();
            DeleteCommand = new Helper.CommandHandler(OnDelete);
        }

//...
        private void OnDelete(Object parameter)
        {
            foreach(Employee employee in Employees)
            {
                if(employee .Id == Convert.ToInt32(parameter))
                {
                    _selectedEmployees = employee ;
                }
            }
            Employees.Remove(_selectedEmployees );
        }
//...

Implementacja ICommand:

    public class CommandHandler : ICommand
    {
        Action<object> _TargetExecuteMethod;
        Func<bool> _TargetCanExecuteMethod;

        public CommandHandler(Action<object> executeMethod)
        {
            _TargetExecuteMethod = executeMethod;
        }

        public CommandHandler(Action<Object> executeMethod, Func<bool> canExecuteMethod)
        {
            _TargetExecuteMethod = executeMethod;
            _TargetCanExecuteMethod = canExecuteMethod;
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }

        bool ICommand.CanExecute(object parameter)
        {

            if (_TargetCanExecuteMethod != null)
            {
                return _TargetCanExecuteMethod();
            }

            if (_TargetExecuteMethod != null)
            {
                return true;
            }

            return false;
        }
		
        public event EventHandler CanExecuteChanged = delegate { };

        void ICommand.Execute(object parameter)
        {
            if (_TargetExecuteMethod != null)
            {
                _TargetExecuteMethod(parameter);
            }
        }
    }

Pozdrawiam.

1 użytkowników online, w tym zalogowanych: 0, gości: 1