TreeViewItem扩展时调用命令

Gig*_*igi 6 c# wpf treeview mvvm treeviewitem

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

好吧,我发现它毕竟不是那么简单.我环顾四周,尝试了一些事情.例如,使用MVVM Light的EventToCommand:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="TreeViewItem.Expanded">
        <cmd:EventToCommand Command="{Binding Path=FolderNodeToggledCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>
Run Code Online (Sandbox Code Playgroud)

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

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

那么当TreeViewItem扩展时,如何在ViewModel中调用命令呢?

Gay*_*Fow 7

为了实现这一点,您可以使用附加行为,并且您将看到它是一个干净的MVVM策略.

创建一个WPF应用程序并添加此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>
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)

我使用Relay Command,但您可以交替使用Delegate Command.Relay Command的源代码位于http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

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

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
}
Run Code Online (Sandbox Code Playgroud)

然后将此类的名称空间导入到Xaml中......

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

Resharper将为您执行此操作,或者为您提供智能提示.

最后连接View Model.使用像这样的快速和脏的方法......

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
Run Code Online (Sandbox Code Playgroud)

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

此解决方案的关键方面是STYLE中指定的行为!因此它适用于每个TreeViewItem.没有任何代码(除了行为).

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