Images in a TreeView

Hi, I've got the following problem.

I'm looking forward to add a second image to a TreeView node, that indicates the status of the node (something like the small images that appears in MSVStudio .NET when you have it integrated with source safe (a lock if it is checked in, or a tick if it is checked out).

Only this kind of solution will be valid.

In order to have a more accurate of what I'm talking about check

http://es.geocities.com/n101noemi/TreeNode.jpg


Is there any way to achieve it? And in case there is, how?

Thanks a lot

Tincho
LVL 9
tinchosAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

malharoneCommented:
Public Class Form2
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents TreeView1 As System.Windows.Forms.TreeView
    Friend WithEvents ImageList1 As System.Windows.Forms.ImageList
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container
        Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form2))
        Me.TreeView1 = New System.Windows.Forms.TreeView
        Me.ImageList1 = New System.Windows.Forms.ImageList(Me.components)
        Me.SuspendLayout()
        '
        'TreeView1
        '
        Me.TreeView1.ImageIndex = -1
        Me.TreeView1.Location = New System.Drawing.Point(8, 8)
        Me.TreeView1.Name = "TreeView1"
        Me.TreeView1.SelectedImageIndex = -1
        Me.TreeView1.Size = New System.Drawing.Size(208, 176)
        Me.TreeView1.TabIndex = 0
        '
        'ImageList1
        '
        Me.ImageList1.ImageSize = New System.Drawing.Size(16, 16)
        Me.ImageList1.ImageStream = CType(resources.GetObject("ImageList1.ImageStream"), System.Windows.Forms.ImageListStreamer)
        Me.ImageList1.TransparentColor = System.Drawing.Color.Transparent
        '
        'Form2
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.TreeView1)
        Me.Name = "Form2"
        Me.Text = "Form2"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.TreeView1.ImageList = Me.ImageList1
        Dim tn1, tn2, tn3 As TreeNode
        tn1 = New TreeNode("C:\", 0, 0)
        tn2 = New TreeNode("Windows", 1, 1)
        tn3 = New TreeNode("Program Files", 2, 2)

        tn1.Nodes.Add(tn2)
        tn1.Nodes.Add(tn3)
        Me.TreeView1.Nodes.Add(tn1)

    End Sub
End Class
Bob LearnedCommented:
Do you know what an ImageList is and how to use it?  I don't know the specific syntax for C#, mostly a VB programmer, but I could help, but I don't have much time to work out the specifics now.  Just something to get you started.
tinchosAuthor Commented:
malharone,

I dont have problems with the image

Apart from that, I quite don't see which is the part of your code supposed to add the other image.



TheLearnedOne,

Yes, I do know how to use an ImageList, in fact I'm using it, what I don't know is how to add this second image to the node of the treeview.

Thanks

Tincho
CompTIA Network+

Prepare for the CompTIA Network+ exam by learning how to troubleshoot, configure, and manage both wired and wireless networks.

malharoneCommented:
image list is a form control .... so from the tool box, find the imagelist, and drag it into your form. once you do that, then go to the imagelist properties, and add images to the images collection.
Bob LearnedCommented:
Oooh, now I understand.  You want 2 images for one node.  I didn't get that one right away.  You could do an owner-drawn tree view (ugly!), or you could use an one image that has 2 parts to indicate both the type and the state.
tinchosAuthor Commented:
Just in order to clarify.

What I'm looking forward to is to add have 2 images for the node.

The original one and the new 'status' one.

As in the image.

Tincho
malharoneCommented:
"Apart from that, I quite don't see which is the part of your code supposed to add the other image."??

i added images into my imagelist using the UI -- property builder.
and then using "Me.TreeView1.ImageList = Me.ImageList1" or "this.TreeView1.ImageList = this.ImageList1;", i assigned the imagelist to the treeview control in the form.

in the following line, i construct the node. the constructor i used is:
sub new (node_label_text, image_index, selected_index)
node_label_text: should be obvious ... it's a string that's displayed as the label
image_index: is the index of the image from the image list which is used to display the image when the focus is NOT on that node
selected_index: is the index of the image from the image list which is used to display the image when the focus IS on that node -- when the node is selected.

in my example below, i've used 0,0 or 1,1 or 2,2 .. since i want the same image displayed wether the node is selected or not.
tn1 = New TreeNode("C:\", 0, 0)
tinchosAuthor Commented:
TheLearnedOne

Yeap, that's a possible approach, I had that in mind, but.......

suppose that in the treeview you have 4 different images and then you have 5 different states.....

then I would have to have so many images as the combination of them (20 different images)

as the number of states can grow and the number of different images also, it's not acceptable.

Tincho
malharoneCommented:
you can't really display two different images for single node. what you can do is display an image for one status, and when the status changes change the imageindex and selectedimageindex properties.


Public Class Form2
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents TreeView1 As System.Windows.Forms.TreeView
    Friend WithEvents ImageList1 As System.Windows.Forms.ImageList
    Friend WithEvents Button1 As System.Windows.Forms.Button
    Friend WithEvents Button2 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container
        Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form2))
        Me.TreeView1 = New System.Windows.Forms.TreeView
        Me.ImageList1 = New System.Windows.Forms.ImageList(Me.components)
        Me.Button1 = New System.Windows.Forms.Button
        Me.Button2 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'TreeView1
        '
        Me.TreeView1.ImageIndex = -1
        Me.TreeView1.Location = New System.Drawing.Point(8, 8)
        Me.TreeView1.Name = "TreeView1"
        Me.TreeView1.SelectedImageIndex = -1
        Me.TreeView1.Size = New System.Drawing.Size(208, 176)
        Me.TreeView1.TabIndex = 0
        '
        'ImageList1
        '
        Me.ImageList1.ImageSize = New System.Drawing.Size(16, 16)
        Me.ImageList1.ImageStream = CType(resources.GetObject("ImageList1.ImageStream"), System.Windows.Forms.ImageListStreamer)
        Me.ImageList1.TransparentColor = System.Drawing.Color.Transparent
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(224, 120)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(64, 32)
        Me.Button1.TabIndex = 1
        Me.Button1.Text = "Change Status"
        '
        'Button2
        '
        Me.Button2.Location = New System.Drawing.Point(224, 160)
        Me.Button2.Name = "Button2"
        Me.Button2.Size = New System.Drawing.Size(64, 32)
        Me.Button2.TabIndex = 1
        Me.Button2.Text = "Undo Change"
        '
        'Form2
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.Button1)
        Me.Controls.Add(Me.TreeView1)
        Me.Controls.Add(Me.Button2)
        Me.Name = "Form2"
        Me.Text = "Form2"
        Me.ResumeLayout(False)

    End Sub

