I'm trying to add a few more icons to elements of a standard System.Windows.Forms.TreeView control.
My plan was to only change the label area of the treeview control, but it shows a strange behaviour. If I click a node to select it, when the mouse button is depressed the background is draw correctly with the highlight color. However, the text is the wrong unselected color until I release the mouse button. It's as if e.State
contains the wrong state between when the mouse button is pressed and released.
Here is what I'm doing: I init with this.DrawMode = TreeViewDrawMode.OwnerDrawText
and then register my event handler with this.DrawNode += LayoutTreeView_DrawNode
. Here is the handler:
void LayoutTreeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
Color color = (e.State & TreeNodeStates.Selected) != 0 ?
SystemColors.HighlightText : SystemColors.WindowText;
TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.SingleLine |
TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis;
TextRenderer.DrawText(e.Graphics, e.Node.Text, Font, e.Bounds, color, flags);
}
If I set the handler to its default case...
void LayoutTreeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
e.DefaultDraw = true;
}
...the same thing happens, which is weird since windows is actually drawing it now. This behaviour is in Windows XP with .Net 3.5.
Is there any way to work around this strange behaviour?
Change
Color color = (e.State & TreeNodeStates.Selected) != 0 ?
SystemColors.HighlightText : SystemColors.WindowText;
to
Color color = (e.State & TreeNodeStates.Focused) != 0 ?
SystemColors.HighlightText : SystemColors.WindowText;
This worked on Vista x64 and VS 2008 with .Net 3.5. Let me know if it works for you.
What I observed when watching the default windows behavior was that the text and highlight weren't drawn until the node was selected and had focus. So I checked for the focused condition in order to change the text color. However this doesn't precisely mimic the Widows behavior where the new colors aren't used until the mouse is released. It appears the point when it chooses to draw the blue highlight status changes when in ownerdrawn mode versus windows drawing it... Which admittedly is confusing.
EDIT
However, when you create your own derived treeview you have full control over when everything is drawn.
public class MyTreeView : TreeView
{
bool isLeftMouseDown = false;
bool isRightMouseDown = false;
public MyTreeView()
{
DrawMode = TreeViewDrawMode.OwnerDrawText;
}
protected override void OnMouseDown(MouseEventArgs e)
{
TrackMouseButtons(e);
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
TrackMouseButtons(e);
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
TrackMouseButtons(e);
base.OnMouseMove(e);
}
private void TrackMouseButtons(MouseEventArgs e)
{
isLeftMouseDown = e.Button == MouseButtons.Left;
isRightMouseDown = e.Button == MouseButtons.Right;
}
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
// don't call the base or it will goof up your display!
// capture the selected/focused states
bool isFocused = (e.State & TreeNodeStates.Focused) != 0;
bool isSelected = (e.State & TreeNodeStates.Selected) != 0;
// set up default colors.
Color color = SystemColors.WindowText;
Color backColor = BackColor;
if (isFocused && isRightMouseDown)
{
// right clicking on a
color = SystemColors.HighlightText;
backColor = SystemColors.Highlight;
}
else if (isSelected && !isRightMouseDown)
{
// if the node is selected and we're not right clicking on another node.
color = SystemColors.HighlightText;
backColor = SystemColors.Highlight;
}
using (Brush sb = new SolidBrush(backColor))
e.Graphics.FillRectangle(sb,e.Bounds);
TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.SingleLine |
TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis;
TextRenderer.DrawText(e.Graphics, e.Node.Text, Font, e.Bounds, color, backColor, flags);
}
}