1

I'm making a contactlist with VB.NET with status images. I'm loading this list from MSSQL but when i reload the list it flickers.

This list is a TableLayoutPanel with dynamic created pictureboxes and labels.

My question is:

How can i change my image inside a dynamic picturebox when u reload my contacts instead of reloading the entire list.

My code to create the table:

While UserData.Read

    If UserData("Status").ToString = "Online" Then

        If UserData("NieuwBericht").ToString = "Ja" Then

            Dim newPictureBox As New PictureBox
            newPictureBox.Image = My.Resources.greenchat
            newPictureBox.Visible = True
            newPictureBox.Width = 30
            newPictureBox.Height = 30
            newPictureBox.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox)

            Dim newPictureBox2 As New PictureBox
            newPictureBox2.Image = My.Resources.greenbubblechat
            newPictureBox2.Visible = True
            newPictureBox2.Width = 30
            newPictureBox2.Height = 30
            newPictureBox2.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox2.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox2)

            Dim newLabel As New Label
            AddHandler newLabel.Click, AddressOf Chatbox
            newLabel.Text = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString
            newLabel.Name = UserData("Username").ToString
            newLabel.Font = New Font("Microsoft sans serif", 12)
            newLabel.Dock = DockStyle.Fill
            newLabel.TextAlign = ContentAlignment.MiddleLeft
            newLabel.Visible = True
            ChatContactList.Controls.Add(newLabel)

        ElseIf UserData("NieuwBericht").ToString = "Nee" Then

            Dim newPictureBox As New PictureBox
            newPictureBox.Image = My.Resources.greenchat
            newPictureBox.Visible = True
            newPictureBox.Width = 30
            newPictureBox.Height = 30
            newPictureBox.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox)

            Dim newPictureBox2 As New PictureBox
            newPictureBox2.Image = My.Resources.greybubblechat
            newPictureBox2.Visible = True
            newPictureBox2.Width = 30
            newPictureBox2.Height = 30
            newPictureBox2.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox2.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox2)

            Dim newLabel As New Label
            AddHandler newLabel.Click, AddressOf Chatbox
            newLabel.Text = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString
            newLabel.Name = UserData("Username").ToString
            newLabel.Font = New Font("Microsoft sans serif", 12)
            newLabel.Dock = DockStyle.Fill
            newLabel.TextAlign = ContentAlignment.MiddleLeft
            newLabel.Visible = True
            ChatContactList.Controls.Add(newLabel)

        End If

    ElseIf UserData("Status").ToString = "Afwezig" Then

        If UserData("NieuwBericht").ToString = "Ja" Then

            Dim newPictureBox As New PictureBox
            newPictureBox.Image = My.Resources.orangechat
            newPictureBox.Visible = True
            newPictureBox.Width = 30
            newPictureBox.Height = 30
            newPictureBox.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox)

            Dim newPictureBox2 As New PictureBox
            newPictureBox2.Image = My.Resources.greenbubblechat
            newPictureBox2.Visible = True
            newPictureBox2.Width = 30
            newPictureBox2.Height = 30
            newPictureBox2.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox2.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox2)

            Dim newLabel As New Label
            AddHandler newLabel.Click, AddressOf Chatbox
            newLabel.Text = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString
            newLabel.Name = UserData("Username").ToString
            newLabel.Font = New Font("Microsoft sans serif", 12)
            newLabel.Dock = DockStyle.Fill
            newLabel.TextAlign = ContentAlignment.MiddleLeft
            newLabel.Visible = True
            ChatContactList.Controls.Add(newLabel)

        ElseIf UserData("NieuwBericht").ToString = "Nee" Then

            Dim newPictureBox As New PictureBox
            newPictureBox.Image = My.Resources.orangechat
            newPictureBox.Visible = True
            newPictureBox.Width = 30
            newPictureBox.Height = 30
            newPictureBox.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox)

            Dim newPictureBox2 As New PictureBox
            newPictureBox2.Image = My.Resources.greybubblechat
            newPictureBox2.Visible = True
            newPictureBox2.Width = 30
            newPictureBox2.Height = 30
            newPictureBox2.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox2.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox2)

            Dim newLabel As New Label
            AddHandler newLabel.Click, AddressOf Chatbox
            newLabel.Text = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString
            newLabel.Name = UserData("Username").ToString
            newLabel.Font = New Font("Microsoft sans serif", 12)
            newLabel.Dock = DockStyle.Fill
            newLabel.TextAlign = ContentAlignment.MiddleLeft
            newLabel.Visible = True
            ChatContactList.Controls.Add(newLabel)

        End If

    ElseIf UserData("Status").ToString = "Offline" Then

        If UserData("NieuwBericht").ToString = "Ja" Then

            Dim newPictureBox As New PictureBox
            newPictureBox.Image = My.Resources.redchat
            newPictureBox.Visible = True
            newPictureBox.Width = 30
            newPictureBox.Height = 30
            newPictureBox.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox)

            Dim newPictureBox2 As New PictureBox
            newPictureBox2.Image = My.Resources.greenbubblechat
            newPictureBox2.Visible = True
            newPictureBox2.Width = 30
            newPictureBox2.Height = 30
            newPictureBox2.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox2.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox2)

            Dim newLabel As New Label
            AddHandler newLabel.Click, AddressOf Chatbox
            newLabel.Text = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString
            newLabel.Name = UserData("Username").ToString
            newLabel.Font = New Font("Microsoft sans serif", 12)
            newLabel.Dock = DockStyle.Fill
            newLabel.TextAlign = ContentAlignment.MiddleLeft
            newLabel.Visible = True
            ChatContactList.Controls.Add(newLabel)

        ElseIf UserData("NieuwBericht").ToString = "Nee" Then

            Dim newPictureBox As New PictureBox
            newPictureBox.Image = My.Resources.redchat
            newPictureBox.Visible = True
            newPictureBox.Width = 30
            newPictureBox.Height = 30
            newPictureBox.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox)

            Dim newPictureBox2 As New PictureBox
            newPictureBox2.Image = My.Resources.greybubblechat
            newPictureBox2.Visible = True
            newPictureBox2.Width = 30
            newPictureBox2.Height = 30
            newPictureBox2.SizeMode = PictureBoxSizeMode.Zoom
            newPictureBox2.Name = UserData("Username").ToString
            ChatContactList.Controls.Add(newPictureBox2)

            Dim newLabel As New Label
            AddHandler newLabel.Click, AddressOf Chatbox
            newLabel.Text = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString
            newLabel.Name = UserData("Username").ToString
            newLabel.Font = New Font("Microsoft sans serif", 12)
            newLabel.Dock = DockStyle.Fill
            newLabel.TextAlign = ContentAlignment.MiddleLeft
            newLabel.Visible = True
            ChatContactList.Controls.Add(newLabel)

        End If

    End If

