A UserControl is a bit like a subform. It is a container for an assortment of controls which act together or represent some logical unit. In this case, it would be the visual representation of some user states (properties). Rather than creating and managing individual controls, you can put them on one UC and encapsulate much of the code to manage them.
In the Solution Explorer window, right click, select Add, then pick UserControl. I added a TableLayoutPanel, 2 PictureBoxes and a Label (all with proper names of course). There is also an ImageList
with a quick grab of your images:
This alone replaces many many lines of code you have to create new controls and then setting the same properties over and over again. To make them act as a logical object, you can add properties which abstracts the minutiae of selecting images etc:
Public Class Chatter
Public Enum ChatStatus
Unknown
Online
Away
Offline
End Enum
Public Enum ChatMsgStatus
Undefined ' kludge to force the initial state
Unknown
[New]
Read
End Enum
' one set of images for all chatter instances
Private Shared Imgs As Image()
Public Property ChatId As Int32 ' or guid?
Private chName As String = ""
Public ReadOnly Property ChatName As String
Get
Return chName
End Get
End Property
Private mStatus As ChatMsgStatus = ChatMsgStatus.Undefined
Public Property MsgStatus As ChatMsgStatus
Get
Return mStatus
End Get
Set(value As ChatMsgStatus)
If (value <> mStatus) Then
Select Case value
Case ChatMsgStatus.New
pbMStatus.Image = Imgs(3)
Case ChatMsgStatus.Read
pbMStatus.Image = Imgs(4)
Case Else
pbMStatus.Image = Imgs(4)
End Select
End If
mStatus = value
End Set
End Property
Private chStatus As ChatStatus = ChatStatus.Unknown
Public Property Status As ChatStatus
Get
Return chStatus
End Get
Set(value As ChatStatus)
If value <> chStatus Then
Select Case value
Case ChatStatus.Online
pbUStatus.Image = Imgs(0)
Case ChatStatus.Away
pbUStatus.Image = Imgs(1)
Case ChatStatus.Offline
pbUStatus.Image = Imgs(2)
Case Else
End Select
End If
chStatus = value
End Set
End Property
Public Sub New()
' This call is required by the designer.
InitializeComponent()
If Imgs Is Nothing Then
Imgs = New Image() {My.Resources.ChatUserGrn, My.Resources.ChatUserYlw,
My.Resources.ChatUserRed, My.Resources.ChatBalloonGrn,
My.Resources.ChatBalloonGry}
' see note
End If
End If
' Add any initialization after the InitializeComponent() call.
End Sub
' no need to create one without Identifiers
Public Sub New(n As Int32, cname As String)
MyClass.New()
' default intitial values:
chName = cname
ChatId = n
lblChName.Text = cname
Me.Status = ChatStatus.Online
Me.MsgStatus = ChatMsgStatus.Unknown
End Sub
End Class
(Edit) I didn't like the result using an ImageList. This version, loads an array of images from resources. Aside from only loading one copy of GreenUser for use by all users, it allows you to tailor it as needed. For instance change the backcolor to SystemColors.Window
to match the user's theme. If you also use a label instead of a picturebox, you can use the Text
property for "?" or even indicate the number of new messages.
I am sure there is more to it and I can think of several things I would want it to know (for instance overlay the green balloon with the number of unread messages). But the point here are the concepts of encapsulation, DRY and reusable code.
When you compile it, there will be a new Chatter
control in the toolbox. Add some at runtime using the properties exposed:
Dim c As New Chatter(42, "Ziggy von Hausen")
flpChat.Controls.Add(c)
c = New Chatter(14, "ThDutoit")
c.MsgStatus = Chatter.ChatMsgStatus.New
flpChat.Controls.Add(c)
c = New Chatter(78, "Plutonix")
c.Status = Chatter.ChatStatus.Offline
flpChat.Controls.Add(c)
c = New Chatter(4, "Codexer")
c.MsgStatus = Chatter.ChatMsgStatus.New
c.Status = Chatter.ChatStatus.Away
flpChat.Controls.Add(c)
The Id would be something to uniquely identify each chat participant. The name is not usually enough (SO has over 50 pages of people named "Steve") and you will want a way to identify to link a control to a user. (An alternative, would be a ChatterBox
reference in the user list which is a reference to the related UserControl
:
Dim user = "Codexer"
Dim chatter = flpChat.Controls.
OfType(Of Chatter).
FirstOrDefault(Function(c) c.ChatName.StartsWith(user))
If chatter IsNot Nothing Then
chatter.Status = chatter.ChatStatus.Online
End If
It is sub optimal to search each time and an Id
would be better than a mere name. The ideal would be for a ChatUser
class with all the other stuff the app has to store by user. The class should include a reference to the control so that when the status changes or whatever, the class could simply:
myChatterBox.Status = myStatus
Result:
Certainly they can be created with considerably less code. In the course of things, you can change the status of either image by just setting the related property.
As an added benefit, because you are no longer creating individual controls, and because UserControl
inherits from Component
you dont have to worry about leaks if/when these are removed.
A Must Read:
Creating a Windows Form User Control