Solved

Adding a blue "(1)" to the end of the text on a treenode

Posted on 2004-08-13
17
350 Views
Last Modified: 2008-01-09
I want to add a blue "(1)" or other value to the end of a treenode's text.  This is done in Outlook when an email arrives whereby the text is black but the ending "(1)" value is blue.  Is this possible?

Thanks for any help,

Hammer
0
Comment
Question by:HAMMER33333
  • 9
  • 5
  • 3
17 Comments
 
LVL 8

Expert Comment

by:wguerram
ID: 11797597
This question should worth more points
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11804982
the .Text field can contain html
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11804986
0
 

Author Comment

by:HAMMER33333
ID: 11809451
Gregory,

        I want to do this on a Client application in VB .Net.  Also, this does not show how to change the color where you would end up with  the text black and the "(1)" in blue.

Thanks,

Hammer
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11810305
whoops was thinking asp.net ... you would have to override the paint method for the node ill see if I can find an example.
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11813961
This code will add a blue (Total node count)

But i still can't figure out how to show the number of children.

I think you to keep track of that i a collection.

I am still finding out how, but probably this can help you.

Just add this code to a class in a windows control library

1. Create a new Windows Control Library
2. Add the class
3. Compile
4. Go to your project form and right click the toolbox
5. Choose customize Box or choose items (something like that, i don't remember in vs .net 2003)
6. select .NET Framework components
7. Click the browse button and search the compile dll of the windows library
8. click OK
it should be in the toolbox

---------------------------------------


Imports System.Runtime.InteropServices
Imports System.ComponentModel

Public Class MyTreeView
    Inherits Windows.Forms.TreeView

    Private Const DT_LEFT As Integer = &H0&

    Private Const WM_NOTIFY As Integer = &H4E
    Private Const OCM_NOTIFY As Integer = &H204E

    'Custom draw messages
    Private Const NM_FIRST As Integer = 0
    Private Const NM_CUSTOMDRAW As Integer = (NM_FIRST - 12)
   
    Private Const CDDS_PREPAINT As Integer = &H1
    Private Const CDDS_ITEMPREPAINT As Integer = (&H10000 Or &H1)
    Private Const CDDS_ITEMPOSTPAINT As Integer = (&H10000 Or &H2)

    'TreeView Messages
    Private Const TV_FIRST As Integer = &H1100
    Private Const TVM_GETITEM As Integer = (TV_FIRST + 12)
    Private Const TVM_GETCOUNT As Integer = &H1105
    Private Const TVM_GETITEMRECT As Integer = (TV_FIRST + 4)

    'Return values
    Private Const CDRF_SKIPDEFAULT As Integer = &H4
    Private Const CDRF_DODEFAULT As Integer = &H0
    Private Const CDRF_NOTIFYITEMDRAW As Integer = &H20
    Private Const CDRF_NOTIFYPOSTPAINT As Integer = &H10            

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure NMHDR
        Dim hwndFrom As Integer
        Dim idfrom As Integer
        Dim code As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure NMCUSTOMDRAW
        Dim hdr As NMHDR
        Dim dwDrawStage As Integer
        Dim hdc As IntPtr
        Dim rc As Rectangle
        Dim dwItemSpec As IntPtr ' this is control specific, but it's how to specify an item.  valid only with CDDS_ITEM bit set
        Dim uItemState As Integer
        Dim lItemlParam As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure NMTVCUSTOMDRAW
        Dim NMCD As NMCUSTOMDRAW
        Dim clrText As Integer
        Dim clrTextBk As Integer
        Dim iLevel As Integer
    End Structure    

    <Description("Send messages to windows"), _
    DllImport("user32.dll", EntryPoint:="SendMessageA", _
    SetLastError:=True, CharSet:=CharSet.Auto, ExactSpelling:=True, _
    CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByRef lParam As IntPtr) As Integer

    End Function
    <Description("Send messages to windows"), _
    DllImport("user32.dll", EntryPoint:="SendMessageA", _
    SetLastError:=True, CharSet:=CharSet.Auto, ExactSpelling:=True, _
    CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SendMessageItemRect(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByRef lParam As Rectangle) As Integer

    End Function

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Select Case m.Msg
            Case OCM_NOTIFY
                Dim NMTVCD As NMTVCUSTOMDRAW

                NMTVCD = Marshal.PtrToStructure(m.LParam, GetType(NMTVCUSTOMDRAW))

                Select Case NMTVCD.NMCD.hdr.code
                    Case NM_CUSTOMDRAW                        
                        Select Case NMTVCD.NMCD.dwDrawStage
                            Case CDDS_PREPAINT
                                ' Tell it we want to be told when an
                                ' item is drawn.
                                m.Result = CDRF_NOTIFYITEMDRAW

                            Case CDDS_ITEMPREPAINT

                                m.Result = CDRF_NOTIFYPOSTPAINT

                            Case CDDS_ITEMPOSTPAINT
                                Dim _Count As Integer
                                Dim ptr As New IntPtr
                                Dim rcItem As New Rectangle
                                Dim g As Graphics = Graphics.FromHdc(NMTVCD.NMCD.hdc)
                                Dim b As New SolidBrush(Color.Blue)

                                rcItem.X = NMTVCD.NMCD.dwItemSpec
                                'If node is visible show blue (count)
                                If SendMessageItemRect(m.HWnd, TVM_GETITEMRECT, 1, rcItem) Then
                                    _Count = SendMessage(m.HWnd, TVM_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
                                    g.DrawString("(" & _Count.ToString & ")", Me.Font, b, rcItem.Width, rcItem.Y)
                                End If

                                g.Dispose()
                                b.Dispose()

                                m.Result = CDRF_SKIPDEFAULT
                        End Select

                End Select
            Case Else
                MyBase.WndProc(m)
        End Select
    End Sub

End Class
0
 
LVL 8

Accepted Solution

by:
wguerram earned 250 total points
ID: 11815972
Here is the code showing the number of subitems in blue like outlook.

It was a hard work to do...

But it is pretty nice.
---------------------------------------------------------------------------------


Imports System.Runtime.InteropServices
Imports System.ComponentModel

Public Class MyTreeView
    Inherits Windows.Forms.TreeView

    Private Const DT_LEFT As Integer = &H0&

    Private Const WM_NOTIFY As Integer = &H4E
    Private Const OCM_NOTIFY As Integer = &H204E

    'Custom draw messages
    Private Const NM_FIRST As Integer = 0
    Private Const NM_CUSTOMDRAW As Integer = (NM_FIRST - 12)
   
    Private Const CDDS_PREPAINT As Integer = &H1
    Private Const CDDS_ITEMPREPAINT As Integer = (&H10000 Or &H1)
    Private Const CDDS_ITEMPOSTPAINT As Integer = (&H10000 Or &H2)

    'TreeView Messages
    Private Const TV_FIRST As Integer = &H1100
    Private Const TVM_GETITEM As Integer = (TV_FIRST + 12)
    Private Const TVM_GETCOUNT As Integer = &H1105
    Private Const TVM_GETITEMRECT As Integer = (TV_FIRST + 4)
    Private Const TVM_GETNEXTITEM As Integer = (TV_FIRST + 10)
    Private Const TVGN_NEXT As Integer = &H1    
    Private Const TVGN_CHILD As Integer = &H4

    'Return values
    Private Const CDRF_SKIPDEFAULT As Integer = &H4
    Private Const CDRF_DODEFAULT As Integer = &H0
    Private Const CDRF_NOTIFYITEMDRAW As Integer = &H20
    Private Const CDRF_NOTIFYPOSTPAINT As Integer = &H10            

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure NMHDR
        Dim hwndFrom As Integer
        Dim idfrom As Integer
        Dim code As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure NMCUSTOMDRAW
        Dim hdr As NMHDR
        Dim dwDrawStage As Integer
        Dim hdc As IntPtr
        Dim rc As Rectangle
        Dim dwItemSpec As IntPtr ' this is control specific, but it's how to specify an item.  valid only with CDDS_ITEM bit set
        Dim uItemState As Integer
        Dim lItemlParam As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure NMTVCUSTOMDRAW
        Dim NMCD As NMCUSTOMDRAW
        Dim clrText As Integer
        Dim clrTextBk As Integer
        Dim iLevel As Integer
    End Structure    

    <Description("Send messages to windows"), _
    DllImport("user32.dll", EntryPoint:="SendMessageA", _
    SetLastError:=True, CharSet:=CharSet.Auto, ExactSpelling:=True, _
    CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    End Function
    <Description("Send messages to windows"), _
    DllImport("user32.dll", EntryPoint:="SendMessageA", _
    SetLastError:=True, CharSet:=CharSet.Auto, ExactSpelling:=True, _
    CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SendMessageItemRect(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByRef lParam As Rectangle) As Integer

    End Function

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Select Case m.Msg
            Case OCM_NOTIFY
                Dim NMTVCD As NMTVCUSTOMDRAW

                NMTVCD = Marshal.PtrToStructure(m.LParam, GetType(NMTVCUSTOMDRAW))

                Select Case NMTVCD.NMCD.hdr.code
                    Case NM_CUSTOMDRAW                        
                        Select Case NMTVCD.NMCD.dwDrawStage
                            Case CDDS_PREPAINT
                                ' Tell it we want to be told when an
                                ' item is drawn.
                                m.Result = CDRF_NOTIFYITEMDRAW

                            Case CDDS_ITEMPREPAINT

                                m.Result = CDRF_NOTIFYPOSTPAINT

                            Case CDDS_ITEMPOSTPAINT
                                Dim _Count As Integer
                                Dim ptr As New IntPtr
                                Dim rcItem As New Rectangle
                                Dim g As Graphics = Graphics.FromHdc(NMTVCD.NMCD.hdc)
                                Dim b As New SolidBrush(Color.Blue)

                                rcItem.X = NMTVCD.NMCD.dwItemSpec
                                'If node is visible show blue (count)
                                If SendMessageItemRect(m.HWnd, TVM_GETITEMRECT, 1, rcItem) Then
                                    _Count = GetChildrenCount(m.HWnd, NMTVCD.NMCD.dwItemSpec, True)
                                    g.DrawString("(" & _Count.ToString & ")", Me.Font, b, rcItem.Width, rcItem.Y)
                                End If

                                g.Dispose()
                                b.Dispose()

                                m.Result = CDRF_SKIPDEFAULT
                        End Select

                End Select
            Case Else
                MyBase.WndProc(m)
        End Select
    End Sub

    Private Function GetChildrenCount(ByVal TreeHandle As IntPtr, ByVal hWndParentNode As Integer, ByVal AllSubItems As Boolean) As Long
        Dim intNode As Integer
        Dim lngRetVal As Long

        intNode = SendMessage(TreeHandle, TVM_GETNEXTITEM, TVGN_CHILD, hWndParentNode)
        Do While intNode
            lngRetVal += 1

            If AllSubItems Then
                lngRetVal = lngRetVal + GetChildrenCount(TreeHandle, intNode, AllSubItems)
            End If

            intNode = SendMessage(TreeHandle, TVM_GETNEXTITEM, TVGN_NEXT, intNode)
        Loop

        Return lngRetVal
    End Function

End Class
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11851310
HAMMER33333,

Could you get this to work?
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:HAMMER33333
ID: 11856774
wguerram,

        I keep getting the error:

            Value of type 'System.IntPtr' cannot be converted to 'Integer'.

       on lines like this:

            m.Result = CDRF_NOTIFYPOSTPAINT


       Did this code ever work for you?  I created a form and a Control Class for the tree.  It is when I compile the tree that I get multiple errors like the one above.

Thanks for the help,

Hammer
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11859068
Sorry, since i am using VS 2005 probably the compiler makes the conversion

Change those lines with this

m.Result = IntPtr.op_Explicit(CDRF_NOTIFYPOSTPAINT)

This will convert the value to intptr.

For me it is working.

If you have any other problem please let me know it.
0
 

Author Comment

by:HAMMER33333
ID: 11860053
wguerram,

        I am really lost.  I did the steps above but I am thinking I need a form and then drag the new treeview onto the form.  After the item is in the toolbox what then?   Do I end up with one new Treeview class and another solution which contains the form where this new class will be placed?

Sorry for the confusion,

Hammer
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11860206
Yes, the steps above are for creating a DLL.

After the item is in the toolbox just drag it into the your form, add nodes and the class will add a blue bracket adding the sub nodes count.

Yes you will end up with another treeview class which should be placed in the control library.

Now create a new project, add a reference to the DLL, this class should appear in the toolbox, just drag the control into the form.

You will have two project, one for the treeview class (windows control library) and another for the form (Your application)

You have problems, just let me know it
0
 

Author Comment

by:HAMMER33333
ID: 11881986
wguerram,

         This works great.  Is there a way I could do this with a normal treeview?  OR do I have to use the class and then change the class such that it will only add the value on specific nodes?   I wanted to have a treeview where I could add the blue "(1)" or other values programatically after the treeview was built.  So while the application is running it will set specific nodes with the value and remove the value from other nodes.

Hope this makes sense,
Thanks for the great treeview,

Hammer
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11882205
I am happy this was useful for you, since it was not easy for me to do that.

Well You can use the standard Treeview, but you to have to use this api

SetWindowLong 'To handle the WindowProc, here you send the addres of the procedure that will handle the WindowProc

The procedure would be

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

I have never use it, probably you can find an example on internet how to do it.

If come up with something i will let you know.
0
 

Author Comment

by:HAMMER33333
ID: 11882319
wguerram,

        You have been more than helpful.  I will close this out and let you get back to your work.   Thank you so much for the help.

Take care,

Hammer
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11882356
You are welcome.
0
 
LVL 8

Expert Comment

by:wguerram
ID: 11886821
Hi, it's me again

Take a look at this:

I added a new event to the treeview class:

AfterDrawNode(ByVal node As TreeNode, ByVal g As Graphics, ByVal rect As Rectangle)

You can use this one to make your own custom drawing.

node-> Reference to the node
g-> use this one to make drawings
rect->the position and size of the node

I delete the node count functions, since you won't need it anymore.

Hope this help you.


Imports System.Runtime.InteropServices
Imports System.ComponentModel

Public Class MyTreeView
    Inherits Windows.Forms.TreeView

    Public Event AfterDrawNode(ByVal node As TreeNode, ByVal g As Graphics, ByVal rect As Rectangle)

    Private Const DT_LEFT As Integer = &H0&

    Private Const OCM_NOTIFY As Integer = &H204E

    'Custom draw messages
    Private Const NM_FIRST As Integer = 0
    Private Const NM_CUSTOMDRAW As Integer = (NM_FIRST - 12)
   
    Private Const CDDS_PREPAINT As Integer = &H1
    Private Const CDDS_ITEMPREPAINT As Integer = (&H10000 Or &H1)
    Private Const CDDS_ITEMPOSTPAINT As Integer = (&H10000 Or &H2)

    'TreeView Messages
    Private Const TV_FIRST As Integer = &H1100    
    Private Const TVM_GETITEMRECT As Integer = (TV_FIRST + 4)    

    'Return values
    Private Const CDRF_SKIPDEFAULT As Integer = &H4
    Private Const CDRF_DODEFAULT As Integer = &H0
    Private Const CDRF_NOTIFYITEMDRAW As Integer = &H20
    Private Const CDRF_NOTIFYPOSTPAINT As Integer = &H10

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure NMHDR
        Dim hwndFrom As Integer
        Dim idfrom As Integer
        Dim code As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure NMCUSTOMDRAW
        Dim hdr As NMHDR
        Dim dwDrawStage As Integer
        Dim hdc As IntPtr
        Dim rc As Rectangle
        Dim dwItemSpec As IntPtr ' this is control specific, but it's how to specify an item.  valid only with CDDS_ITEM bit set
        Dim uItemState As Integer
        Dim lItemlParam As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure NMTVCUSTOMDRAW
        Dim NMCD As NMCUSTOMDRAW
        Dim clrText As Integer
        Dim clrTextBk As Integer
        Dim iLevel As Integer
    End Structure

    <Description("Send messages to windows"), _
    DllImport("user32.dll", EntryPoint:="SendMessageA", _
    SetLastError:=True, CharSet:=CharSet.Auto, ExactSpelling:=True, _
    CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    End Function
    <Description("Send messages to windows"), _
    DllImport("user32.dll", EntryPoint:="SendMessageA", _
    SetLastError:=True, CharSet:=CharSet.Auto, ExactSpelling:=True, _
    CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SendMessageItemRect(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByRef lParam As Rectangle) As Integer

    End Function

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Select Case m.Msg
            Case OCM_NOTIFY
                Dim NMTVCD As NMTVCUSTOMDRAW

                NMTVCD = Marshal.PtrToStructure(m.LParam, GetType(NMTVCUSTOMDRAW))

                Select Case NMTVCD.NMCD.hdr.code
                    Case NM_CUSTOMDRAW
                        Select Case NMTVCD.NMCD.dwDrawStage
                            Case CDDS_PREPAINT
                                ' Tell it we want to be told when an
                                ' item is drawn.
                                m.Result = IntPtr.op_Explicit(CDRF_NOTIFYITEMDRAW)

                            Case CDDS_ITEMPREPAINT

                                m.Result = IntPtr.op_Explicit(CDRF_NOTIFYPOSTPAINT)

                            Case CDDS_ITEMPOSTPAINT
                                Dim ptr As New IntPtr
                                Dim rcItem As New Rectangle
                                Dim g As Graphics = Graphics.FromHdc(NMTVCD.NMCD.hdc)

                                rcItem.X = NMTVCD.NMCD.dwItemSpec                                
                                If SendMessageItemRect(m.HWnd, TVM_GETITEMRECT, 1, rcItem) Then
                                    Dim node As TreeNode = TreeNode.FromHandle(Me, NMTVCD.NMCD.dwItemSpec)
                                    If Not node Is Nothing Then
                                        RaiseEvent AfterDrawNode(node, g, rcItem)
                                    End If
                                End If

                                g.Dispose()

                                m.Result = IntPtr.op_Explicit(CDRF_SKIPDEFAULT)
                        End Select

                End Select
            Case Else
                MyBase.WndProc(m)
        End Select
    End Sub
End Class
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

1.0 - Introduction Converting Visual Basic 6.0 (VB6) to Visual Basic 2008+ (VB.NET). If ever there was a subject full of murkiness and bad decisions, it is this one!   The first problem seems to be that people considering this task of converting…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
This video discusses moving either the default database or any database to a new volume.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now