2012-02-03 145 views
2

我想在TreeView中以OwnerDrawText模式自定義DrawNode。即使使用這個處理程序,我發現它非常慢:TreeView自定義DrawNode .NET 3.5 Windows窗體

void RegistryTreeDrawNode(object sender, DrawTreeNodeEventArgs e) 
    { 
     e.DrawDefault = true; 
    } 

我做錯了什麼?

謝謝。

回答

11

我想你可能需要展示更多的代碼,你想要做什麼。不應該有任何明顯不同的繪圖,而不是所有者繪圖;你基本上覆蓋了默認繪製,然後在你發佈的內容中撤消它。這是醜陋的沒有收穫......但不應該是一個perf perf。因此,爲了擺脫缺乏代碼,追求自定義繪製樹的核心願望,讓我告訴你現在沒有很多好的信息。

在過去的幾天裏,我一直在做自己定製的treeview工作,最終可能會寫出所有我學過的教程。在此期間,請隨時查看我的代碼,看看它是否有助於您。

礦是一個簡單的自定義繪製的資源管理器樹視圖。填充樹視圖的代碼與TreeView繪圖代碼是分開的。如果您想運行我的代碼,您可能需要添加您自己的+/-圖像。

工具\ IconReader.cs

using System; 
using System.Runtime.InteropServices; 

namespace TreeViewTestProject.Utilities 
{ 
    public class IconReader 
    { 
     public enum IconSize 
     { 
      Large = 0, 
      Small = 1 
     }; 

     public enum FolderType 
     { 
      Open = 0, 
      Closed = 1 
     }; 

     /// <summary> 
     /// Returns an icon for a given file - indicated by the name parameter. 
     /// </summary> 
     /// <param name="name">Pathname for file.</param> 
     /// <param name="size">Large or small</param> 
     /// <param name="linkOverlay">Whether to include the link icon</param> 
     /// <returns>System.Drawing.Icon</returns> 
     public static System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay) 
     { 
      Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO(); 
      uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES; 

      if(true == linkOverlay) flags |= Shell32.SHGFI_LINKOVERLAY; 

      /* Check the size specified for return. */ 
      if(IconSize.Small == size) 
      { 
       flags |= Shell32.SHGFI_SMALLICON; 
      } 
      else 
      { 
       flags |= Shell32.SHGFI_LARGEICON; 
      } 

      Shell32.SHGetFileInfo(name, 
       Shell32.FILE_ATTRIBUTE_NORMAL, 
       ref shfi, 
       (uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi), 
       flags); 

      // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly 
      System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone(); 
      User32.DestroyIcon(shfi.hIcon);  // Cleanup 
      return icon; 
     } 