End While

Image of my contactlist

enter image description here

Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
Thomas Dutoit
  • 129
  • 1
  • 12
  • Interesting. I remember suggesting a user control when you posted a bit ago. If you had a user control you could handle these sort of things easier... Then you can write methods to handle anyone of these... This means you should create a user control that would house everything about that person... Also that's why your code above is long and asking for trouble. All that above could be cut in half and more. – Trevor Mar 07 '16 at 12:34
  • Here's my comment if you forgot. http://stackoverflow.com/questions/35722326/chat-contact-list-vb-net-and-mssql – Trevor Mar 07 '16 at 12:39
  • I agree with @Codexer, and I am not fond of UserControls in lieu of Custom Controls. As is, your form is just a bunch of unrelated controls showing related info. – Ňɏssa Pøngjǣrdenlarp Mar 07 '16 at 14:46
  • can't find usercontroll in my tools ?? i'm very new at this xd, do you have any example? – Thomas Dutoit Mar 08 '16 at 11:35
  • You have to add it to project. Right click solution then add new user control. – Trevor Mar 08 '16 at 12:04
  • okay, and now? like i said, i'm new at this so adding a user control isn't a solution to my problem i guess :) – Thomas Dutoit Mar 08 '16 at 14:59
  • Note: when more than one person has commented on your post, you need to use @+username (as I did in the third comment for Codexer) for them to get pinged. You get pinged for all comments because it is your post. [Creating a Windows Form User Control](https://msdn.microsoft.com/en-us/library/aa302342.aspx) – Ňɏssa Pøngjǣrdenlarp Mar 08 '16 at 15:13
  • @Plutonix, at first it just looks i have to build everything over again to get the same result. with the user control i have to (like my tablelayoutpanel) create a connection with sql, get all the exact same data, load the same bunch of codes end than load the user control in my tablelayoutpanel.. Isn't very useful i think. i have this feeling that it isn't clear that 'making a user control' (without explanation how to make it) isn't a solution unless i know how to build that. – Thomas Dutoit Mar 08 '16 at 15:25
  • It is *very* useful as it helps with [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) which the code posted suffers from. A UC (I am almost done building a sample) would help make the code OO and allow you to treat those entries as a single object with properties like name, Status (online, Afzweid etc) and so forth. You'd create one per Chatter and just tickle the properties thereafter. – Ňɏssa Pøngjǣrdenlarp Mar 08 '16 at 15:38
  • a visual example would be VERY useful in this case. i try to do as much without asking but sometimes it's very hard. – Thomas Dutoit Mar 08 '16 at 16:18

1 Answers1

2

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:

enter image description here

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:

enter image description hereenter image description here

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

Community
  • 1
  • 1
Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
  • Nice :) it works beautifull and you're right. less code is needed. @Plutonix – Thomas Dutoit Mar 10 '16 at 11:41
  • I'm still wrestling with some things @Plutonix. -AddHandler When i do `AddHandler C.Click, AddressOf Chatbox` then i can't click it. -Update How can i perform an update of these controls when they are made? These controls are dynamically generated. – Thomas Dutoit Mar 10 '16 at 11:55
  • Of course, I have no idea of the specifics of your implementation. I used a TLP so there is no area of the UC for the user to click. You can funnel the child control events though by mapping them to the UC handler `Private Sub Chatter_Click(sender As Object, e As EventArgs) Handles Me.Click, blChName.Click, pbMStatus.Click, pbUStatus.Click`. When you add them AddHandler would specify the ChatBox (the UC) click event. As the answer explains/shows, update by setting the related property of any of them. – Ňɏssa Pøngjǣrdenlarp Mar 10 '16 at 14:03
  • I should mention that the way I would **really** do this is with a single, fixed image for User and the Balloon which was a transparent PNG cutout to show the color behind. Rather than re-assigning images, I'd just change the back color of whatever is behind it. If it was a Label, I could add Text to it to represent the "?" for unknown and a numeral for the number of unread messages. With that approach, you'd quickly find MsgStatus is not needed - the state can be derived from the Number of unread msgs – Ňɏssa Pøngjǣrdenlarp Mar 10 '16 at 23:58
  • like you made it is perfect but the onclick is a problem. I tried different things like c.click, c.lblChName.name but nu luck at all – Thomas Dutoit Mar 11 '16 at 14:15
  • The Click event for a UC is no different, use AddHandler to connect the event to an existing event handler in your form. The issue is that - at least the way mine is set up - *there is no UC surface for the user to click on*. You have to map/funnel/route the label and picbox clicks in the UserControl code. Give it a try and ask a new question if you cant get it to work. – Ňɏssa Pøngjǣrdenlarp Mar 11 '16 at 14:27
  • Never mind onclick works :) 1 problem left Updating a Chatter image – Thomas Dutoit Mar 11 '16 at 15:13
  • See: http://i.imgur.com/SEmHF7q.jpg Change the PicBoxes to labels so you can use the TEXT property to display the number of msgs (dont need the enum then), Load the images from resources and change the backcolor to use `SystemColors.Window` to respect the user theme rather than just the UC BG. Looks better, IMO. The user image should change when you change the property – Ňɏssa Pøngjǣrdenlarp Mar 11 '16 at 15:13
  • I don't need a count of unread messages. i want do update `c = New Chatter(4, "Codexer") c.MsgStatus = Chatter.ChatMsgStatus.New c.Status = Chatter.ChatStatus.Away` Like if its set to away, update to online – Thomas Dutoit Mar 11 '16 at 15:22
  • thats almost identical to the code in the answer and the code used to get the result in the image I just posted...whats not working? – Ňɏssa Pøngjǣrdenlarp Mar 11 '16 at 15:26
  • **What** are you trying to do? Change the status of one of the existing chat users? If so change the `Status` for that chat control, dont create a new one. These are objects, you just need to get a reference to the existing one and change the property – Ňɏssa Pøngjǣrdenlarp Mar 11 '16 at 15:33
  • well that's the problem. i tried different things but i cant find the correct syntax to update an existing object – Thomas Dutoit Mar 14 '16 at 09:03
  • @plutonix i tried to connect with an existing object with chatID, chname but without succes – Thomas Dutoit Mar 14 '16 at 09:42
  • a) either save a reference to each control with the related User data when you create them or b) find the control in the controls collection then `c.Status = newStatus` Method A is better but I have no idea how you have the user data stired – Ňɏssa Pøngjǣrdenlarp Mar 14 '16 at 13:49
  • what i do now is this `While UserData.Read Dim CPID As String Dim CPUN As String CPID = UserData("Username").ToString CPUN = UserData("Voornaam").ToString & " " & UserData("Achternaam").ToString Dim CP As New Contacts(CPID, CPUN) CP.Name = CPID CP.ContactName.Name = CPID AddHandler CP.ContactName.Click, AddressOf Chatbox.......` – Thomas Dutoit Mar 15 '16 at 09:07
  • so i user t.dutoit as ChatID @plutonix – Thomas Dutoit Mar 15 '16 at 12:57