展开 TreeViewItem 时调用命令 [英] Invoke Command when TreeViewItem is Expanded

查看:23
本文介绍了展开 TreeViewItem 时调用命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

听起来够简单吗?我有一个 TreeView,我希望在扩展其中一个节点时发生一些事情.我正在使用 MVVM,因此某物"是 ViewModel 中的一个命令.

Sounds simple enough? I have a TreeView, and I want something to happen when one of the nodes is expanded. I'm using MVVM, so that 'something' is a command in the ViewModel.

嗯,我发现这毕竟不是那么简单.我环顾四周并尝试了一些方法.例如,使用 MVVM Light 的 EventToCommand:

Well, I'm finding that it's not so simple after all. I've looked around and tried a few things. For example, using MVVM Light's EventToCommand:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="TreeViewItem.Expanded">
        <cmd:EventToCommand Command="{Binding Path=FolderNodeToggledCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

此代码(基于 thisthis) 不起作用(没有触发;该命令绑定在 ViewModel 中,但在展开节点时永远不会触发相应的方法).我也试过用 i:InvokeCommandAction 替换 cmd:EventToCommand 并且结果是一样的.第二个链接中的解决方案"显然有点矫枉过正,我不想更改 ToggleButton,因为我想使用 WPF TreeView WinForms Style 有自己的 ToggleButton.第二个链接中的次要答案表明我可能试图在 TreeView 上使用不存在的事件.

This code (based on this and this) doesn't work (nothing fires; the command is bound in the ViewModel but the corresponding method is never fired when a node is expanded). I've also tried replacing cmd:EventToCommand with i:InvokeCommandAction and the results are the same. The 'solution' in the second link is clearly overkill and I don't want to change the ToggleButton since I want to use the WPF TreeView WinForms Style which has its own ToggleButton. The secondary answer in the second link suggests that I might be attempting to use an event on TreeView that doesn't exist.

另一个可能的解决方案可能是绑定 TreeViewItem 的 IsExpanded 属性.但是我想保持我绑定的对象干净 DTOs 并执行ViewModel 中的操作,而不是被绑定的对象.

Another possible solution could be to bind the TreeViewItem's IsExpanded property. However I'd like to keep the objects I'm binding to as clean DTOs and perform an action in the ViewModel, not in the objects being bound.

那么当 TreeViewItem 展开时,在 ViewModel 中调用命令需要什么?

So what will it take to invoke a command in the ViewModel when a TreeViewItem is expanded?

推荐答案

要使其正常工作,您可以使用附加行为,您将看到这是一个干净的 MVVM 策略.

To get this working, you can use an attached behaviour, and you'll see that it's a clean MVVM strategy.

创建一个 WPF 应用程序并添加此 Xaml...

Create a WPF app and add this Xaml...

<Grid>
    <TreeView>
        <TreeView.Resources>
            <Style TargetType="TreeViewItem">
                <Setter Property="bindTreeViewExpand:Behaviours.ExpandingBehaviour" Value="{Binding ExpandingCommand}"/>
            </Style>
        </TreeView.Resources>
        <TreeViewItem Header="this" >
            <TreeViewItem Header="1"/>
            <TreeViewItem Header="2"><TreeViewItem Header="Nested"></TreeViewItem></TreeViewItem>
            <TreeViewItem Header="2"/>
            <TreeViewItem Header="2"/>
            <TreeViewItem Header="2"/>
        </TreeViewItem>
        <TreeViewItem Header="that" >
            <TreeViewItem Header="1"/>
            <TreeViewItem Header="2"/>
            <TreeViewItem Header="2"/>
            <TreeViewItem Header="2"/>
            <TreeViewItem Header="2"/>
        </TreeViewItem>        
    </TreeView>
</Grid>

然后像这样创建一个视图模型...

Then create a View Model like this...

public class ViewModel : INotifyPropertyChanged
{
    public ICommand ExpandingCommand { get; set; }
    public ViewModel()
    {
        ExpandingCommand = new RelayCommand(ExecuteExpandingCommand, CanExecuteExpandingCommand);
    }
    private void ExecuteExpandingCommand(object obj)
    {
        Console.WriteLine(@"Expanded");
    }
    private bool CanExecuteExpandingCommand(object obj)
    {
        return true;
    }
    #region INotifyPropertyChanged Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}

我使用中继命令,但您可以交替使用委托命令.中继命令的来源位于 http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

I use the Relay Command, but you can use the Delegate Command interchangeably. The source for the Relay Command is at http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

然后创建一个单独的类,看起来像这样...

Then create a separate class that looks like this...

public static class Behaviours
{
    #region ExpandingBehaviour (Attached DependencyProperty)
    public static readonly DependencyProperty ExpandingBehaviourProperty =
        DependencyProperty.RegisterAttached("ExpandingBehaviour", typeof(ICommand), typeof(Behaviours),
            new PropertyMetadata(OnExpandingBehaviourChanged));
    public static void SetExpandingBehaviour(DependencyObject o, ICommand value)
    {
        o.SetValue(ExpandingBehaviourProperty, value);
    }
    public static ICommand GetExpandingBehaviour(DependencyObject o)
    {
        return (ICommand) o.GetValue(ExpandingBehaviourProperty);
    }
    private static void OnExpandingBehaviourChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TreeViewItem tvi = d as TreeViewItem;
        if (tvi != null)
        {
            ICommand ic = e.NewValue as ICommand;
            if (ic != null)
            {
                tvi.Expanded += (s, a) => 
                {
                    if (ic.CanExecute(a))
                    {
                        ic.Execute(a);

                    }
                    a.Handled = true;
                };
            }
        }
    }
    #endregion
}

然后将这个类的命名空间导入到你的Xaml中...

Then import the name space of this class into your Xaml...

xmlns:bindTreeViewExpand="clr-namespace:BindTreeViewExpand"(你的命名空间会有所不同!)

xmlns:bindTreeViewExpand="clr-namespace:BindTreeViewExpand" (your name space will be different!)

Resharper 会为你做这件事,或者给你一个智能提示.

Resharper will do this for you, or give you an intellesense prompt.

最后连接视图模型.使用这种快速而肮脏的方法......

Finally wire up the View Model. Use the quick and dirty method like this...

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }

然后,在解析命名空间并且接线正确后,它就会开始工作.将您的调试器固定在 Execute 方法中,并观察您是否获得了 RoutedEvent 参数.您可以解析它以获取展开的树视图项.

Then, after the name spaces are resolved and the wiring is correct, it will start to work. Anchor your debugger in the Execute method and observe that you get a RoutedEvent argument. You can parse this to get which Tree view item was expanded.

此解决方案的关键方面是在 STYLE 中指定的行为!所以它适用于每一个 TreeViewItem.两者都没有代码(除了行为).

The key aspect in this solution is the behaviour being specified in the STYLE! So it is applied to each and every TreeViewItem. No code behind either (other than the behaviour).

我上面列出的行为将事件标记为已处理.您可能希望根据您所追求的行为来改变它.

The behaviour I listed above marks the event as handled. You may wish to change that depending upon the behaviour you are after.

这篇关于展开 TreeViewItem 时调用命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