zoukankan      html  css  js  c++  java
  • WPF如何实现TreeView节点重命名

    背景

      我们经常看到一些软件比如酷狗音乐,在对列表右键进行重命名的时候,当前列表会泛白并且进入可编辑状态,当我们更改完成后就会并进入非编辑状态,这些具体是怎么实现的呢?下面的方法也许会提供一些思路,下面的TreeView节点是通过数据双向绑定的方式,绑定到TextBlock控件和TextBox控件的Text属性上,并且让两者绑定相同的属性,同时使TextBox控件刚好完全覆盖TextBlock控件, 由于TextBlock控件和TextBox控件的区别,TextBlock控件无法实现编辑,所以我在TextBlock控件的上面覆盖了一个TextBox控件,初始状态下我们设置TextBox的Visibility属性为Collapsed当我们点击重命名的时候,我们再设置TextBox的Visibility属性为Visible,这样我们就能够进行节点的重命名,当然当我们命名完成后(该TextBox失去焦点之后)我们再设置TextBox的Visibility属性为Collapsed,这样就完成了重命名的过程,当然我们还有很多重要的工作要做,比如如何获取HierarchicalDataTemplate中的TextBox控件这个是关键,其次TextBlock控件和TextBox控件必须同时绑定到同一属性,这样当属性值发生改变时,就能够更改TextBlock的Text属性值。注意:TextBox的默认绑定方式Mode=TwoWay

    一  不太成熟做法

    前端XAML代码(关键部分)

    <TreeView.ItemTemplate>
           <HierarchicalDataTemplate DataType="{x:Type localex:TreeMode}" ItemsSource="{Binding Children}">
                <CheckBox Tag="{Binding Children}" IsChecked="{Binding IsChecked, Mode=TwoWay}" ToolTip="{Binding ToolTip}">
                <StackPanel Orientation="Horizontal">
                       <Image VerticalAlignment="Center" Source="{Binding Icon}"/>
                       <StackPanel Orientation="Vertical">
                       <TextBlock Text="{Binding Name, Mode=TwoWay}" HorizontalAlignment="Center" Width="Auto"/>
                       <TextBox x:Name="renametextbox" Text="{Binding Name, Mode=TwoWay}" HorizontalAlignment="Center" Margin="0,-20,0,0"
                                 Width="Auto"  Visibility="Collapsed"  LostFocus="renametextbox_LostFous"/>
                       </StackPanel>
                </StackPanel>
                  <CheckBox.ContextMenu>
                      <ContextMenu>
                         <MenuItem  Name="reNameItem" Header="重命名" Click="ReNameTreeViewItem_Click">      
                         </MenuItem>
                      </ContextMenu>
                   </CheckBox.ContextMenu>
                </CheckBox>  
            </HierarchicalDataTemplate>
      </TreeView.ItemTemplate>  

    后端核心代码:

            //下面的部分是在鼠标指针位于此元素(TreeViewItem)上并且按下鼠标右键时发生。
            private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
            {
                 //此处item定义的是一个类的成员变量,是一个TreeViewItem类型
                 item = GetParentObjectEx<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
                 if (item != null)
                  {
                     //使当前节点获得焦点
                     item.Focus();
                     //系统不再处理该操作
                     e.Handled = true;
                  }
            }
    
            //对当前TreeViewItem进行重命名
            private void ReNameTreeViewItem_Click(object sender, RoutedEventArgs e)
            {
                 //获取在TreeView.ItemTemplate中定义的TextBox控件
                 tempTextBox = FindVisualChild<TextBox>(item as DependencyObject);
                //设置该TextBox的Visibility 属性为Visible
                 tempTextBox.Visibility = Visibility.Visible;
            }  

                 下面的这个函数主要是利用VisualTreeHelper.GetParent()方法获取视觉树上面的各种控件,当我们鼠标点击TreeView节点的时候,我们沿着视觉树VisualTree依次向上查找获取

            相应的控件,在本例中依次查找到的控件为:TextBlock-》StackPanel-》StackPanel-》ContentPresenter-》BulletDecorator-》CheckBox-》ContentPresenter-》Boarder-》Grid-》TreeViewItem,通过每一次的向上查找最终找到我们需要的TreeViewItem对象。

     //获取当前TreeView的TreeViewItem
          public TreeViewItem GetParentObjectEx<TreeViewItem>(DependencyObject obj) where TreeViewItem : FrameworkElement
          {
            DependencyObject parent = VisualTreeHelper.GetParent(obj);
            while (parent != null)
             {
                if (parent is TreeViewItem)
                {
                   return (TreeViewItem)parent;
                }
                parent = VisualTreeHelper.GetParent(parent);
            }
             return null;
          } 

            下面的这个函数也是非常重要的,由于我们定义的TextBox控件是在TreeView.ItemTemplate中定义的,所以无法通过this来查找当前的控件,如果无法获取当前的该控件,就无法进行下面的操作,所以这个函数也是非常重要的。和鼠标点击是沿着视觉树向上查找不同,此处我们需要沿着视觉树向下查找,直到找到我们TextBox控件为止,最终返回TextBox控件对象,这个刚好和上面的过程相反,但是这个过程也是非常重要的,具体的使用方式可以参考MSDN上面有更加具体的说明。      

      //获取ItemTemplate内部的各种控件
            private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                    if (child != null && child is childItem)
                        return (childItem)child;
                    else
                    {
                        childItem childOfChild = FindVisualChild<childItem>(child);
                        if (childOfChild != null)
                            return childOfChild;
                    }
                }
                return null;
            }          
    
            //当TextBox失去焦点时发生此事件
            private void renametextbox_LostFous(object sender, RoutedEventArgs e)
            {
                tempTextBox.Visibility = Visibility.Collapsed;
            } 

     二  正确做法

      之前由于对整个TextBox控件不太熟悉进而走了很多弯路,对于这个问题正确的做法是只用一个TextBox控件,并且Model层定义一个属性直接绑定到TextBox的IsReadOnly属性上面,这样处理就不用定义两个控件并通过Visibility来切换这么麻烦了,这样才是正确的做法。

  • 相关阅读:
    Map与对象关系的思考之P1563玩具谜题
    vector性能调优之resize与reserve
    模拟--P1328 生活大爆炸版石头剪刀布 题解
    模拟--P1540 机器翻译
    get、post、put、delete
    Java POI 导出EXCEL经典实现 Java导出Excel弹出下载框(转载)
    Docker(4) 制作镜像
    Git(1) 常用命令
    Linux(1) 常用命令
    Docker(3) 基础知识
  • 原文地址:https://www.cnblogs.com/seekdream/p/4423553.html
Copyright © 2011-2022 走看看