#End Region
    Public tn1, tn2, tn3 As TreeNode
    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.TreeView1.ImageList = Me.ImageList1

        tn1 = New TreeNode("C:\", 0, 0)
        tn2 = New TreeNode("Windows", 1, 1)
        tn3 = New TreeNode("Program Files", 2, 2)

        tn1.Nodes.Add(tn2)
        tn1.Nodes.Add(tn3)
        Me.TreeView1.Nodes.Add(tn1)

    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        tn1.ImageIndex = 1
        tn1.SelectedImageIndex = 1
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        tn1.ImageIndex = 0
        tn1.SelectedImageIndex = 0
    End Sub
End Class
Bob LearnedCommented:
The normal behavior for the node is to have one image, and I can't think of another way around that limitation right now.
tinchosAuthor Commented:
malharone

I need to show both things... one possible suggestion is TheLearnedOne's that is to compose both images and show that ones, but I'm looking if there is a way to achieve it in a different way.


TheLearnedOne

I just think that Microsoft must have made it some way, and I was trying to achieve that. Besides, do you know any way of composing 2 images at runtime so as to say

Image image1 = nodeImage;
Image image2 = statusImage;

Image image3 = image1 + image2;        // it would be the composing of both images........ so as to avoid having all the combination of images?

Tincho
malharoneCommented:
do you have to combine the images at runtime?? can't you first create your image3 at design time -- using an image editor, and then use it? this may not be the best solution, but saves you render time at execution.
tinchosAuthor Commented:
Nop, its not viable

Imagine that you have 6 (A, B, C, D, E, F) different images for nodes and 6 possible states (1, 2, 3, 4, 5, 6)

then you would have 36 images in your imagelist
(A1, A2, A3, A4, A5, A6, B1, B2, B3, B4, B5, B6, C1, C2, C3, C4, C5, C6, D1, D2, D3, D4, D5, D6, E1, E2, E3, E4, E5, E6, F1, F2, F3, F4, F5, F6)

and the number of images and status can be higher, so it would end up with the image list being too big.

Tincho
malharoneCommented:
yup.. you could end up with
n! -- n factorial number of images.
let me check if you can programmatically "merge" two images. do they have the same dimension??
Bob LearnedCommented:
I know that you can use the Graphics object:  Graphics.DrawImage(image, x, y) to draw one image on another, but I wouldn't know what the C# syntax is.
tinchosAuthor Commented:
malharone

yes, they do.....

TheLearnedOne

Graphics.DrawImage(image, x, y)
which one would be the first image and which one the second?

Tincho

Bob LearnedCommented:
BTW In the history of Micro$oft, there has been a lot of magic performed that they haven't let us "little people" in on.  There is a lot of talent in that company, so I would say that they just created a control that does just what they need, and it doesn't necessarily have to be a TreeView, it just has to look like one.  There is no way of telling what was done from the end result!
Bob LearnedCommented:
If we were going to use the SourceSafe functionality as a template:

Image #1:  Lock state, image #2 module type.

Get the graphics object for #1.  Make the image wider by the amount of #2.  

VB Code:
   Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

      Dim w As Integer = Me.PictureBox1.Width
      Me.PictureBox1.Size = New Size(w + Me.PictureBox2.Width, Me.PictureBox1.Height)

      Dim g As Graphics = e.Graphics

      g.DrawImage(Me.PictureBox2.Image, w, 0)

   End Sub
malharoneCommented:
assuming you have 3 differnt picture boxes on the form.

        Dim b1, b2 As Bitmap
        b1 = New Bitmap("C:\WINDOWS\Web\Wallpaper\Ripple.jpg")
        b2 = New Bitmap("C:\WINDOWS\Web\Wallpaper\Autumn.jpg")
        Me.PictureBox1.Image = b1
        Me.PictureBox2.Image = b2
        Dim b3 As New Bitmap(b1)
        Dim x, y As Integer
        x = Math.Min(b1.Width, b2.Width)
        y = Math.Min(b1.Height, b2.Height)
        Dim i, j As Integer
        For i = 1 To x Step 2
            For j = 1 To y Step 2
                b3.SetPixel(i, j, b2.GetPixel(i, j))
            Next
        Next
        Me.PictureBox3.Image = b3
tinchosAuthor Commented:
TheLearnedOne, malharone

Thanks for your posts........
I'm going home now, I'll try them tomorrow, but at least the idea seems to be that one.

Tincho
malharoneCommented:
fix:
change
        x = Math.Min(b1.Width, b2.Width)
        y = Math.Min(b1.Height, b2.Height)

to
        x = Math.Min(b1.Width, b2.Width) - 1
        y = Math.Min(b1.Height, b2.Height) - 1
tinchosAuthor Commented:
TheLearnedOne

I'm looking for a way to compose both images into one, not to paint them at the same time. Any ideas of how to do it?


malharone

I've looked at your code, and it seems that it's not composing both images but

newImage = image1
then overwrite each pixel in image1 with the pixel in image2

so, it's kind of overwriting newImage with image2 and there's nothing left from image1

Am I right?

Tincho
Bob LearnedCommented:
Sometimes we struggle to get on the same page.

Initially, when the program first runs, you can programmatically combine all images, as necessary.  You can use a picture box control to perform all the work.  Once all the images are constructed, they can be added to the ImageList control.

Painting, composing, are basically the same process.  All you need is to be able to have a picture box where the .Image property contains the graphics for both, whether you "compose" or "paint", they are persistent graphics, and you can add them to the ImageList.
tinchosAuthor Commented:
TheLearnedOne

I understood the first paragraph, but the socond one I'm not sure about what you meant.

My question was because your function was PictureBox1_Paint and I only have a graphic instance in the paint method.

Tincho
Bob LearnedCommented:
Okay, now you see what I mean about being on the same page.  

You are right, the form would have to be visible before you get the Graphics.  Not a good option.

It took me a while, since I hadn't worked much with this, but I have learned something for myself.  I tried this in VB.NET for myself:

(1)  Defined module-level Bitmap and Graphics:  

      Dim workingBitmap As Bitmap
      Dim g As Graphics

(2)  Added three picture box controls:  pictureCombine (32 x 16), picture1 (16 x 16), and picture2 (16 x 16)

(3)  Created CreateImage routine:

   Private Sub CreateImage()

      workingBitmap = New Bitmap(Me.pictureCombine.Image, 32, 16)

      g = Graphics.FromImage(workingBitmap)

      g.Clear(Color.White)

      g.DrawImage(Me.picture1.Image, 0, 0)
      g.DrawImage(Me.picture2.Image, 16, 0)

      Me.pictureCombine.Image = workingBitmap

   End Sub

(4) Added this code to Form_Closing routine (If you dispose of this to early, you will get errors):

      workingBitmap.Dispose()
      g.Dispose()

What I got was two images combined onto one picture box (pictureCombine) that was 32 x 16.  You could add this image to the ImageList.  Also, the form wasn't visible, so this should be more what you would need.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
tinchosAuthor Commented:
Thanks TheLearnedOne

I'm going home now, let me check it back on monday when I return to work and I'll let you know

Tincho
tinchosAuthor Commented:
Sorry, on tuesday (Monday is a holiday in Argentina)

tinchosAuthor Commented:
Sorry for the delay in the response TheLearnedOne

I've tested your code and it worked just fine.
Even though it was not the original solution I was looking for (I was looking for something already implemented in .net), it is a very useful and workable alternative.

Thanks a lot

Tincho
Bob LearnedCommented:
Your welcome.  I wish I could have provided you with a better solution, but I couldn't find one.
tinchosAuthor Commented:
No problem at all

As I said before, it was quite useful, and when solutions cannot be achieved, alternatives are as valid as them.

Tincho
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.