使用ICommandSource将命令挂接到CustomDataGridColumn
本文关键字:CustomDataGridColumn 命令 ICommandSource 使用 | 更新日期: 2023-09-27 17:58:57
我对自定义控件很陌生,请原谅,但我正在尝试将命令挂接到DataGridComboBoxColumn
的SelectionChangedEvent
。除了我的命令为空之外,一切都在工作。我有点不明白为什么它仍然是空的。你能告诉我为什么我仍然是空的吗?为什么?
public class CustomComboBoxColumn : DataGridComboBoxColumn, ICommandSource
{
private bool _canExecute;
static CustomComboBoxColumn()
{
}
private EventHandler canExecuteChagnedEventHandler;
#region Dependancy Properties
/// <summary>
/// Allows the combobox to be editable
/// </summary>
public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register(
"IsEditable", typeof(bool), typeof(CustomComboBoxColumn), new PropertyMetadata(default(bool)));
public bool IsEditable
{
get { return (bool)GetValue(IsEditableProperty); }
set { SetValue(IsEditableProperty, value); }
}
/// <summary>
/// Command Property
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(CustomComboBoxColumn),
new PropertyMetadata((ICommand)null, new PropertyChangedCallback(OnCommandChanged)));
[TypeConverter(typeof(CommandConverter))]
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
/// <summary>
/// Command Parameter
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter", typeof(object), typeof(CustomComboBoxColumn), new PropertyMetadata(default(object)));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
/// <summary>
/// Command Target
/// </summary>
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
"CommandTarget", typeof(IInputElement), typeof(CustomComboBoxColumn),
new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
#endregion
#region Properties
private ComboBox _comboBox;
public ComboBox ComboBox
{
get { return _comboBox; }
set
{
if (_comboBox != null)
_comboBox.SelectionChanged -= OnSelectionChanged;
_comboBox = value;
if (_comboBox != null)
_comboBox.SelectionChanged += OnSelectionChanged;
}
}
#endregion
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
var comboBox = editingElement as ComboBox;
if (comboBox != null)
{
ComboBox = comboBox;
comboBox.IsEditable = IsEditable;
}
return base.PrepareCellForEdit(editingElement, editingEventArgs);
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
FireCommand();
}
private void FireCommand()
{
if (Command != null)
{
Command.Execute(CommandParameter);
}
}
/// <summary>
/// Calls OnCommandChanged to hook up new commands
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomComboBoxColumn control = d as CustomComboBoxColumn;
if (control != null)
control.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
}
/// <summary>
/// Calls UnhookCommand and HookCommand
/// </summary>
/// <param name="oldCommand"></param>
/// <param name="newCommand"></param>
protected virtual void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
{
if (oldCommand != null)
UnHookCommand(oldCommand, newCommand);
HookCommand(oldCommand, newCommand);
}
/// <summary>
/// Attaches CanExecuteChanged EventHandler
/// </summary>
/// <param name="oldCommand"></param>
/// <param name="newCommand"></param>
private void HookCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = CanExecuteChagned;
canExecuteChagnedEventHandler = handler;
if (newCommand != null)
newCommand.CanExecuteChanged += canExecuteChagnedEventHandler;
}
/// <summary>
/// Detaches CanExecuteChagned EventHandler
/// </summary>
/// <param name="oldCommand"></param>
/// <param name="newCommand"></param>
private void UnHookCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = CanExecuteChagned;
oldCommand.CanExecuteChanged -= CanExecuteChagned;
}
/// <summary>
/// Sets CanExecute
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CanExecuteChagned(object sender, EventArgs e)
{
if (Command != null)
{
RoutedCommand rc = Command as RoutedCommand;
if (rc != null)
{
_canExecute = rc.CanExecute(CommandParameter, CommandTarget) ? true : false;
}
else
{
_canExecute = Command.CanExecute(CommandParameter) ? true : false;
}
}
}
}
ViewModel:
public ViewModel()
{
ChangeDescriptionCommand = new DelegateCommand(UpdateDescription);
}
public DelegateCommand ChangeDescriptionCommand { get; set; }
//..other methods
xaml:
<icc:CustomComboBoxColumn Header="Item Number"
DisplayMemberPath="Code"
SelectedValuePath="Id"
x:Name="ItemColumn"
SelectedValueBinding="{Binding PartId}"
Command="{Binding ChangeDescriptionCommand}">
<icc:CustomComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.Parts,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
</Style>
</icc:CustomComboBoxColumn.ElementStyle>
<icc:CustomComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.Parts,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
</Style>
</icc:CustomComboBoxColumn.EditingElementStyle>
</icc:CustomComboBoxColumn>
更新因此,我发现由于某种原因,命令没有初始化。
更新2我创建了一个全新的控件,引用了How To Implementation ICommandSource一步一步地进行,它是链接中的滑块控件。我复制粘贴到新的类中,将Slider
更改为DataGridComboBoxColumn
,并且我控制着同样的问题。null命令。
如果您没有使用Interaction.Triggers
,这里是第二个可能的答案。
主窗口.xaml
<Window x:Class="CustomDG.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:local="clr-namespace:CustomDG"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:MyVM x:Key="VMStatic" />
<CollectionViewSource x:Key="ItemsCVS" Source="{Binding ComboItems}" />
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding MyItems, Source={StaticResource VMStatic}}">
<DataGrid.Columns>
<local:CustomComboBoxColumn
Command="{Binding TestCommand, Source={StaticResource VMStatic}}"
Header="Column With Predefined Values"
ItemsSource="{Binding MySubItems, Source={StaticResource VMStatic}}"
SelectedItemBinding="{Binding SelectedSubItem, Source={StaticResource VMStatic}}"
SelectedValuePath="Name" DisplayMemberPath="Name" >
</local:CustomComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
自定义组合框列.xaml
<DataGridComboBoxColumn x:Class="CustomDG.CustomComboBoxColumn"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CustomDG"
mc:Ignorable="d" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}" >
<EventSetter Event="SelectionChanged" Handler="SomeSelectionChanged" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
自定义组合框列.xaml.cs
public partial class CustomComboBoxColumn : DataGridComboBoxColumn, ICommandSource
{
public CustomComboBoxColumn()
{
InitializeComponent();
}
private void SomeSelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaiseCommand();
}
将其应用于您的上下文
根据我的评论,您也应该在命令中添加一个引用与ItemsSource-绑定中一样,因为ComboBoxColumn继承DataContex
这是你的XAML更新,给你一个想法。。。我在上面的一节中显示的是有效的,并且经过了测试,这里它更像是一个试探性的
<icc:CustomComboBoxColumn Header="Item Number"
DisplayMemberPath="Code"
SelectedValuePath="Id"
x:Name="ItemColumn"
SelectedValueBinding="{Binding PartId}"
Command="{Binding ChangeDescriptionCommand,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
<icc:CustomComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.Parts,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
</Style>
代码中的绑定
如果以上操作失败,您可以将命令绑定到主XAML构造函数代码后面(而不是使用主XAML本身)同样,下面的代码经过了测试,它确实有效。
Binding myBinding = new Binding();
myBinding.Source = this.DataContext;
myBinding.Path = new PropertyPath("ChangeDescriptionCommand");
myBinding.Mode = BindingMode.OneWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(ItemColumn, CustomComboBoxColumn.CommandProperty, myBinding);
这个问题的上下文对我来说有点不清楚,但这就是我(在我对请求的理解中)如何使它发挥作用的。
首先,我必须为UI 设计一个基本的示例
<Window x:Class="CustomDG.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:local="clr-namespace:CustomDG"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="ItemsCVS" Source="{Binding ComboItems}" />
<ContentControl x:Key="TestCmdStatic" Content="{Binding TestCmd}" />
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding MyItems}">
<DataGrid.Columns>
<local:CustomComboBoxColumn
Header="Column With Predefined Values"
ItemsSource="{Binding Source={StaticResource ItemsCVS}}"
SelectedValueBinding="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="SubName"
DisplayMemberPath="SubName" >
</local:CustomComboBoxColumn>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding TestCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</Grid>
</Window>
我看到当我更改网格项或组合子项的选择时,TestCmd
背后的逻辑被激发,所以我认为原始问题可能在XAML中,但没有显示。
可能的注意事项:
- 将事件的交互触发器设置为
DataGrid
级别,以及 - 使用静态资源进行
ComboBoxColumn
级别的绑定
这可能是一个愚蠢的问题,但在XAML中,您是否将自定义属性与视图模型的实际命令绑定?
类似于:
<local:CustomComboBoxColumn
Header="My header"
Command="{Binding ChangeDescriptionCommand}">
</local:CustomComboBoxColumn>
然而,与其为命令创建自己的属性,不如使用机器学习等现有的交互机制。