c# - 展开 TreeViewItem 时调用命令

标签 c# wpf mvvm treeview treeviewitem

听起来很简单?我有一个 TreeView,我希望在展开其中一个节点时发生一些事情。我正在使用 MVVM,因此“某物”是 ViewModel 中的命令。

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

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

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

另一个possible solution可能是绑定(bind) TreeViewItem 的 IsExpanded 属性。但是我想保持我绑定(bind)的对象干净DTOs并在 ViewModel 中执行操作,而不是在被绑定(bind)的对象中。

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

最佳答案

要使其正常工作,您可以使用附加行为,您会发现这是一个干净的 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>

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

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
}

我使用中继命令,但您可以交替使用委托(delegate)命令。中继命令的来源是 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
}

然后将这个类的名字空间导入你的Xaml...

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

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

最后连接 View 模型。使用像这样的快速而肮脏的方法...

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

然后,解析完名字空间,连线无误后,就可以开始工作了。将您的调试器锚定在 Execute 方法中,并观察您是否获得了 RoutedEvent 参数。您可以解析它以获取扩展了哪个 TreeView 项目。

此解决方案的关键方面是在 STYLE 中指定的行为!因此它应用于每个 TreeViewItem。两者都没有代码(行为除外)。

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

关于c# - 展开 TreeViewItem 时调用命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23316932/

相关文章:

c# - WPF绑定(bind):SelectedItem和SelectedIndex不起作用

ios - iOS MVVM 中 View Controller 之间的通信

c# - RadComboBox 不会在点击时折叠

c# - 我可以将这三个相似的函数合并为一个函数吗?

c# Upcast 与泛型

c# - Visual Studio 专用访问器和 checkin 代码

c# - WPF如何使用MVVM模式将值绑定(bind)到TextBox correclty

wpf - 以编程方式将 Datagrid 行显示在 WPF、MVVM 中

c# - 间隔为 0,0,1 的 DispatcherTimer 不正好是 1 秒?

.net - DrawingVisual Mouse Up事件