Как получить доступ к CommandTarget из команды ContextMenu? - PullRequest
0 голосов
/ 07 мая 2018

У меня есть ContextMenu, который предполагает установить значение для своего родителя TextBox.

enter image description here

Текстовое поле не может иметь имя (по требованию), поэтому я устанавливаю его как CommandTarget

    <TextBox Text="{Binding TextBoxOne, UpdateSourceTrigger=LostFocus}">
        <TextBox.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Set to 35"
                          Command="{Binding SetAmountCommand}"
                          CommandParameter="35"
                          CommandTarget="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}}" />
                <MenuItem Header="Set to 50"
                          Command="{Binding SetAmountCommand}"
                          CommandParameter="50"
                          CommandTarget="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}}" />
            </ContextMenu>
        </TextBox.ContextMenu>

Как получить доступ к TextBox.Text внутри Команды?

ViewModel

public class MainVm : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public string TextBoxOne { get; set; } = "One";

    private ICommand _setAmountCommand;
    public ICommand SetAmountCommand
    {
        get
        {
            return _setAmountCommand ?? (_setAmountCommand = new CommandParameterHandler((o) =>
            {
                object param = o;
                double amount = (double)o;
                //MyParentTextBox.Text = amount; //What to put here ? (Cannot be TextBoxOne = amount, need to route from View)
            }, true));
        }
    }
}

Общий CommandParameterHandler

public class CommandParameterHandler : ICommand
{
    private Action<object> _action;
    private bool _canExecute;
    public CommandParameterHandler(Action<object> action, bool canExecute)
    {
        _action = action;
        _canExecute = canExecute;
    }
    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }

    public event EventHandler CanExecuteChanged;

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

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

После 2 дней поиска ответа я наткнулся на этот RoutedCommand учебник. Да, вы можете получить доступ к CommandTarget из Command, но это должен быть статический RoutedCommand. Этот подход соответствует потребности, так как SetAmountCommand используется несколькими MenuItem.

1010 * XAML *

<Window x:Class="WpfCommandTargetDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfCommandTargetDemo">
    <Window.CommandBindings>
        <CommandBinding CanExecute="SetAmountCommand_CanExecute"
                        Command="{x:Static local:CustomRoutedCommand.SetAmountCommand}"
                        Executed="SetAmountCommand_Executed" />
    </Window.CommandBindings>
    <StackPanel>
        <TextBox Text="{Binding TextBoxOne, UpdateSourceTrigger=LostFocus}">
            <TextBox.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Set to 35"
                              Command="{x:Static local:CustomRoutedCommand.SetAmountCommand}"
                              CommandParameter="35"
                              CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget}" />
                    <MenuItem Header="Set to 50"
                              Command="{x:Static local:CustomRoutedCommand.SetAmountCommand}"
                              CommandParameter="50"
                              CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget}" />
                </ContextMenu>
            </TextBox.ContextMenu>
        </TextBox>
    </StackPanel>
</Window>

CodeBehind

public partial class MainWindow : Window
{
    private readonly MainVm _mainVm;

    public MainWindow()
    {
        InitializeComponent();
        _mainVm = new MainVm();
        DataContext = _mainVm;
    }

    void SetAmountCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    void SetAmountCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        object param = e.Parameter; //CommandParameter
        TextBox textbox = e.OriginalSource as TextBox; //CommandTarget
        if (textbox != null)
        {
            textbox.Text = param.ToString();
        }
    }
}

RoutedCommand должно быть static, поскольку оно статически связано с элементом XAML.

public static class CustomRoutedCommand
{
    public static readonly RoutedCommand SetAmountCommand = new RoutedCommand();
}

Для полноты, у меня не может быть Command на моей ViewModel. SetAmountCommand свойство удалено.

public class MainVm : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public string TextBoxOne { get; set; } = "One";
}
0 голосов
/ 08 мая 2018

В команду можно передать только один CommandParameter. Если вы хотите передать что-то в дополнение к фактическому значению, вы можете создать собственный составной тип, который содержит более одного значения:

public class CompositeParameter : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return this;
    }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), 
        typeof(string), typeof(CompositeParameter));

    public string Value
    {
        get { return (string)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ControlProperty = DependencyProperty.Register(nameof(Control),
        typeof(FrameworkElement), typeof(CompositeParameter));
    public FrameworkElement Control
    {
        get { return (FrameworkElement)GetValue(ControlProperty); }
        set { SetValue(ControlProperty, value); }
    }
}

Просмотр модели:

public ICommand SetAmountCommand
{
    get
    {
        return _setAmountCommand ?? (_setAmountCommand = new CommandParameterHandler((o) =>
        {
            CompositeParameter param = o as CompositeParameter;
            if (param != null)
            {
                double amount = Convert.ToDouble(param.Value);
                //...
                TextBox textBox = param.Control as TextBox;
                if (textBox != null)
                    textBox.Text = param.Value;
            }
        }, true));
    }
}

Вид:

<TextBox Text="{Binding TextBoxOne, UpdateSourceTrigger=LostFocus}">
    <TextBox.ContextMenu>
        <ContextMenu>
            <ContextMenu.Resources>
                <local:CompositeParameter x:Key="paramA"
                                          Value="35" 
                                          Control="{Binding PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
                <local:CompositeParameter x:Key="paramB"
                                          Value="50" 
                                          Control="{Binding PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
            </ContextMenu.Resources>
            <MenuItem Header="Set to 35"
                      Command="{Binding SetAmountCommand}" 
                      CommandParameter="{StaticResource paramA}" />
            <MenuItem Header="Set to 50"
                      Command="{Binding SetAmountCommand}"
                      CommandParameter="{StaticResource paramB}" />
        </ContextMenu>
    </TextBox.ContextMenu>
</TextBox>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...