вверху:
public partial class CustomFolderBrowser : Form
{
private TreeView folderTreeView;
private Button okButton;
private Button cancelButton;
private Button makeNewFolderButton;
private BufferedLabel feedbackLabel;
private Image expandIcon;
private Image collapseIcon;
public string SelectedPath { get; private set; }
затем в событии drawode:
private void FolderTreeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
// Get the bounds of the node
Rectangle nodeRect = e.Node.Bounds;
// Clear the icon area before redrawing
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds); // Clear previous drawing
/*--------- 1. Draw expand/collapse icon ---------*/
if (e.Node.Nodes.Count > 0)
{
// Calculate position for expand/collapse icon
Point ptExpand = new Point(nodeRect.Left - 16, nodeRect.Top + (nodeRect.Height - 16) / 2); // Align vertically
// Choose the appropriate icon
Image expandCollapseImg = e.Node.IsExpanded ? collapseIcon : expandIcon;
// Draw the icon (ensure it is redrawn over a clear background)
e.Graphics.DrawImage(expandCollapseImg, ptExpand);
}
/*--------- 2. Draw node text ---------*/
// Get the node's font (default if none is set)
Font nodeFont = e.Node.NodeFont ?? ((TreeView)sender).Font;
// Set the color for the text (highlight if selected)
Brush textBrush = SystemBrushes.WindowText;
if ((e.State & TreeNodeStates.Selected) != 0)
{
textBrush = SystemBrushes.HighlightText;
e.Graphics.FillRectangle(SystemBrushes.Highlight, nodeRect); // Highlight background
}
// Draw the text
e.Graphics.DrawString(e.Node.Text, nodeFont, textBrush, nodeRect);
// Draw focus rectangle if node is selected
if ((e.State & TreeNodeStates.Focused) != 0)
{
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds);
}
}
этот артефакт возникает во многих узлах. я просто показываю несколько примеров.

и здесь я отметил артефакты справа красными кружками

