Использование команд в WPF

Рейтинг: 0Ответов: 1Опубликовано: 18.01.2023

Как правильно создать Command, чтобы при нажатии на кнопку, текст улетал в TextBlock из TextBox. Пробовал, но не получалось. Решил понизить сложность и просто вывести MessageBox, но опять ничего

MainWindow.xaml:

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Vertical">
            <TextBox Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}" Height="30" BorderBrush="Blue">
                <TextBox.Resources>
                    <Style TargetType="Border">
                        <Setter Property="CornerRadius" Value="10"/>
                    </Style>
                </TextBox.Resources>
            </TextBox>
            <Border BorderBrush="Black" BorderThickness="1" Width="130" Height="30" CornerRadius="10" Margin="0,10,0,0">
                <TextBlock Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
        </StackPanel>
        <StackPanel Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Vertical">
            <TextBox Text="" Height="30" BorderBrush="Blue">
                <TextBox.Resources>
                    <Style TargetType="Border">
                        <Setter Property="CornerRadius" Value="10"/>
                    </Style>
                </TextBox.Resources>
            </TextBox>
            <Border BorderBrush="Black" BorderThickness="1" Width="130" Height="30" CornerRadius="10" Margin="0,10,0,0">
                <TextBlock Text="" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
            <Button Content="ПЕРЕСЛАТЬ" Width="130" Height="30" Margin="0,10" Command="{Binding command}"/>
        </StackPanel>
    </Grid>

MainWindow.xaml.cs:

public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowVM();
        }

BaseVM.cs:

class BaseVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

MainWindowVM.cs:

class MainWindowVM : BaseVM
    {
        private string _synchronizedText;
        public string SynchronizedText
        {
            get => _synchronizedText;
            set
            {
                _synchronizedText = value;
                OnPropertyChanged(nameof(SynchronizedText));
            }
        }

        public ICommand command { get; set; }

        public MainWindowVM()
        {
            command = new Command();
        }

        private bool canExecuteMethod(object parameter)
        {
            return true;
        }

        private void ExecuteMethod(object parameter)
        {
            MessageBox.Show("OK","OK",MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }

Command.cs:

class Command : ICommand
    {
        public event EventHandler CanExecuteChanged;
        Action<object> executeMethod;
        Func<object, bool> canExecuteMethod;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            executeMethod(parameter);
        }
    }

введите сюда описание изображения введите сюда описание изображения

Ответы

▲ 1Принят

Например вот такая реализация команды:

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

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

    public bool CanExecute(object parameter)
        => _canExecute == null || _canExecute(parameter);

    public void Execute(object parameter)
        => _execute(parameter);
}

Используется так:

public ICommand MyCommand { get; } = new RelayCommand(parameter =>
{
    MessageBox.Show(SynchronizedText);
},
parameter => SynchronizedText?.Length > 0); // CanExecute, заблокирует кнопку, если условие не выполнено
<Button ... Command="{Binding MyCommand}"/>

parameter служит для передачи параметра в команду, например так:

public ICommand MyCommand { get; } = new RelayCommand(parameter =>
{
    string text = (string)parameter;
    MessageBox.Show(text);
},
parameter => parameter is string text && text.Length > 0);
<Button ... Command="{Binding MyCommand}" CommandParameter="{Binding SynchronizedText}"/>

Сам CanExecute в этой реализации команды необязателен, поэтому можно просто вот так:

public ICommand MyCommand { get; } = new RelayCommand(parameter =>
{
    MessageBox.Show(SynchronizedText);
});

Это будет значить, что CanExecute всегда true.


Немного подробней про CanExecute. Представьте что кнопка выполняет примерно такой код, просто для понимания:

var parameter = ...;
if (Command.CanExecute(parameter))
{
    Command.Execute(parameter);
}

То есть если метод CanExecute вернул false, сама команда гарантированно не будет вызвана.

А так же кнопка подписывается на событие Command.CanExecuteChanged и при сработке события проверяет CanExecute и меняет свой IsEnabled в зависимости от полученного оттуда значения. Поэтому если используете команду, управляйте IsEnabled только из кода команды через условие CanExecute.

Если взглянуть в RelayCommand, то можно увидеть, что событие CanExecuteChanged сделано как прокси-событие для CommandManager.RequerySuggested, а это событие вызывается при любом событии ввода - тык мышкой или при вводе с клавиатуры.

Но бывают ситуации, когда надо принудительно вызвать CanExecute, не дожидаясь ввода, чтобы улучшить поведение интерфейса. Для этого просто выполните в коде:

CommandManager.InvalidateRequerySuggested();