Overview





This is a graphical Custom Control that extends the available ToolBox items in VB.Net.
Fortunately it is very easy to create, compile, and distribute your own Custom Controls these days.



How-To



This is a Custom Control that was written as a tutorial for those interested in writing their own Controls. It started out with a standard winforms application (the demo application).
From the File menu, choose Add-->New Project, then from the Dialog that appears, choose and name, a Class Library project (which provides you with an empty Class template - Class1). This adds a suitable project to the solution. This project will contain the StarRatingControl.

 You could also choose a Windows Forms Control Library project (which provides you with an empty UserControl template - UserControl1).

In this case the Class Library was utilized, deleting the default Class1, then adding a new UserControl, naming it StarRatingControl.






The StarRatingControl is composed of a UserControl, containing five PictureBoxes and one Label. The PictureBoxes contain (at runtime) either a grey star, a gold star, or a part grey, part gold star.





There are three UserControl views -

live

This view is when the user moves the mouse over the Control.

clicked

This view is when the user has clicked one of the UserControl PictureBoxes whilst in live view. In this view the UserControl remains frozen until the user moves the mouse outside of the UserControl.

average

This view is where the stars display a graphical average of all ratings, and the label displays a textual average.
This is the view that displays when the user moves the mouse outside of the UserControl.




↑ Back to top

The StarRatingControl code




The code is simple, handling the MouseMove and MouseDown events for the PictureBoxes, and the MouseLeave event for the UserControl and all of the contained Controls. This is to facilitate changing the currentView variable to allow using the UserControl as an input Control, and also as a display Control.


Imports System.ComponentModel
 
<ToolboxBitmap(GetType(StarRatingControl), "star16x16.png")> _
Public Class StarRatingControl
 
    Private pictureboxes() As PictureBox
 
    Private Enum ControlView
        live
        clicked
        average
    End Enum
 
    Private currentView As ControlView = ControlView.live
    Private rating As Integer = -1
    Private ratings As New List(Of Integer)
 
    ''' <summary>
    ''' Default Constructor
    ''' </summary>
    ''' <remarks>Sets up class level array and PictureBox EventHandlers</remarks>
    Public Sub New()
 
        ' This call is required by the Windows Form Designer.
        InitializeComponent()
 
        ' Add any initialization after the InitializeComponent() call.
 
        pictureboxes = New PictureBox() {PictureBox1, PictureBox2, PictureBox3, PictureBox4, PictureBox5}
 
        For Each pb As PictureBox In pictureboxes
            AddHandler pb.MouseMove, AddressOf pb_MouseMove
            AddHandler pb.MouseDown, AddressOf pb_MouseDown
            AddHandler pb.MouseLeave, AddressOf pb_MouseLeave
        Next
 
        ratings.Add(0)
 
    End Sub
 
#Region "     selection handlers"
 
    Private Sub pb_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        Dim pb As PictureBox = DirectCast(sender, PictureBox)
        If currentView = ControlView.live Then
            Dim i As Integer = Array.IndexOf(pictureboxes, pb)
            For x As Integer = 0 To 4
                If x <= i Then
                    pictureboxes(x).Image = My.Resources.star_512
                Else
                    pictureboxes(x).Image = My.Resources.star_512_grey
                End If
            Next
        ElseIf currentView = ControlView.average Then
            currentView = ControlView.live
        End If
    End Sub
 
    Private Sub pb_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        Dim pb As PictureBox = DirectCast(sender, PictureBox)
        If currentView = ControlView.live Then
            If e.Button = Windows.Forms.MouseButtons.Left Then
                Dim i As Integer = Array.IndexOf(pictureboxes, pb)
                rating = i
                ratings(ratings.Count - 1) = rating + 1
                ratings.Add(0)
                currentView = ControlView.clicked
            End If
            changeStars()
        End If
    End Sub
 
#End Region
 
#Region "     mouseleave handlers"
 
    Private Sub StarRatingControl_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        If Not Me.Bounds.Contains(Me.ParentForm.PointToClient(MousePosition)) Then
            currentView = ControlView.average
            changeStars()
        End If
    End Sub
 
    Private Sub Label1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Label1.MouseLeave
        StarRatingControl_MouseLeave(sender, e)
    End Sub
 
    Private Sub pb_MouseLeave(ByVal sender As Object, ByVal e As EventArgs)
        StarRatingControl_MouseLeave(sender, e)
    End Sub
 
#End Region
 
    ''' <summary>
    ''' changeStars method
    ''' </summary>
    ''' <remarks>Handles display for fixed control views</remarks>
    Private Sub changeStars()
        If currentView = ControlView.clicked Then
            For x As Integer = 0 To 4
                If x <= rating Then
                    pictureboxes(x).Image = My.Resources.star_512
                Else
                    pictureboxes(x).Image = My.Resources.star_512_grey
                End If
            Next
            Label1.Text = String.Format("Average {0:n2} over {1} ratings", ratings.Sum / (ratings.Count - 1), ratings.Count - 1)
        ElseIf currentView = ControlView.average Then
            If ratings.Count = 1 Then
                Label1.Text = "No ratings yet"
                For x As Integer = 0 To 4
                    pictureboxes(x).Image = My.Resources.star_512_grey
                Next
                Return
            End If
 
            Dim i As Integer = CInt(Math.Floor(ratings.Sum / (ratings.Count - 1))) - 1
            For x As Integer = 0 To 4
                If x <= i Then
                    pictureboxes(x).Image = My.Resources.star_512
                Else
                    pictureboxes(x).Image = My.Resources.star_512_grey
                End If
            Next
            If i < 4 AndAlso (ratings.Sum / (ratings.Count - 1) - (i + 1)) <> 0 Then
                Dim img1 As Bitmap = My.Resources.star_512
                Dim img2 As Bitmap = My.Resources.star_512_grey
                Dim gr As Graphics = Graphics.FromImage(img2)
                Dim r As Rectangle = New Rectangle(0, 0, CInt(Math.Round((ratings.Sum / (ratings.Count - 1) - (i + 1)) * 25)), 25)
                gr.DrawImage(img1, r, r, GraphicsUnit.Pixel)
                pictureboxes((i + 1)).Image = img2
            End If
            Label1.Text = String.Format("Average {0:n2} over {1} ratings", ratings.Sum / (ratings.Count - 1), ratings.Count - 1)
        End If
    End Sub
 
End Class


↑ Back to top


Adding a ToolBoxBitmap






Finally, as this is a Control that can be added to your ToolBox in it's compiled state, a 16x16 image was added to the UserControl project, and set as an Embedded Resource. To use the image as the icon shown in the ToolBox, a ToolboxBitmap Attribute was added to the top of the StarRatingControl code.




Conclusion



This is a simple example that shows how a versatile, reusable, and distributable Custom Control can be made easily in VB.Net


↑ Back to top

Other Resources




Download Demo project here