вот полный код. немного длинноват, поэтому я положил его внизу.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace CustomFolder
{
public partial class CustomFolderBrowser : Form
{
private TreeView folderTreeView;
private Button okButton;
private Button cancelButton;
private Button makeNewFolderButton;
private BufferedLabel feedbackLabel;
private Image expandIcon;
private Image collapseIcon;
public string SelectedPath { get; private set; }
public CustomFolderBrowser()
{
InitializeComponents();
okButton.Enabled = false;
this.StartPosition = FormStartPosition.CenterScreen;
// Load the icons from resources (make sure these are 16x16 size)
expandIcon = Properties.Resources.icons8_forward_8; // Right-facing arrow
collapseIcon = Properties.Resources.icons8_expand_arrow_8; // Down-facing arrow
// Enable owner drawing for the TreeView
folderTreeView.DrawMode = TreeViewDrawMode.OwnerDrawAll;
folderTreeView.DrawNode += FolderTreeView_DrawNode;
this.Resize += CustomFolderBrowser_Resize;
this.Move += CustomFolderBrowser_Move;
LoadDrives();
}
private void CustomFolderBrowser_Move(object sender, EventArgs e)
{
UpdateFeedbackLabel();
}
private void CustomFolderBrowser_Resize(object sender, EventArgs e)
{
UpdateFeedbackLabel();
}
private void UpdateFeedbackLabel()
{
// Code to update move and resize feedback, if necessary
}
private void InitializeComponents()
{
this.Text = "Select Folder";
this.Size = new Size(332, 349);
folderTreeView = new DoubleBufferedTreeView
{
Size = new Size(290, 208),
Location = new Point(12, 55)
};
folderTreeView.AfterSelect += FolderTreeView_AfterSelect;
folderTreeView.BeforeExpand += FolderTreeView_BeforeExpand;
okButton = new Button
{
Text = "Ok",
Size = new Size(75, 23),
Location = new Point(147, 280)
};
okButton.Click += OkButton_Click;
cancelButton = new Button
{
Text = "Cancel",
Size = new Size(75, 23),
Location = new Point(227, 280)
};
cancelButton.Click += (s, e) => this.Close();
makeNewFolderButton = new Button
{
Text = "Make New Folder",
Size = new Size(120, 23),
Location = new Point(12, 280)
};
feedbackLabel = new BufferedLabel
{
Text = string.Empty,
ForeColor = Color.Red,
Size = new Size(300, 20),
Location = new Point(12, 25)
};
Controls.Add(folderTreeView);
Controls.Add(feedbackLabel);
Controls.Add(okButton);
Controls.Add(cancelButton);
Controls.Add(makeNewFolderButton);
}
private void LoadDrives()
{
foreach (var drive in DriveInfo.GetDrives())
{
TreeNode node = new TreeNode(drive.Name) { Tag = drive.RootDirectory.FullName };
folderTreeView.Nodes.Add(node);
node.Nodes.Add("Loading..."); // Placeholder for expandable drives
}
feedbackLabel.Select();
}
private void FolderTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
TreeNode node = e.Node;
if (node.Nodes[0].Text == "Loading...")
{
node.Nodes.Clear();
LoadSubDirectories(node);
}
}
private void LoadSubDirectories(TreeNode node)
{
string path = (string)node.Tag;
try
{
DirectoryInfo directoryInfo = new DirectoryInfo(path);
foreach (var directory in directoryInfo.GetDirectories())
{
TreeNode subNode = new TreeNode(directory.Name) { Tag = directory.FullName };
node.Nodes.Add(subNode);
subNode.Nodes.Add("Loading...");
okButton.Enabled = true;
}
}
catch (UnauthorizedAccessException)
{
feedbackLabel.Text = "Access Denied to folder.";
okButton.Enabled = false;
}
}
private void FolderTreeView_AfterSelect(object sender, TreeViewEventArgs e)
{
string selectedPath = (string)e.Node.Tag;
DirectoryInfo directoryInfo = new DirectoryInfo(selectedPath);
try
{
var subDirs = directoryInfo.GetDirectories();
feedbackLabel.Text = string.Empty;
SelectedPath = selectedPath;
okButton.Enabled = true;
}
catch (UnauthorizedAccessException)
{
feedbackLabel.Text = "Folder Access Denied.";
okButton.Enabled = false;
}
}
private void FolderTreeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
// Get the bounds of the node
Rectangle nodeRect = e.Node.Bounds;
// Clear the icon area before redrawing
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds); // Clear previous drawing
/*--------- 1. Draw expand/collapse icon ---------*/
if (e.Node.Nodes.Count > 0)
{
// Calculate position for expand/collapse icon
Point ptExpand = new Point(nodeRect.Left - 16, nodeRect.Top + (nodeRect.Height - 16) / 2); // Align vertically
// Choose the appropriate icon
Image expandCollapseImg = e.Node.IsExpanded ? collapseIcon : expandIcon;
// Draw the icon (ensure it is redrawn over a clear background)
e.Graphics.DrawImage(expandCollapseImg, ptExpand);
}
/*--------- 2. Draw node text ---------*/
// Get the node's font (default if none is set)
Font nodeFont = e.Node.NodeFont ?? ((TreeView)sender).Font;
// Set the color for the text (highlight if selected)
Brush textBrush = SystemBrushes.WindowText;
if ((e.State & TreeNodeStates.Selected) != 0)
{
textBrush = SystemBrushes.HighlightText;
e.Graphics.FillRectangle(SystemBrushes.Highlight, nodeRect); // Highlight background
}
// Draw the text
e.Graphics.DrawString(e.Node.Text, nodeFont, textBrush, nodeRect);
// Draw focus rectangle if node is selected
if ((e.State & TreeNodeStates.Focused) != 0)
{
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds);
}
}
private void OkButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(SelectedPath))
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
private void UpdateTreeView()
{
folderTreeView.BeginUpdate();
try
{
// Perform updates to the TreeView here
}
finally
{
folderTreeView.EndUpdate();
}
}
public class BufferedLabel : Label
{
public BufferedLabel()
{
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
}
public class DoubleBufferedTreeView : TreeView
{
public DoubleBufferedTreeView()
{
// Enable double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();
}
protected override void WndProc(ref Message m)
{
// Suppress background erase to reduce flickering
if (m.Msg == 0x0014) // WM_ERASEBKGND
return;
base.WndProc(ref m);
}
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... cts-betwee