我有一个ContextMenu
,假设在其父TextBox
上设置值。
文本框中没有名称(按要求),因此我将其设置为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
?
视图模型
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));
}
}
}
Generic 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);
}
}
答案 0 :(得分:1)
您只能将一个 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>
答案 1 :(得分:0)
在寻找答案2天后,我遇到了这个RoutedCommand教程。是的,您可以从CommandTarget
访问Command
,但必须是静态RoutedCommand
。这种方法符合需求,因为SetAmountCommand
共享MenuItem
。
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>
代码隐藏
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();
}
为了完整性,我的ViewModel上没有Command
。 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";
}