I am creating a custom treeview control for my winform application. In doing this, I noticed that there is a really annoying feature of the treeview I am having trouble removing.
Upon selecting a node (Mouse Down), the text of that node is highlighted, I don't want this. However, I also don't to use drawing of any kind. I am looking for a way to do this without having to set the style to owner drawn and manually drawing the text of the nodes being added.
public class CustomTreeView : TreeView
{
// Fields for the custom checkbox images
private Image _checkedImage = Properties.Resources.CheckIcon;
private Image _uncheckedImage = Properties.Resources.UnCheckIcon;
private Image _intermediateImage = Properties.Resources.IntermediateCheckIcon;
private ImageList _imageList;
public CustomTreeView()
{
this.CheckBoxes = false;
_imageList = new ImageList();
_imageList.ImageSize = new Size(26, 26);
_imageList.Images.Add("Unchecked", _uncheckedImage);
_imageList.Images.Add("Checked", _checkedImage);
_imageList.Images.Add("Intermediate", _intermediateImage);
this.ImageList = _imageList;
this.Font = new Font("Gadugi", 12f);
this.ForeColor = Color.DodgerBlue;
this.BackColor = SystemColors.Control;
this.BorderStyle = BorderStyle.None;
this.Scrollable = true;
this.ShowLines = false;
this.ShowRootLines = false;
this.ShowNodeToolTips = true;
this.ShowPlusMinus = false;
this.HideSelection = false;
this.ItemHeight = 30;
}
private int GetEffectiveState(TreeNode node)
{
if (node.Nodes.Count == 0)
{
return node.Checked ? 1 : 0;
}
else
{
bool allChecked = true;
bool anyChecked = false;
foreach (TreeNode child in node.Nodes)
{
int childState = GetEffectiveState(child);
if (childState == 0)
allChecked = false;
else if (childState == 1)
anyChecked = true;
else
{
allChecked = false;
anyChecked = true;
}
}
if (allChecked)
return 1;
else if (anyChecked)
return 2;
else
return 0;
}
}
private void UpdateStateImageIndex(TreeNode node)
{
node.ImageIndex = GetEffectiveState(node);
node.SelectedImageIndex = node.ImageIndex;
}
private void SetNodeChecked(TreeNode node, bool checkedState)
{
node.Checked = checkedState;
foreach (TreeNode child in node.Nodes)
{
SetNodeChecked(child, checkedState);
}
}
private void UpdateStateIndices(TreeNode node)
{
while (node != null)
{
UpdateStateImageIndex(node);
node = node.Parent;
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
TreeViewHitTestInfo hitTestInfo = HitTest(e.Location);
if (hitTestInfo.Location == TreeViewHitTestLocations.Image)
{
TreeNode node = hitTestInfo.Node;
if (node != null)
{
int currentState = GetEffectiveState(node);
if (currentState == 1)
{
SetNodeChecked(node, false);
}
else
{
SetNodeChecked(node, true);
}
UpdateStateIndices(node);
}
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
TreeViewHitTestInfo hitTestInfo = HitTest(e.Location);
if (hitTestInfo.Location == TreeViewHitTestLocations.Image)
{
this.Cursor = Cursors.Hand;
}
else
{
this.Cursor = Cursors.Default;
}
}
protected override void OnAfterCheck(TreeViewEventArgs e)
{
base.OnAfterCheck(e);
UpdateStateIndices(e.Node);
}
private void UpdateStateIndicesRecursive(TreeNode node)
{
node.Expand();
foreach (TreeNode child in node.Nodes)
{
UpdateStateIndicesRecursive(child);
}
UpdateStateImageIndex(node);
}
public void UpdateAllStateIndices()
{
foreach (TreeNode node in this.Nodes)
{
UpdateStateIndicesRecursive(node);
}
}
protected override void OnBeforeCollapse(TreeViewCancelEventArgs e)
{
e.Cancel = true;
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
{
e.Handled = true;
}
base.OnKeyDown(e);
}
protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
{
e.Cancel = true;
base.OnBeforeSelect(e);
}
}
TreeViewHitTestLocations.Image, it prevents it but the functionality of the checkbox is lost (requires 2 clicks or more to get a toggle) Sidenote: This is not a commercial app in the sense of being sold, however it will be distributed among my team at work.