views:

407

answers:

1

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?

+1  A: 

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);
    }
}
Jason D
Also, capturing mouse buttons with mouseup and down doesn't work. This might be XP specific but the control is already processing selection and redrawing BEFORE the mosue events are raised in the control.
Coincoin
I tried something similar and it works fine.... until you scroll and drag and drop. I resigned myself to drawing the whole item myself and now at least the text is synchronised with the back ground color even though the behaviour is a little strange. I will accept your answer as it is, I guess, the nearest we can get to the original XP behaviour without glitch.
Coincoin
OOf. I guess I didn't bother with scroll and drag and drop. I've done the whole draw it yourself bit. It's a lot of code, but worthwhile if needed. (I believe my code was ~1000 LOC as I needed to draw check boxes, plus minus and the lines... )
Jason D