如何从 DataTemplate 引发命令并使用命令参数?

How to raise a command from a DataTemplate and using command parameter?

我有多个 DataTemplates,里面有一个 TextBox,它们是用 DataTemplateSelector 选择的。 TextBoxes 表示树中的节点。当我在运行时单击 TextBox 时,应该触发 ZoomPlusButtonCommand 命令。这样可行。但我想知道,我的 ObservableCollection 中的哪个 TextBox 触发了这个命令。每个节点都有一个 id,所以我希望这个 id 作为命令参数。我该怎么做?

XAML:

<DataTemplate x:Key="RootNode" DataType="{x:Type vm:TreeNodeViewModel}">
    <TextBox Tag="{Binding NodeTag}" Text="{Binding Node.Description}" Margin="{Binding NodeMargin}">
        <TextBox.Style>
            <Style>
                <Setter Property="TextBox.Background" Value="#FFF6212D"/>
                ...
                <Style.Triggers>
                    <Trigger Property="TextBox.IsMouseOver" Value="True">
                        <Setter Property="TextBox.BorderBrush" Value="AliceBlue"/>
                    </Trigger>
                    ...
                </Style.Triggers>
            </Style>
        </TextBox.Style>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="PreviewMouseDown">
                <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Path=DataContext.ZoomPlusButtonCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
</DataTemplate>

MainViewModel:

public class MainViewModel : ViewModelBase
    {
        // list of all existing nodes in the tree editor
        public ObservableCollection<TreeNodeViewModel> Nodes { get; set; }

        // commands
        private ZoomPlusButtonCommand _zoomPlusButtonCommand;
        public ZoomPlusButtonCommand ZoomPlusButtonCommand
        {
            get
            {
                return _zoomPlusButtonCommand;
            }
            set
            {
                _zoomPlusButtonCommand = value;
            }
        }

        public MainViewModel()
        {
            Nodes = new ObservableCollection<TreeNodeViewModel>();
            Nodes.Add(new TreeNodeViewModel(4,0,"TEST1",new Thickness(0, 100, 0, 0)));
            Nodes.Add(new TreeNodeViewModel(56, 1, "TEST2", new Thickness(100, 150, 0, 0)));
            Nodes.Add(new TreeNodeViewModel(56, 2, "TEST3", new Thickness(200, 200, 0, 0)));

            // initialize commands
            _zoomPlusButtonCommand = new ZoomPlusButtonCommand(this);
        }
    }

文本框(节点)的ViewModel:

public class TreeNodeViewModel : ViewModelBase
{
    private TreeNode _node;
    public TreeNode Node
    {
        get
        {
            return _node;
        }
        set
        {
            _node = value;
            RaisePropertyChanged(nameof(Node));
        }
    }

    private string _nodeTag;
    public string NodeTag
    {
        get
        {
            return _nodeTag;
        }
        set
        {
            _nodeTag = value;
            RaisePropertyChanged(nameof(NodeTag));
        }
    }

    private Thickness _nodeMargin;
    public Thickness NodeMargin
    {
        get
        {
            return _nodeMargin;
        }
        set
        {
            _nodeMargin = value;
            RaisePropertyChanged(nameof(NodeMargin));
        }
    }

    public TreeNodeViewModel(int id, int level, string tag, Thickness margin)
    {
        Node = new TreeNode();
        Node.Id = id;
        Node.Level = level;
        Node.Description = "Knoten";

        NodeTag = tag;
        NodeMargin = margin;
    }
}

命令:

public class ZoomPlusButtonCommand : ICommand
{
    private MainViewModel mainViewModel;

    public ZoomPlusButtonCommand(MainViewModel vm)
    {
        mainViewModel = vm;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        if (mainViewModel.ComboBoxZoomSelectedIndex - 1 >= 0)
        {
            mainViewModel.ComboBoxZoomSelectedIndex -= 1;
        }
    }

    public event EventHandler CanExecuteChanged
    {
        // only implemented to suppress the compiler warning that this event will never be used
        add { }
        remove { }
    }
}

假设您的 ICommand 实现(ZoomPlusButtonCommand class)可以处理命令参数,您可以像这样更改代码:

XAML

<DataTemplate x:Key="RootNode" DataType="{x:Type vm:TreeNodeViewModel}">
    <TextBox Tag="{Binding NodeTag}" Text="{Binding Node.Description}" Margin="{Binding NodeMargin}">
        <TextBox.Style>
            <Style>
                <Setter Property="TextBox.Background" Value="#FFF6212D"/>
                ...
                <Style.Triggers>
                    <Trigger Property="TextBox.IsMouseOver" Value="True">
                        <Setter Property="TextBox.BorderBrush" Value="AliceBlue"/>
                    </Trigger>
                    ...
                </Style.Triggers>
            </Style>
        </TextBox.Style>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="PreviewMouseDown">
                <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Path=DataContext.ZoomPlusButtonCommand}" CommandParameter="{Binding NodeTag}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
</DataTemplate>

MainViewModel

public class MainViewModel : ViewModelBase
{
    // list of all existing nodes in the tree editor
    public ObservableCollection<TreeNodeViewModel> Nodes { get; set; }

    // commands
    private ZoomPlusButtonCommand<string> _zoomPlusButtonCommand;
    public ZoomPlusButtonCommand<string> ZoomPlusButtonCommand
    {
        get
        {
            return _zoomPlusButtonCommand;
        }
        set
        {
            _zoomPlusButtonCommand = value;
        }
    }

    public MainViewModel()
    {
        Nodes = new ObservableCollection<TreeNodeViewModel>();
        Nodes.Add(new TreeNodeViewModel(4,0,"TEST1",new Thickness(0, 100, 0, 0)));
        Nodes.Add(new TreeNodeViewModel(56, 1, "TEST2", new Thickness(100, 150, 0, 0)));
        Nodes.Add(new TreeNodeViewModel(56, 2, "TEST3", new Thickness(200, 200, 0, 0)));

        // initialize commands
        _zoomPlusButtonCommand = new ZoomPlusButtonCommand<string>(this);
    }
}