     /// <summary> 
     /// Used to access system folder icons. 
     /// </summary> 
     /// <param name="size">Specify large or small icons.</param> 
     /// <param name="folderType">Specify open or closed FolderType.</param> 
     /// <returns>System.Drawing.Icon</returns> 
     public static System.Drawing.Icon GetFolderIcon(string Foldername, IconSize size, FolderType folderType) 
     { 
      // Need to add size check, although errors generated at present! 
      uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES; 

      if(FolderType.Open == folderType) 
      { 
       flags |= Shell32.SHGFI_OPENICON; 
      } 

      if(IconSize.Small == size) 
      { 
       flags |= Shell32.SHGFI_SMALLICON; 
      } 
      else 
      { 
       flags |= Shell32.SHGFI_LARGEICON; 
      } 

      // Get the folder icon 
      Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO(); 
      Shell32.SHGetFileInfo(Foldername, 
       Shell32.FILE_ATTRIBUTE_DIRECTORY, 
       ref shfi, 
       (uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi), 
       flags); 

      System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle 

      // Now clone the icon, so that it can be successfully stored in an ImageList 
      System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone(); 

      User32.DestroyIcon(shfi.hIcon);  // Cleanup 
      return icon; 
     } 
    } 

    public class Shell32 
    { 
     public const int MAX_PATH = 256; 
     [StructLayout(LayoutKind.Sequential)] 
     public struct SHITEMID 
     { 
      public ushort cb; 
      [MarshalAs(UnmanagedType.LPArray)] 
      public byte[] abID; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct ITEMIDLIST 
     { 
      public SHITEMID mkid; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct BROWSEINFO 
     { 
      public IntPtr  hwndOwner; 
      public IntPtr  pidlRoot; 
      public IntPtr  pszDisplayName; 
      [MarshalAs(UnmanagedType.LPTStr)] 
      public string  lpszTitle; 
      public uint   ulFlags; 
      public IntPtr  lpfn; 
      public int   lParam; 
      public IntPtr  iImage; 
     } 

     // Browsing for directory. 
     public const uint BIF_RETURNONLYFSDIRS = 0x0001; 
     public const uint BIF_DONTGOBELOWDOMAIN = 0x0002; 
     public const uint BIF_STATUSTEXT   = 0x0004; 
     public const uint BIF_RETURNFSANCESTORS = 0x0008; 
     public const uint BIF_EDITBOX   = 0x0010; 
     public const uint BIF_VALIDATE   = 0x0020; 
     public const uint BIF_NEWDIALOGSTYLE  = 0x0040; 
     public const uint BIF_USENEWUI   = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX); 
     public const uint BIF_BROWSEINCLUDEURLS = 0x0080; 
     public const uint BIF_BROWSEFORCOMPUTER = 0x1000; 
     public const uint BIF_BROWSEFORPRINTER = 0x2000; 
     public const uint BIF_BROWSEINCLUDEFILES = 0x4000; 
     public const uint BIF_SHAREABLE   = 0x8000; 

     [StructLayout(LayoutKind.Sequential)] 
     public struct SHFILEINFO 
     { 
      public const int NAMESIZE = 80; 
      public IntPtr hIcon; 
      public int  iIcon; 
      public uint dwAttributes; 
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] 
      public string szDisplayName; 
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)] 
      public string szTypeName; 
     }; 

     public const uint SHGFI_ICON    = 0x000000100;  // get icon 
     public const uint SHGFI_DISPLAYNAME   = 0x000000200;  // get display name 
     public const uint SHGFI_TYPENAME   = 0x000000400;  // get type name 
     public const uint SHGFI_ATTRIBUTES   = 0x000000800;  // get attributes 
     public const uint SHGFI_ICONLOCATION  = 0x000001000;  // get icon location 
     public const uint SHGFI_EXETYPE    = 0x000002000;  // return exe type 
     public const uint SHGFI_SYSICONINDEX  = 0x000004000;  // get system icon index 
     public const uint SHGFI_LINKOVERLAY   = 0x000008000;  // put a link overlay on icon 
     public const uint SHGFI_SELECTED   = 0x000010000;  // show icon in selected state 
     public const uint SHGFI_ATTR_SPECIFIED  = 0x000020000;  // get only specified attributes 
     public const uint SHGFI_LARGEICON   = 0x000000000;  // get large icon 
     public const uint SHGFI_SMALLICON   = 0x000000001;  // get small icon 
     public const uint SHGFI_OPENICON   = 0x000000002;  // get open icon 
     public const uint SHGFI_SHELLICONSIZE  = 0x000000004;  // get shell size icon 
     public const uint SHGFI_PIDL    = 0x000000008;  // pszPath is a pidl 
     public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;  // use passed dwFileAttribute 
     public const uint SHGFI_ADDOVERLAYS   = 0x000000020;  // apply the appropriate overlays 
     public const uint SHGFI_OVERLAYINDEX  = 0x000000040;  // Get the index of the overlay 

     public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010; 
     public const uint FILE_ATTRIBUTE_NORMAL  = 0x00000080; 

     [DllImport("Shell32.dll")] 
     public static extern IntPtr SHGetFileInfo(
      string pszPath, 
      uint dwFileAttributes, 
      ref SHFILEINFO psfi, 
      uint cbFileInfo, 
      uint uFlags 
      ); 
    } 

    public class User32 
    { 
     /// <summary> 
     /// Provides access to function required to delete handle. This method is used internally 
     /// and is not required to be called separately. 
     /// </summary> 
     /// <param name="hIcon">Pointer to icon handle.</param> 
     /// <returns>N/A</returns> 
     [DllImport("User32.dll")] 
     public static extern int DestroyIcon(IntPtr hIcon); 
    } 
} 

