使用ICommandSource将命令挂接到CustomDataGridColumn

本文关键字:CustomDataGridColumn 命令 ICommandSource 使用 | 更新日期: 2023-09-27 17:58:57

我对自定义控件很陌生,请原谅,但我正在尝试将命令挂接到DataGridComboBoxColumnSelectionChangedEvent。除了我的命令为空之外,一切都在工作。我有点不明白为什么它仍然是空的。你能告诉我为什么我仍然是空的吗?为什么?

   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命令。

使用ICommandSource将命令挂接到CustomDataGridColumn

如果您没有使用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>

然而,与其为命令创建自己的属性,不如使用机器学习等现有的交互机制。

相关文章:
  • 没有找到相关文章