Answered by:
Create Custom Shape that holds Icons

Question
-
I want to be able to create a custom shape that holds icons. Lets say for simplicity that it is a sqaure. I need the ability to load up to X amount of icons in the square. Lets say I need up to 6 icons. These icons will always be the same. However not all custom shapes will need them. I am assuming that I have to create a custom shape object class. I also want the icons to automatically adjust in size when I change the size of the shape. I am guessing that one way to do this would be to drop a TableLayoutPanel into the shape and then dock it to the shape. Then set the dock property of the icon to the cell that it resides into within the TableLayoutPanel in the shape. The number of rows/columns of the TableLayoutPanel would be predicated on the number of icons needed.
How do I do this? Any other ideas?
Thanks
EM- Edited by ExcelMonkey Wednesday, October 14, 2009 9:44 PM clarity
Wednesday, October 14, 2009 9:02 PM
Answers
-
Alright. How about this - just past the code behind a blank form, add six images to Project Resources (you may want to change their names to reflect the ones I have to make it easier) and run it. I think it illustrates my point pretty well.
Public Class Form1 Private pnl As New MultiImagePanel(MultiImagePanel.Icons.CheckedTask) Private WithEvents Button1 As New Button Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Button1.Text = "Change" Me.Size = New Size(300, 300) Me.Controls.Add(Button1) Me.Controls.Add(pnl) Button1.Location = New Point(135, 40) pnl.Location = New Point(Button1.Left, Button1.Bottom + 10) End Sub Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click If CInt(pnl.SelectedImage) = 5 Then pnl.SelectedImage = 0 Else pnl.SelectedImage = CType(CInt(pnl.SelectedImage) + 1, MultiImagePanel.Icons) End If End Sub End Class Public Class MultiImagePanel Inherits Panel Public Enum Icons CheckBox_Green = 0 CheckBox_Red = 1 CheckedTask = 2 CheckedTask_All = 3 close = 4 Collapse_small = 5 End Enum Private Shared Images As ImageList Shared Sub New() Images = New ImageList() Images.ColorDepth = ColorDepth.Depth32Bit Images.ImageSize = New Size(32, 32) 'Load the six images into the list... Images.Images.AddRange(New Image() {My.Resources.CheckBox_Green, My.Resources.CheckBox_Red, _ My.Resources.CheckedTask, My.Resources.CheckedTask_All, My.Resources.close, My.Resources.Collapse_small}) End Sub Private m_SelectedImage As Icons Public Property SelectedImage() As Icons Get Return m_SelectedImage End Get Set(ByVal value As Icons) m_SelectedImage = value SetImage(value) End Set End Property Public Sub New(Optional ByVal _picture As Icons = Icons.close) MyBase.Size = New Size(32, 32) MyBase.BackgroundImageLayout = ImageLayout.Zoom 'Using a default icon ("close") SetImage(_picture) End Sub Private Sub SetImage(ByVal _picture As Icons) m_SelectedImage = _picture 'The index within the image list must match the enum values. MyBase.BackgroundImage = Images.Images(CInt(_picture)) End Sub #Region " Size Management " Public Shadows ReadOnly Property Size() As Size Get Return MyBase.Size End Get End Property Public Shadows ReadOnly Property Width() As Int32 Get Return MyBase.Width End Get End Property Public Shadows ReadOnly Property Height() As Int32 Get Return MyBase.Width End Get End Property Private Sub MultiImagePanel_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles MyBase.SizeChanged If MyBase.Width <> 32 OrElse MyBase.Height <> 32 Then MyBase.Size = New Size(32, 32) End If End Sub #End Region End Class
- Proposed as answer by MaDFroG20091013 Wednesday, October 21, 2009 2:00 AM
- Marked as answer by Jeff Shan Wednesday, October 21, 2009 8:50 AM
Friday, October 16, 2009 6:30 PM -
EM - that seems like the right approach to me. You just need to have the ability to show all 6 images at once (which I think you already have) The devil is definitely in teh details here because - as you seem to realize based on your pseudo code - you need to adjust the location and size to provide a natural look and feel to the changed suite of imges being displayed.
Good luck!- Marked as answer by ExcelMonkey Thursday, October 22, 2009 2:06 AM
Wednesday, October 21, 2009 1:54 PM -
In terms of the functionality to hide or show the images it can be done many ways - and much of it depends upon your architecture. For instance, how does the panel class know when to show/hide icons? Is it through events or through certain property changes? Are there other conditions that dictate when one can be shown or not.
The actual showing and hiding is pretty sinple though. In my example below I have simplified the process by using colored sub-panels -- the important part is that I have exposed boolean properties that directly influece which images are visible. This example is not very practical for a real-worls scenario, but if you replace the properties with some kind of managing/controller method or component then it might make more sense (in other words, what you replace these properties with could be a strictly internal process that is triggered by events or state changes). As with before, just paste this code behind a blank form and give it a try.
Public Class Form1 Private pnl As New MultiIconPanel Private btn As New Button Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load btn.Text = "Switch" btn.Location = New Point(10, 10) Me.Controls.Add(btn) AddHandler btn.Click, AddressOf btn_Click pnl.Location = New Point(btn.Left, btn.Bottom + 10) Me.Controls.Add(pnl) End Sub Private Sub btn_Click(ByVal sender As Object, ByVal e As EventArgs) Static value As Int32 = 0 value += 1 If value > 3 Then value = 1 Select Case value Case 1 pnl.ShowImage1 = True pnl.ShowImage2 = False pnl.ShowImage3 = False Case 2 pnl.ShowImage1 = True pnl.ShowImage2 = True pnl.ShowImage3 = False Case 3 pnl.ShowImage1 = True pnl.ShowImage2 = True pnl.ShowImage3 = True End Select End Sub End Class Public Class MultiIconPanel Inherits Panel Public Sub New() 'Just for the example so it can be seen. Me.BorderStyle = Windows.Forms.BorderStyle.FixedSingle For i As Int32 = 1 To 3 Dim P As New Panel P.Name = "P" & i.ToString P.Size = New Size(CInt(Me.Width / 3), Me.Height) P.Left = CInt(Me.Width / 3) * (i - 1) P.Visible = False 'Defaulting all as hidden. Me.Controls.Add(P) Select Case i Case 1 P.BackColor = Color.Red Case 2 P.BackColor = Color.Yellow Case 3 P.BackColor = Color.Green End Select Next End Sub Public Property ShowImage1() As Boolean Get Return Me.Controls("P1").Visible End Get Set(ByVal value As Boolean) Me.Controls("P1").Visible = value Me.Refresh() End Set End Property Public Property ShowImage2() As Boolean Get Return Me.Controls("P2").Visible End Get Set(ByVal value As Boolean) Me.Controls("P2").Visible = value Me.Refresh() End Set End Property Public Property ShowImage3() As Boolean Get Return Me.Controls("P3").Visible End Get Set(ByVal value As Boolean) Me.Controls("P3").Visible = value Me.Refresh() End Set End Property End Class
- Marked as answer by ExcelMonkey Monday, October 26, 2009 2:54 PM
Thursday, October 22, 2009 2:15 PM
All replies
-
Anybody out there?
Thanks
EMThursday, October 15, 2009 6:28 PM -
Maybe something like this ??.. http://www.vbcity.com/forums/topic.asp?tid=163181Thursday, October 15, 2009 7:50 PM
-
I will look at this closer and see. I am creating a flow chart. The flow chart uses shapes. I am trying to replace my shapes with custom shapes that hold icons.
Thanks
EMThursday, October 15, 2009 8:41 PM -
I know I can draw shapes as follows below
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)Handles Me.Paint
Dim BlackPen As New Drawing.Pen(Color.Black, 2)
e.Graphics.DrawRectangle(BlackPen, 10, 10, 100, 100)
End Sub
I also know I can add a panel and put a tablelayoutpanel in this panel as follows:
'Add Panel
pnl = New Panel
pnl.BackColor = Color.Black
Me.Controls.Add(pnl)
'Add TableLayoutPanel to Panel
TLP = New TableLayoutPanel
TLP.ColumnCount = 2
TLP.RowCount = 2
TLP.CellBorderStyle = TableLayoutPanelCellBorderStyle.Outset
TLP.Padding = New Padding(1, 1, 1, 1)
pnl.Controls.Add(TLP)
I was hoping I could work with one of these options.
Thanks
EMFriday, October 16, 2009 3:34 AM -
Why icons as those are from the time the first graphics where desingned, why not more exanchable bitmaps like jpeg, png, gif or whatever.
Success
CorFriday, October 16, 2009 4:10 AM -
Sure, I am not to fussy at this point. I just want to be able to pull in at least 6 "icon-sized" images and have them adjust in size based on the size of the square or panel or whatever I am using.
Thanks
EM
Friday, October 16, 2009 3:09 PM -
If you use the panel's BackgroundImage property then set the BackgroundImageLayout property to Zoom it will automatically resize the image to the panel. If you use .PNG images then any transaparency will be retained (jpegs lose the transaparency) giving the appearance of a custom shape. The user's awareness of the panel's shape would be when the cursor changes as the mouse enters the bounds of the panel.
For the images I would have a Shared scope ImageList that gets loaded in the Shared Sub New constructor (happens only once per app life), then the background image can be set programmatically by index (or name) from within this custom Panel-inheriting class.
Would this work?Friday, October 16, 2009 4:43 PM -
Maybe. Could you provide me with a sample to wrap my head around. Ultmately I am drawing a a bunch of squares. Assume each sqaure represents a collection of images. Assume the image represents a boolean paremeter (i.e. if the sqaure has a certain property then the incon is show if not, it is not shown). There is only 1 master list of images (no unique images for any square).
The idea here is to draw the sqaures at defined locations and load the images into them. The squares represent summaries of data.
Thanks
EMFriday, October 16, 2009 6:00 PM -
Alright. How about this - just past the code behind a blank form, add six images to Project Resources (you may want to change their names to reflect the ones I have to make it easier) and run it. I think it illustrates my point pretty well.
Public Class Form1 Private pnl As New MultiImagePanel(MultiImagePanel.Icons.CheckedTask) Private WithEvents Button1 As New Button Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Button1.Text = "Change" Me.Size = New Size(300, 300) Me.Controls.Add(Button1) Me.Controls.Add(pnl) Button1.Location = New Point(135, 40) pnl.Location = New Point(Button1.Left, Button1.Bottom + 10) End Sub Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click If CInt(pnl.SelectedImage) = 5 Then pnl.SelectedImage = 0 Else pnl.SelectedImage = CType(CInt(pnl.SelectedImage) + 1, MultiImagePanel.Icons) End If End Sub End Class Public Class MultiImagePanel Inherits Panel Public Enum Icons CheckBox_Green = 0 CheckBox_Red = 1 CheckedTask = 2 CheckedTask_All = 3 close = 4 Collapse_small = 5 End Enum Private Shared Images As ImageList Shared Sub New() Images = New ImageList() Images.ColorDepth = ColorDepth.Depth32Bit Images.ImageSize = New Size(32, 32) 'Load the six images into the list... Images.Images.AddRange(New Image() {My.Resources.CheckBox_Green, My.Resources.CheckBox_Red, _ My.Resources.CheckedTask, My.Resources.CheckedTask_All, My.Resources.close, My.Resources.Collapse_small}) End Sub Private m_SelectedImage As Icons Public Property SelectedImage() As Icons Get Return m_SelectedImage End Get Set(ByVal value As Icons) m_SelectedImage = value SetImage(value) End Set End Property Public Sub New(Optional ByVal _picture As Icons = Icons.close) MyBase.Size = New Size(32, 32) MyBase.BackgroundImageLayout = ImageLayout.Zoom 'Using a default icon ("close") SetImage(_picture) End Sub Private Sub SetImage(ByVal _picture As Icons) m_SelectedImage = _picture 'The index within the image list must match the enum values. MyBase.BackgroundImage = Images.Images(CInt(_picture)) End Sub #Region " Size Management " Public Shadows ReadOnly Property Size() As Size Get Return MyBase.Size End Get End Property Public Shadows ReadOnly Property Width() As Int32 Get Return MyBase.Width End Get End Property Public Shadows ReadOnly Property Height() As Int32 Get Return MyBase.Width End Get End Property Private Sub MultiImagePanel_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles MyBase.SizeChanged If MyBase.Width <> 32 OrElse MyBase.Height <> 32 Then MyBase.Size = New Size(32, 32) End If End Sub #End Region End Class
- Proposed as answer by MaDFroG20091013 Wednesday, October 21, 2009 2:00 AM
- Marked as answer by Jeff Shan Wednesday, October 21, 2009 8:50 AM
Friday, October 16, 2009 6:30 PM -
Ok I got this working. Effectively it loads a form with one of your images in it. There is also a button on the form. When you click the button, the click event changes the picture.
What I need to do is to be able to show multiple pictures at the same time. So if there are 6 images I need a way to indicate which images need to be shown simultaneously. I then need to ensure that the pictures adjust in size based on the size of the control that the images are in. I am not allowing the user to adjust the size of the control at run-time. I am adjusting the size and position to place them in a flow chart for aesthetics.
I am assuming that it is likely easier to create a class that has properties. You would create an instance of the object, set properties, define location on the form, and then define the size. This would then allow me to show multiple instances of this object all of which will have their images loaded as prescribed by properties.
'create Instance of Object
X = New Shape
'Set Properties
Shape.HasProperty1 = True
Shape.HasProperty2 = False
Shape.HasProperty3 = False
Shape.HasProperty4 = True
Shape.HasProperty5 = False
Shape.HasProperty6 = True
'Show Images based on property
If Shape.HasProperty1 = True Then
'Show Image1
End if
If Shape.HasProperty2 = True Then
'Show Image2
End if
If Shape.HasProperty3 = True Then
'Show Image3
End if
If Shape.HasProperty4 = True Then
'Show Image4
End if
If Shape.HasProperty5 = True Then
'Show Image5
End if
If Shape.HasProperty6 = True Then
'Show Image6
End if
'Set Location
'Set Size
Does that make sense?
Thanks
EMSaturday, October 17, 2009 3:16 AM -
I think you can do this. Do you think try it first and then ask a question when you encounter a error will be a better idea?Wednesday, October 21, 2009 2:00 AM
-
EM - that seems like the right approach to me. You just need to have the ability to show all 6 images at once (which I think you already have) The devil is definitely in teh details here because - as you seem to realize based on your pseudo code - you need to adjust the location and size to provide a natural look and feel to the changed suite of imges being displayed.
Good luck!- Marked as answer by ExcelMonkey Thursday, October 22, 2009 2:06 AM
Wednesday, October 21, 2009 1:54 PM -
Thanks Digboy.
The custom panel has a SelectedImage property which takes a value.
Public Property SelectedImage() As Icons
Get
Return m_SelectedImage
End Get
Set(ByVal value As Icons)
m_SelectedImage = value
SetImage(value)
End Set
End Property
Then within the button click event you pull a single image into view by doing the following:
If CInt(pnl.SelectedImage) = 5 Then
pnl.SelectedImage = 0
Else
pnl.SelectedImage = CType(CInt(pnl.SelectedImage) + 1, MultiImagePanel.Icons)
End If
So if I want to show multiple images then do I need to change the SelectedImage property such that it takes multiple values (i.e. list or array to hold these values)?Private _SelectedImages As List(Of Double)
Public Property SelectedImages() As List(Of Double)
Get
Return _SelectedImages
End Get
Set(ByVal value As List(Of Double))
_SelectedImages = value
End Set
End Property
And then do I need to adjust the SetImage Sub so that it sets multiple images? I am assuming the variable declarations in the () need to account for multiple icons and not single icons. I am not sure how to do this. Does _picture have to be a collection of Icons?
Public Sub New(Optional ByVal _picture As Icons = Icons.close)
MyBase.Size = New Size(32, 32)
MyBase.BackgroundImageLayout = ImageLayout.Zoom
'Using a default icon ("close")
SetImages(_picture)
End Sub
Private Sub SetImages(ByVal _picture As Icons)
m_SelectedImages = _picture
'The index within the image list must match the enum values.
MyBase.BackgroundImage = Images.Images(CInt(_picture))
End Sub
And then I am assuming that I would need to fill the list as follows:
pnl.SelectedImages.Add(0)
pnl.SelectedImages.Add(2)
pnl.SelectedImages.Add(4)Have I gone off on a tangent here? Sorry I don't have VS at my fingertips during the day!
Thanks
EM
- Edited by ExcelMonkey Wednesday, October 21, 2009 5:19 PM Clarity
Wednesday, October 21, 2009 4:47 PM -
In terms of the functionality to hide or show the images it can be done many ways - and much of it depends upon your architecture. For instance, how does the panel class know when to show/hide icons? Is it through events or through certain property changes? Are there other conditions that dictate when one can be shown or not.
The actual showing and hiding is pretty sinple though. In my example below I have simplified the process by using colored sub-panels -- the important part is that I have exposed boolean properties that directly influece which images are visible. This example is not very practical for a real-worls scenario, but if you replace the properties with some kind of managing/controller method or component then it might make more sense (in other words, what you replace these properties with could be a strictly internal process that is triggered by events or state changes). As with before, just paste this code behind a blank form and give it a try.
Public Class Form1 Private pnl As New MultiIconPanel Private btn As New Button Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load btn.Text = "Switch" btn.Location = New Point(10, 10) Me.Controls.Add(btn) AddHandler btn.Click, AddressOf btn_Click pnl.Location = New Point(btn.Left, btn.Bottom + 10) Me.Controls.Add(pnl) End Sub Private Sub btn_Click(ByVal sender As Object, ByVal e As EventArgs) Static value As Int32 = 0 value += 1 If value > 3 Then value = 1 Select Case value Case 1 pnl.ShowImage1 = True pnl.ShowImage2 = False pnl.ShowImage3 = False Case 2 pnl.ShowImage1 = True pnl.ShowImage2 = True pnl.ShowImage3 = False Case 3 pnl.ShowImage1 = True pnl.ShowImage2 = True pnl.ShowImage3 = True End Select End Sub End Class Public Class MultiIconPanel Inherits Panel Public Sub New() 'Just for the example so it can be seen. Me.BorderStyle = Windows.Forms.BorderStyle.FixedSingle For i As Int32 = 1 To 3 Dim P As New Panel P.Name = "P" & i.ToString P.Size = New Size(CInt(Me.Width / 3), Me.Height) P.Left = CInt(Me.Width / 3) * (i - 1) P.Visible = False 'Defaulting all as hidden. Me.Controls.Add(P) Select Case i Case 1 P.BackColor = Color.Red Case 2 P.BackColor = Color.Yellow Case 3 P.BackColor = Color.Green End Select Next End Sub Public Property ShowImage1() As Boolean Get Return Me.Controls("P1").Visible End Get Set(ByVal value As Boolean) Me.Controls("P1").Visible = value Me.Refresh() End Set End Property Public Property ShowImage2() As Boolean Get Return Me.Controls("P2").Visible End Get Set(ByVal value As Boolean) Me.Controls("P2").Visible = value Me.Refresh() End Set End Property Public Property ShowImage3() As Boolean Get Return Me.Controls("P3").Visible End Get Set(ByVal value As Boolean) Me.Controls("P3").Visible = value Me.Refresh() End Set End Property End Class
- Marked as answer by ExcelMonkey Monday, October 26, 2009 2:54 PM
Thursday, October 22, 2009 2:15 PM -
Thanks Digboy, this is really helpful. I would be interested in knowing what you mean by:
"if you replace the properties with some kind of managing/controller method or component then it might make more sense"
Currently I am going down the boolean path. What will likely happen is I will build an XML File during my form load event.
Private Sub Form1_Load()
dim doc as Xlement = _
<Objects>
<Object name = "Object1"/>
<Images>
<Image1>TRUE</Image1>
<Image2>FALSE</Image2>
<Image3>TRUE</Image3>
</Images>
<Object name = "Object2"/>
<Images>
<Image1>FALSE</Image1>
<Image2>FALSE</Image2>
<Image3>FALSE</Image3>
</Images>
<Object name = "Object3"/>
<Images>
<Image1>FALSE</Image1>
<Image2>TRUE</Image2>
<Image3>TRUE</Image3>
<Images>
</Objects>
doc.Save("C:\test.xml)
End Sub
Then I will likley build the correct # of panels by looping throught the XML and counting the Object elements. Then I will load the desired images into the panel by looping through the Image elements and testing for the boolen values in each element. Only the booleans will dictate whether the images are visible. The user will not have any interaction that will influence whether the images are shown.
I will then have have the appropriate logic so that I can size and place all the panels on the form. These will have to adjust in size based on the size of the form. Will use the Form's width/height as boundaries with some padding. Then I will also put in a MouseOver event for the panels so that the panels in question can be zoomed into if they are small. This is for a scenario where a lot of panels are created and the panel and the icons are sized too small. The Zoom will just have a maxsize varible and the panel will increase to that size while the mouse is over the panel.
The images will run left to right in two rows in the panel.
Thanks again.
EMMonday, October 26, 2009 3:27 PM -
Thanks Digboy, this is really helpful. I would be interested in knowing what you mean by:
"if you replace the properties with some kind of managing/controller method or component then it might make more sense"
I was thinking more of a scenario where an object (the panel in your case) has some sort of variable "state" wherein depending upon numerous factors (e.g. user GUI actions, permitted roles, form-based access, etc.) the number of images would vary. Think more along the lines of context menues that dynamically show different menu options depending on the context. In that case, since there may be a few different ways in which this state/context changes, you would have a single component that monitors each of those factors and changes the presentation of the images accordingly (I guess this is similar to the MVP pattern).
What you are describing does not appear to fit this bill so its probably not relevant since no user action determines what gets shown - though I suppose other non-user factors could be at play.Monday, October 26, 2009 11:49 PM