ExplorerTreeView.cs:

using System; 
using System.IO; 
using System.Windows.Forms; 
using TreeViewTestProject.Utilities; 
using System.Collections; 

namespace TreeViewTestProject 
{ 
    public partial class ExplorerTreeView : TreeViewEx 
    { 
                            #region ExplorerNodeSorter Class 
    private class ExplorerNodeSorter : IComparer 
    { 
     public int Compare(object x, object y) 
     { 
      TreeNode nx = x as TreeNode; 
      TreeNode ny = y as TreeNode; 
      bool nxDir = (nx.ImageKey == kDirectoryImageKey); 
      bool nyDir = (ny.ImageKey == kDirectoryImageKey); 

      if(nxDir && !nyDir) 
      { 
       return -1; 
      } 
      else if(nyDir && !nxDir) 
      { 
       return 1; 
      } 
      else 
      { 
       return string.Compare(nx.Text, ny.Text); 
      } 
     } 
    } 
    #endregion 

     private const string kDirectoryImageKey = "directory"; 
     private const string kReplacementText = "C43C65D1-D40F-46F0-BC5E-57265322DDFC"; 

     public ExplorerTreeView() 
     { 
      InitializeComponent(); 

      this.BeforeExpand  += new TreeViewCancelEventHandler(ExplorerTreeView_BeforeExpand); 
      this.ImageList   = m_FileIcons; 
      this.TreeViewNodeSorter = new ExplorerNodeSorter(); 
      this.LabelEdit   = true; 

      // Create the root of the tree and populate it 
      PopulateTreeView(@"C:\"); 
     } 

     private void PopulateTreeView(string DirectoryName) 
     { 
      this.BeginUpdate(); 

      string rootDir  = DirectoryName; 
      TreeNode rootNode = CreateTreeNode(rootDir); 
      rootNode.Text  = rootDir; 

      this.Nodes.Add(rootNode); 
      PopulateDirectory(rootNode); 

      this.EndUpdate(); 
     } 

     private bool PathIsDirectory(string FullPath) 
     { 
      FileAttributes attr = File.GetAttributes(FullPath); 
      return ((attr & FileAttributes.Directory) == FileAttributes.Directory); 
     } 

     private TreeNode CreateTreeNode(string FullPath) 
     { 
      string key = FullPath.ToLower(); 
      string name = ""; 
      object tag = null; 

      if(PathIsDirectory(key)) 
      { 
       DirectoryInfo info = new DirectoryInfo(FullPath); 
       key  = kDirectoryImageKey; 
       name = info.Name; 
       tag  = info; 
      } 
      else 
      { 
       FileInfo info = new FileInfo(FullPath); 
       name = info.Name; 
       tag  = info; 
      } 

      if(!m_FileIcons.Images.ContainsKey(key)) 
      { 
       if(key == "directory") 
       { 
        m_FileIcons.Images.Add(key, IconReader.GetFolderIcon(Environment.CurrentDirectory, IconReader.IconSize.Small, IconReader.FolderType.Open).ToBitmap()); 
       } 
       else 
       { 
        m_FileIcons.Images.Add(key, IconReader.GetFileIcon(FullPath, IconReader.IconSize.Small, false)); 
       } 
      } 

      TreeNode node   = new TreeNode(name);    
      node.Tag    = tag; 
      node.ImageKey   = key; 
      node.SelectedImageKey = key; 

      return node; 
     } 

     private void PopulateDirectory(TreeNode ParentNode) 
     { 
      DirectoryInfo parentInfo = ParentNode.Tag as DirectoryInfo; 
      foreach(DirectoryInfo subDir in parentInfo.GetDirectories()) 
      { 
       TreeNode child = CreateTreeNode(subDir.FullName); 
       PopulateForExpansion(child); 
       ParentNode.Nodes.Add(child); 
      } 

      foreach(FileInfo file in parentInfo.GetFiles()) 
      { 
       ParentNode.Nodes.Add(CreateTreeNode(file.FullName)); 
      } 
     } 

     private void PopulateForExpansion(TreeNode ParentNode) 
     { 
      // We need the +/- to show up if this directory isn't empty... but only want to populate the node on demand 
      DirectoryInfo parentInfo = ParentNode.Tag as DirectoryInfo; 
      try 
      { 
       if((parentInfo.GetDirectories().Length > 0) || (parentInfo.GetFiles().Length > 0)) 
       { 
        ParentNode.Nodes.Add(kReplacementText); 
       } 
      } 
      catch { } 
     } 

     private void ReplacePlaceholderDirectoryNode(TreeNode ParentNode) 
     { 
      if((ParentNode.Nodes.Count == 1) && (ParentNode.Nodes[0].Text == kReplacementText)) 
      { 
       ParentNode.Nodes.Clear(); 
       PopulateDirectory(ParentNode); 
      } 
     } 

     private void ExplorerTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e) 
     { 
      this.BeginUpdate(); 
      ReplacePlaceholderDirectoryNode(e.Node); 
      this.EndUpdate(); 
     } 
    } 
} 

