zoukankan      html  css  js  c++  java
  • Silverlight TreeView MVVM 绑定实现

         在WPF/Silverlight开发中,我们都推荐使用MVVM模式进行开发,便于业务与UI的分离和单元测试。但在Silverlight中对TreeView的处理涉及到对TreeViewItem的相关操作如果用MVVM来实现的话,还是不是那么容易的。因为在微软提供的TreeView控件中并没有包含可以直接对TreeViewItem操作的Attach事件。而且在采用数据绑定的方式下每个TreeViewItem是在根据数据模板的层级关系来自动生成的,因此我们要获取每个TreeViewItem也不是那么容易的。

      这里模拟一个需求场景:在MVVM模式下根据对TreeView节点的展开或者关闭来实现节点图标的修改(类似资源管理器中的目录结构)。那么如何来实现呢?

      分析需求:根据要求,我们知道需要处理的是每个TreeViewItem的Expanded或者Collapsed事件,但是根据前面的描述我们知道在Silverlight的TreeView中并不能直接对TreeViewItem的展开或者关闭事件进行处理。那么我们如何来才能捕获到这两个事件呢?

      在这里,我们只能够对TreeView控件和TreeViewItem控件进行扩展处理。我们可以自定义两个类,一个Tree,一个TreeItem。它们分别继承只TreeView和TreeViewItem类。在它们中我们重新定义两个路由事件:public new event RoutedEventHandler Expanded;   public new event RoutedEventHandler Collapsed;并且重写它们的GetContainerForItemOverride()方法。TreeView的每个孩子节点就是TreeViewItem以及TreeViewItem的子节点的都能够处理这两个事件。这里有点拗...直接上代码大家就明白了:

    TreeItem
     public class TreeItem : TreeViewItem
        {
            public new event RoutedEventHandler Expanded;
            public new event RoutedEventHandler Collapsed;
    
            protected override DependencyObject GetContainerForItemOverride()
            {
                TreeItem item = new TreeItem();
                item.Expanded += (s, e) => this.RaiseEvent(this.Expanded, s, e);
                item.Collapsed += (s, e) => this.RaiseEvent(this.Collapsed, s, e);
                return item; 
            }
    
            protected override void OnExpanded(RoutedEventArgs e)
            {
                this.RaiseEvent(this.Expanded, this, e);
                base.OnExpanded(e);
            }
    
            protected override void OnCollapsed(RoutedEventArgs e)
            {
                this.RaiseEvent(this.Collapsed, this, e);
                base.OnCollapsed(e);
            }
    
            private void RaiseEvent(RoutedEventHandler handler, object sender, RoutedEventArgs e)
            {
                if (handler != null)
                {
                    handler.Invoke(sender, e);
                }
            }
             
        }
    Tree
    public class Tree : TreeView
        {
            public event RoutedEventHandler Expanded;
            public event RoutedEventHandler Collapsed;
    
    
            protected override DependencyObject GetContainerForItemOverride()
            {
                TreeItem item = new TreeItem();
                item.Expanded += (s, e) => this.RaiseEvent(this.Expanded, s, e);
                item.Collapsed += (s, e) => this.RaiseEvent(this.Collapsed, s, e); 
                return item;
            }
            void RaiseEvent(RoutedEventHandler handler, object sender, RoutedEventArgs e)
            {
                if (handler != null)
                    handler.Invoke(sender, e);
            }
        }

    这里我们的目的是使每个TreeViewItem在触发Expanded和Collapsed事件的时候能够通知到Tree的Expanded和Collapsed事件。其实这里我们已经实现了能够通过绑定来处理这两个事件了,但是我们仔细研究会发现,处理的时候并不能很好的处理到TreeViewItem对应的数据项。肿么办???

      其实这里我们只需要自定义两个命令行为(继承自 CommandBehaviorBase<T>)就可以达到这个目的了。PS:我使用的是Silverlight的Prism框架,CommandBehaviorBase<T>需要添加Microsoft.Practices.Prism.dll的引用。这两个命令行为分别处理Tree对应的Expanded事件以及Collapsed事件,在这两个事件中将TreeViewItem的DataContext作为参数传递到后台处理。这里提供Expanded行为的实现,Collapsed行为的实现类似,不再重复。

    TreeExpandedBehavior
     public class TreeExpandedBehavior : CommandBehaviorBase<Tree>
        {
            public TreeExpandedBehavior(Tree targetObject)
                : base(targetObject)
            {
                targetObject.Expanded += new RoutedEventHandler(targetObject_Expanded);
            }
    
            void targetObject_Expanded(object sender, RoutedEventArgs e)
            {
                base.CommandParameter = ((FrameworkElement)sender).DataContext;
                base.ExecuteCommand();
            }
    
        }
    
        public static class TreeExpanded
        {
            public static readonly DependencyProperty TreeExpandedBehaviorProperty =
                DependencyProperty.RegisterAttached("TreeExpandedBehaviorProperty", typeof(TreeExpandedBehavior),
                typeof(TreeExpanded), null);
    
            public static ICommand GetCommand(DependencyObject obj)
            {
                return (ICommand)obj.GetValue(CommandProperty);
            }
    
            public static void SetCommand(DependencyObject obj, ICommand value)
            {
                obj.SetValue(CommandProperty, value);
            }
            public static readonly DependencyProperty CommandProperty =
                   DependencyProperty.RegisterAttached("Command", typeof(ICommand),
                   typeof(TreeExpanded), new PropertyMetadata((s, e) =>
                   {
                       var tree = s as Tree;
                       if (tree != null)
                       {
                           var behavior = GetOrCreateBehavior(tree);
                           behavior.Command = e.NewValue as ICommand;
                       }
                   }));
    
            private static TreeExpandedBehavior GetOrCreateBehavior(Tree targetObject)
            {
                var behavior = targetObject.GetValue(TreeExpandedBehaviorProperty) as TreeExpandedBehavior;
                if (behavior == null)
                {
                    behavior = new TreeExpandedBehavior(targetObject);
                    targetObject.SetValue(TreeExpandedBehaviorProperty, behavior);
                }
                return behavior;
            }
        }

    这样我们在ViewModel中就可以通过命令来处理TreeViewItem的Expanded、Collapsed事件了,并且可以获取到TreeViewItem的数据。
    最后实现效果如图:

    最后附上demo的代码,供大家参考。有不对的地方,希望大家多多建言拍砖。

    MVVMTree.zip

    如果您觉得本文对您有所帮助,请点一下"推荐"按钮,您的"推荐"将是我最大的写作动力!
    作者:rpoplar
    出处:http://www.cnblogs.com/rpoplar/
    本文版权归作者【rpoplar】和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究其法律责任的权利。
  • 相关阅读:
    使用环信WebIm实现一个客服功能
    html中的下拉框—select和input方式
    [LeetCode] 206. Reverse Linked List
    visual studio 2019安装配置可编写c/c++语言的IDE环境
    JS判断数据类型是不是undefined
    idea微服务架构出现 Run Dashboard 按钮方法
    docker 常用命令
    配置docker镜像加速
    linux安装docker
    linux安装redis
  • 原文地址:https://www.cnblogs.com/rpoplar/p/2838304.html
Copyright © 2011-2022 走看看