我正在尝试创建一个名为“DataTextBox”的WPF自定义控件。除了此控件的上下文菜单外,一切正常。实际上,我想在DataTextBox的Context菜单中添加一个项目。为此,我在generic.xaml中定义的DataTextBox样式中添加了一个MenuItem:
<Style TargetType="{x:Type controls:DataTextBox}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{DynamicResource Components_TextBoxCut}" Command="ApplicationCommands.Cut" />
<MenuItem Header="{DynamicResource Components_TextBoxCopy}" Command="ApplicationCommands.Copy" />
<MenuItem Header="{DynamicResource Components_TextBoxPaste}" Command="ApplicationCommands.Paste" />
<MenuItem x:Name="menuItemInsertChecksum" Header="{DynamicResource Components_DataTextBoxInsertChecksum}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:DataTextBox}}, Path=CalculateChecksumCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
...
</Setter.Value>
</Setter>
</Style>
我还在DataTextBox代码隐藏
中添加了一个命令 public static DependencyProperty CalculateChecksumCommandProperty = DependencyProperty.Register("CalculateChecksumCommand", typeof(ICommand), typeof(DataTextBox));
public ICommand CalculateChecksumCommand { get; private set; }
此命令在DataTextBox构造函数中初始化:
public DataTextBox() : base()
{
CalculateChecksumCommand = new RelayCommand(() => CalculateChecksum(), () => CanCalculateChecksum());
}
我遇到的问题是我的上一个MenuItem的Command绑定不起作用,因为找不到“CalculateChecksumCommand”。这意味着永远不会调用“CalculateChecksum()”方法。
我很感激有关该主题的任何帮助。谢谢。
编辑:依赖属性声明应该是:
public static DependencyProperty CalculateChecksumCommandProperty = DependencyProperty.Register("CalculateChecksumCommand", typeof(ICommand), typeof(DataTextBox));
public ICommand CalculateChecksumCommand
{
get { return (ICommand)GetValue(CalculateChecksumCommandProperty); }
private set { SetValue(CalculateChecksumCommandProperty, value); }
}
答案 0 :(得分:1)
托管控件并为其定义样式的窗口,该窗口将其上下文菜单的一个菜单项绑定到它的命令:
XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpfApplication2="clr-namespace:WpfApplication2"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style TargetType="wpfApplication2:UserControl1" x:Shared="False">
<Style.Setters>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.(wpfApplication2:UserControl1.MyCommand)}" Header="Hello" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</Grid.Resources>
<wpfApplication2:UserControl1 />
</Grid>
</Window>
代码
using System;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class DelegateCommand : ICommand
{
private readonly Func<object, bool> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute) : this(execute, s => true)
{
}
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
}
}
控制:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
private DelegateCommand _myCommand;
public UserControl1()
{
InitializeComponent();
MyCommand = new DelegateCommand(Execute);
}
public DelegateCommand MyCommand
{
get { return _myCommand; }
set
{
_myCommand = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void Execute(object o)
{
MessageBox.Show("Hello");
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
答案 1 :(得分:1)
根据Aybe和nkoniishvt的评论,我认为我能够回答我自己的问题。
目标:在CustomControl(而不是UserControl)中创建一个命令,并在此CustomControl的xaml部分中使用它 (正如nkoniishvt所说,命令通常用于ViewModel而不是UI组件。但是,我还没有找到任何类似的解决方案。)
CustomControl代码隐藏:
using GalaSoft.MvvmLight.Command;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication1
{
public class CustomControl1 : TextBox
{
public CustomControl1()
: base()
{
MyCommand = new RelayCommand(() => Execute(), () => CanExecute());
}
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public static DependencyProperty MyCommandProperty = DependencyProperty.Register("MyCommand", typeof(ICommand), typeof(CustomControl1));
public ICommand MyCommand
{
get { return (ICommand)GetValue(MyCommandProperty); }
private set { SetValue(MyCommandProperty, value); }
}
private void Execute()
{
//Do stuff
}
private bool CanExecute()
{
return true;
}
}
}
在Themes / Generic.xaml中定义的CustomControl外观:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Cut" Command="ApplicationCommands.Cut" />
<MenuItem Header="Copy" Command="ApplicationCommands.Copy" />
<MenuItem Header="Past" Command="ApplicationCommands.Paste" />
<MenuItem Header="Execute MyCommand in CustomControl1"
Command="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.TemplatedParent.MyCommand}" />
<!--In this case, PlacementTarget is "txtBox"
This is why we have to find the templated parent of the PlacementTarget because MyCommand is defined in the CustomControl1 code-behind-->
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Grid>
<!--Some UI elements-->
<TextBox Name="txtBox" ContextMenu="{TemplateBinding ContextMenu}" />
<!--Others UI elements-->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
在MainWindow.xaml中使用此CustomControl的示例:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomControl1 />
</Grid>
</Window>
不要忘记在App.xaml中添加资源:
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
通过运行应用程序,我们可以看到MyCommand已正确绑定。这意味着当用户单击ContextMenu中的第四个MenuItem时,将调用Execute()方法。
如果你看到需要改进的地方,谢谢你让我知道。 希望它会帮助某人。
答案 2 :(得分:0)
您是否尝试过实施CustomRoutedCommand?
这适用于我的CustomControl:
public static RoutedCommand CustomCommand = new RoutedCommand();
CommandBinding CustomCommandBinding = new CommandBinding(CustomCommand, ExecutedCustomCommand, CanExecuteCustomCommand);
this.CommandBindings.Add(CustomCommandBinding);
customControl.Command = CustomCommand;
KeyGesture kg = new KeyGesture(Key.F, ModifierKeys.Control);
InputBinding ib = new InputBinding(CustomCommand, kg);
this.InputBindings.Add(ib);
private void ExecutedCustomCommand(object sender, ExecutedRoutedEventArgs e)
{
//what to do;
MessageBox.Show("Custom Command Executed");
}
private void CanExecuteCustomCommand(object sender, CanExecuteRoutedEventArgs e)
{
Control target = e.Source as Control;
if (target != null)
{
e.CanExecute = true;
}
else
{
e.CanExecute = false;
}
}
另一个有趣的example