TreeViewEx.cs:

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace TreeViewTestProject 
{ 
    public partial class TreeViewEx : TreeView 
    { 
     // Notes: TextRenderer uses GDI to render the text, whereas Graphics uses GDI+. "TreeView" has existed for a long long time 
     // and thus uses GDI under the covers. For User Drawing TreeNode's, we need to make sure we use the TextRenderer version 
     // of text rendering functions. 

     #region Properties 

     private DashStyle m_SelectionDashStyle = DashStyle.Dot; 
     public DashStyle SelectionDashStyle 
     { 
      get { return m_SelectionDashStyle; } 
      set { m_SelectionDashStyle = value; } 
     } 

     private DashStyle m_LineStyle = DashStyle.Solid; 
     public DashStyle LineStyle 
     { 
      get { return m_LineStyle; } 
      set { m_LineStyle = value; } 
     } 

     private bool m_ShowLines = true; 
     public new bool ShowLines   // marked as 'new' to replace base functionality fixing ShowLines/FullRowSelect issues in base. 
     { 
      get { return m_ShowLines; } 
      set { m_ShowLines = value; } 
     } 

     protected override CreateParams CreateParams 
     { 
      get 
      { 
       // Removes all the flickering of repainting node's 
       // This is the only thing I found that works properly for doublebuffering a treeview. 
       CreateParams cp = base.CreateParams; 
       cp.ExStyle |= 0x02000000; // WS_CLIPCHILDREN 
       return cp; 
      } 
     } 

     #endregion 

     [DllImport("user32.dll", ExactSpelling = false, CharSet = CharSet.Auto)] 
     private static extern int GetWindowLong(IntPtr hWnd, int nIndex); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] 
     private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, HandleRef lParam); 

     private const int GWL_STYLE   = -16; 
     private const int WS_VSCROLL  = 0x00200000; 
     private const uint TV_FIRST   = 0x1100; 
     private const uint TVM_EDITLABELA = (TV_FIRST + 14); 
     private const uint TVM_EDITLABELW = (TV_FIRST + 65); 

     private bool m_SelectionChanged = false; 
     private bool m_DoubleClicked = false; 
     private bool m_HierarchyChanged = false; 

     public TreeViewEx() 
     { 
      InitializeComponent(); 

      // ShowLines must be "false" for FullRowSelect to work - so we're overriding the variable to correct for that. 
      base.ShowLines = false; 
      this.FullRowSelect = true; 

      this.ItemHeight = 21; 
      this.DrawMode = TreeViewDrawMode.OwnerDrawAll; 
      this.DrawNode += OnDrawNode; 
     } 

     private void OnDrawNode(object sender, DrawTreeNodeEventArgs e) 
     { 
      e.DrawDefault = false; 

      if(e.Node.Bounds.IsEmpty) return; 

      // Clear out the previous contents for the node. If we don't do this, when you mousehover the font will get slightly more bold 
      Rectangle bounds = new Rectangle(0, e.Node.Bounds.Y, this.Width - 1, e.Node.Bounds.Height - 1); 
      e.Graphics.FillRectangle(SystemBrushes.Window, bounds); 

      // Draw everything 
      DrawNodeFocusedHighlight(e); 
      DrawNodeLines(e); 
      DrawPlusMinus(e); 
      DrawNodeIcon(e); 
      DrawNodeText(e); 
     } 

     private void DrawNodeFocusedHighlight(DrawTreeNodeEventArgs e) 
     { 
      if(SelectedNode != e.Node) return; 

      int scrollWidth = 0; 

      if(VScrollVisible()) 
      { 
       scrollWidth = SystemInformation.VerticalScrollBarWidth; 
      } 

      Rectangle bounds = new Rectangle(0, e.Node.Bounds.Y, this.Width - scrollWidth, e.Node.Bounds.Height - 1); 

      if(!e.Node.IsEditing) 
      { 
       e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds); 
      } 

      using(Pen focusPen = new Pen(Color.Black)) 
      { 
       focusPen.DashStyle = SelectionDashStyle; 

       bounds = new Rectangle(0, e.Node.Bounds.Y, this.Width - scrollWidth - 5, e.Node.Bounds.Height - 2); 
       e.Graphics.DrawRectangle(focusPen, bounds); 
      } 
     } 

     private void DrawNodeText(DrawTreeNodeEventArgs e) 
     { 
      if(e.Node.Bounds.IsEmpty) return; 
      if(e.Node.IsEditing) return; 


      Rectangle bounds = e.Node.Bounds; 
      using(Font font = e.Node.NodeFont) 
      { 
       bounds.Width = TextRenderer.MeasureText(e.Node.Text, font).Width; 
       bounds.Y -= 1; 
       bounds.X += 1; 
       if(IsRootNode(e.Node)) 
       { 
        bounds = new Rectangle(this.Margin.Size.Width + Properties.Resources.minus.Width + 9, 0, bounds.Width, bounds.Height); 
       } 

       Color fontColor = SystemColors.InactiveCaptionText; 
       if(this.Focused) 
       { 
        fontColor = e.Node.IsSelected?SystemColors.HighlightText:this.ForeColor; 
       } 

       TextRenderer.DrawText(e.Graphics, e.Node.Text, font, bounds, fontColor); 
      } 
     } 

     private bool IsRootNode(TreeNode Node) 
     { 
      return (Node.Level == 0 && Node.PrevNode == null); 
     } 

     private void DrawNodeLines(DrawTreeNodeEventArgs e) 
     { 
      DrawNodeLineVertical(e); 
      DrawNodeLineHorizontal(e); 
     } 

     private void DrawNodeLineVertical(DrawTreeNodeEventArgs e) 
     { 
      if(IsRootNode(e.Node)) return; 
      if(!ShowLines) return; 

      Pen linePen  = new Pen(Color.Black); 
      linePen.DashStyle = LineStyle; 

      for(int x = 0; x < e.Node.Level; x++) 
      { 
       int xLoc = this.Indent + (x * this.Indent) + (Properties.Resources.minus.Width/2); 
       int height = e.Bounds.Height; 

       if(ShouldDrawVerticalLineForLevel(e.Node, x)) 
       { 
        e.Graphics.DrawLine(linePen, xLoc, e.Bounds.Top, xLoc, e.Bounds.Top + height); 
       } 
      } 

      // Draw the half line for the last node 
      if(e.Node.Parent.LastNode == e.Node) 
      { 
       int halfLoc = (e.Node.Level * this.Indent) + (Properties.Resources.minus.Width/2); 
       e.Graphics.DrawLine(linePen, halfLoc, e.Bounds.Top, halfLoc, (e.Bounds.Top + e.Bounds.Height/2) - 1); 
      } 
     } 

     private bool ShouldDrawVerticalLineForLevel(TreeNode Current, int Level) 
     { 
      TreeNode node = Current; 
      TreeNode c = Current; 
      while(node.Level != Level) 
      { 
       c = node; 
       node = node.Parent; 
      } 
      return !(node.LastNode == c); 
     } 

     private void DrawNodeLineHorizontal(DrawTreeNodeEventArgs e) 
     { 
      if(IsRootNode(e.Node)) return; 
      if(!ShowLines) return; 

      Pen linePen   = new Pen(Color.Black); 
      int xLoc   = (e.Node.Level * this.Indent) + (Properties.Resources.minus.Width/2); 
      int midY   = (e.Bounds.Top + e.Bounds.Bottom)/2 - 1; 
      e.Graphics.DrawLine(linePen, xLoc, midY, xLoc + 7, midY); 
     } 

     private void DrawNodeIcon(DrawTreeNodeEventArgs e) 
     { 
      if(this.ImageList == null) return; 

      int indent = (e.Node.Level * this.Indent) + this.Margin.Size.Width; 
      int iconLeft = indent + this.Indent; 

      int imgIndex = this.ImageList.Images.IndexOfKey(e.Node.ImageKey); 

      if(!IsRootNode(e.Node)) 
      { 
       if(imgIndex >= 0) 
       { 
        Image img = this.ImageList.Images[imgIndex]; 

        int y = (e.Bounds.Y + e.Bounds.Height/2) - (img.Height/2) - 1; 
        e.Graphics.DrawImage(img, new Rectangle(iconLeft, y, img.Width, img.Height), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel); 
       } 
      } 
     } 

     private void DrawPlusMinus(DrawTreeNodeEventArgs e) 
     { 
      if(e.Node.Nodes.Count == 0) return; 

      int indent = (e.Node.Level * this.Indent) + this.Margin.Size.Width; 
      int iconLeft = indent + this.Indent; 

      Image img = Properties.Resources.plus; 
      if(e.Node.IsExpanded) img = Properties.Resources.minus; 

      e.Graphics.DrawImage(img, iconLeft - img.Width - 2, (e.Bounds.Y + e.Bounds.Height/2) - (img.Height/2) - 1); 
     } 

     private bool VScrollVisible() 
     { 
      int style = GetWindowLong(this.Handle, GWL_STYLE); 
      return ((style & WS_VSCROLL) != 0); 
     } 

     private void BeginEditNode() 
     { 
      if(this.SelectedNode == null) return; 
      if(!this.LabelEdit) throw new Exception("This TreeView is not configured with LabelEdit=true"); 

      IntPtr result = SendMessage(new HandleRef(this, this.Handle), TVM_EDITLABELA, IntPtr.Zero, new HandleRef(this.SelectedNode, this.SelectedNode.Handle)); 
      if(result == IntPtr.Zero) 
      { 
       throw new Exception("Failed to send EDITLABEL message to TreeView control."); 
      } 
     } 

     private void TreeViewEx_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e) 
     { 
      if(m_DoubleClicked) 
      { 
       m_DoubleClicked = false; 
       return; 
      } 

      if(m_SelectionChanged) 
      { 
       e.CancelEdit = true; 
       m_SelectionChanged = false; 
      } 
     } 

     private void TreeViewEx_MouseDoubleClick(object sender, MouseEventArgs e) 
     { 
      if(m_HierarchyChanged) 
      { 
       m_HierarchyChanged = false; 
       return; 
      } 

      if((e.Button & MouseButtons.Left) > 0) 
      { 
       if(this.LabelEdit && (this.SelectedNode != null)) 
       { 
        m_DoubleClicked = true; 
        BeginInvoke(new MethodInvoker(delegate() { this.SelectedNode.BeginEdit(); })); 
       } 
      } 
     } 

     private void TreeViewEx_AfterCollapse(object sender, TreeViewEventArgs e) 
     { 
      m_HierarchyChanged = true; 
     } 

     private void TreeViewEx_AfterExpand(object sender, TreeViewEventArgs e) 
     { 
      m_HierarchyChanged = true; 
     } 
    } 
} 
+0

哦,親愛的上帝!感謝你,這兩行幫助了很多:if(e.Node.Bounds.IsEmpty) return; – 2014-11-11 15:12:02