A common question that is often asked in VB forums is how to create a LogIn system where my users can Log in to my application. This should include help for forgotten passwords, and the ability to add a new user account to the database.
In this example, I have encapsulated all of those requirements. Figure 1 shows the main LogIn screen.
Figure 1. The LogIn screen
Figure 2 shows the Password reminder screen, where you can get a Password reminder hint.
Figure 2. Password hint screen.
The ability to add new User Accounts in a LogIn system is often a useful feature. Figure 3 shows the Add New screen with Password strength meter.
Figure 3. Password strength meter.
In general use within a application, you would want to progress to another form after logging in, but this example just progresses to another Panel. (See Figure 4).
All of the screenshots in this article show Panels piled up one above another, and brought to the top of the z-order as necessary.
Figure 4. Success
For data access in this example I used an Access 2007 database, and all of the database interaction takes place in the dataLayer class:
Imports System.Data.OleDb
Public Class dataLayer Private Shared conn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Login.accdb;Persist Security Info=False;")
Private Shared Sub connect() conn.Open() End Sub
Private Shared Sub disconnect() conn.Close() End Sub
Public Shared Function verify(username As String, password As String) As Boolean connect() Dim cmd As New OleDbCommand("SELECT COUNT(*) FROM tblAccounts WHERE username=@username AND password=@password", conn) cmd.Parameters.AddWithValue("@username", username) cmd.Parameters.AddWithValue("@password", password) Dim o As Object = cmd.ExecuteScalar disconnect() Return CBool(o) End Function
Public Shared Sub addNew(username As String, password As String, hint As String) connect() Dim cmd As New OleDbCommand("INSERT INTO tblAccounts ([username], [password], [hint]) VALUES(@username, @password, @hint)", conn)
cmd.Parameters.AddWithValue("@username", username) cmd.Parameters.AddWithValue("@password", password) cmd.Parameters.AddWithValue("@hint", hint) cmd.ExecuteNonQuery() disconnect() End Sub
Public Shared Function getHint(username As String) As String connect() Dim cmd As New OleDbCommand("SELECT hint FROM tblAccounts WHERE username=@username", conn) cmd.Parameters.AddWithValue("@username", username) Dim o As Object = cmd.ExecuteScalar disconnect() If o IsNot Nothing Then Return o.ToString Else Return ("User doesn't exist.") End If End Function
End Class
There are three Public methods, two of which are Functions, and the other is a Sub-Procedure.
The first Function verify checks if a user with the supplied name and password exists in the database, returning a Boolean True or False value.
The second function getHint queries the database and returns a password reminder for a given username.
The Sub-Procedure addNew adds a new user to the database.
In a real world application, I wouldn't recommend saving passwords as plaintext. There are encryption methods available which would enable you to mask passwords in the database. Here are several options: http://msdn.microsoft.com/en-us/library/system.security.cryptography.hashalgorithm(v=vs.80).aspx http://msdn.microsoft.com/en-us/library/as0w18af(v=vs.110).aspx http://www.scproject.biz/secureCipher.php In addition you may wish to only allow an administrator to create new accounts.
Often on official websites when creating a password, you’ll be shown a meter that gauges the strength of your password. There are also sites where you can test a password, such as The Password Meter at: http://www.passwordmeter.com/. This is my version of that meter.
The meter is a custom control created by extending the Label control.
Imports System.Text.RegularExpressions
Public Class passwordMeter Inherits Label
Private StrengthRatings() As String = {"Extremely weak", "Very Weak", "Weak", "Medium", "Strong", "Very Strong"} Private associatedEditControl As TextBox Private password As String
Private _score As Integer = 0 Public Property score() As Integer Get Return _score End Get Set(ByVal value As Integer) _score = value 'repaint control MyBase.Invalidate() End Set End Property
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) If score > 0 Then e.Graphics.FillRectangle(New SolidBrush(GetColor(score)), New Rectangle(0, 0, score * 2, MyBase.ClientSize.Height)) End If MyBase.OnPaint(e) End Sub
Private Function GetColor(ByVal score As Integer) As Color Select Case score Case 0 Return Color.FromArgb(220, 150, 150) Case 10 Return Color.FromArgb(230, 185, 185) Case 20 Return Color.FromArgb(200, 190, 220) Case 30 Return Color.FromArgb(235, 240, 220) Case 40 Return Color.FromArgb(215, 230, 190) Case 50 Return Color.FromArgb(195, 215, 155) End Select End Function
'setup handler Public Sub bindTextbox(ByVal tb As TextBox) associatedEditControl = tb AddHandler associatedEditControl.KeyUp, AddressOf tb_KeyUp End Sub
Private Sub tb_KeyUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) password = associatedEditControl.Text CalculateMeter() End Sub
Private Sub CalculateMeter() Dim passwordScore As Integer = 50
'Length more than 8? If (password.Length < 8) Then passwordScore -= 10
'all lower case? If Regex.IsMatch(password, "^[a-z]+$") Then passwordScore -= 10 End If
'all upper case? If Regex.IsMatch(password, "^[A-Z]+$") Then passwordScore -= 10 End If
'all digits? If Regex.IsMatch(password, "^\d+$") Then passwordScore -= 10 End If
'contains digit? If Not Regex.IsMatch(password, "\d") Then passwordScore -= 10 End If
'contains lower case char? If Not Regex.IsMatch(password, "[a-z]") Then passwordScore -= 10 End If
'contains upper case char? If Not Regex.IsMatch(password, "[A-Z]") Then passwordScore -= 10 End If
'contains special char? If Not Regex.IsMatch(password, "[!,@,#,$,%,^,&,*,?,_,~,-,/""]") Then passwordScore -= 10 End If
'Length more than 4? If (password.Length < 5) Then passwordScore -= 10
passwordScore = Math.Max(passwordScore, 0)
'get strength rating from array MyBase.Text = If(associatedEditControl.Text.Trim <> "", StrengthRatings(passwordScore \ 10), "") 'change score property score = passwordScore
End Sub
This control is always used in association with a TextBox and has a bindTextBox method to establish that connection, which it uses in an encapsulated handler when the TextBox KeyUp message is received.
As a key is released while entering a password, the strength of the password is calculated in the CalculateMeter method and the strength rating is shown in the Label text, and also the BackColor of the Label control is painted with a color dependent on the score property for a percentage of its width.
The Form
The Form in this example uses many more controls than it would appear at a first glance. This is because the controls are all contained in four Panels, three of which are always hidden behind the topmost Panel.
The code used in the Form is very simple.
Public Class frmLogin
Private Sub frmLogin_Load(sender As Object, e As EventArgs) Handles MyBase.Load PasswordMeter1.bindTextbox(pnlNew_txtPassword) End Sub
Private Sub pnlLogIn_btnNew_Click(sender As Object, e As EventArgs) Handles pnlLogIn_btnNew.Click 'new clear_pnlNew() pnlNew.BringToFront() 'username pnlNew_txtUserName.Focus() End Sub
Private Sub pnlLogIn_btnForgot_Click(sender As Object, e As EventArgs) Handles pnlLogIn_btnForgot.Click 'forgot clear_pnlHint() pnlHint.BringToFront() 'username pnlHint_txtUserName.Focus() End Sub
Private Sub pnlSuccess_btnLogOut_Click(sender As Object, e As EventArgs) Handles pnlSuccess_btnLogOut.Click 'logout clear_pnlLogIn() pnlLogIn.BringToFront() 'username pnlLogIn_txtUserName.Focus() End Sub
Private Sub pnlLogIn_btnLogIn_Click(sender As Object, e As EventArgs) Handles pnlLogIn_btnLogIn.Click If dataLayer.verify(pnlLogIn_txtUserName.Text, pnlLogIn_txtPassword.Text) Then showUserName(pnlLogIn_txtUserName.Text) pnlSuccess.BringToFront() 'logout pnlSuccess_btnLogOut.Focus() Else MsgBox("Invalid UserName or Password") End If End Sub
Private Sub pnlHint_btnHint_Click(sender As Object, e As EventArgs) Handles pnlHint_btnHint.Click Dim hint As String = dataLayer.getHint(pnlHint_txtUserName.Text) If hint <> "User doesn't exist." Then lblHint.Text = hint pnlHint_txtPassword.Focus() Else MsgBox(hint) End If End Sub
Private Sub pnlHint_btnLogIn_Click(sender As Object, e As EventArgs) Handles pnlHint_btnLogIn.Click If dataLayer.verify(pnlHint_txtUserName.Text, pnlHint_txtPassword.Text) Then showUserName(pnlHint_txtUserName.Text) pnlSuccess.BringToFront() 'logout pnlSuccess_btnLogOut.Focus() Else MsgBox("Invalid UserName or Password") End If End Sub
Private Sub pnlNew_btnLogIn_Click(sender As Object, e As EventArgs) Handles pnlNew_btnLogIn.Click 'addNew If pnlNew_txtUserName.Text.Trim <> "" AndAlso pnlNew_txtPassword.Text.Trim <> "" AndAlso pnlNew_txtReenter.Text.Trim <> "" AndAlso pnlNew_txtPassword.Text = pnlNew_txtReenter.Text Then dataLayer.addNew(pnlNew_txtUserName.Text, pnlNew_txtPassword.Text, pnlNew_txtHint.Text) showUserName(pnlNew_txtUserName.Text) pnlSuccess.BringToFront() 'logout pnlSuccess_btnLogOut.Focus() End If End Sub
Private Sub clear_pnlHint() pnlHint_txtUserName.Clear() pnlHint_txtPassword.Clear() lblHint.Text = "" End Sub
Private Sub clear_pnlLogIn() pnlLogIn_txtUserName.Clear() pnlLogIn_txtPassword.Clear() End Sub
Private Sub clear_pnlNew() pnlNew_txtUserName.Clear() pnlNew_txtPassword.Clear() pnlNew_txtReenter.Clear() pnlNew_txtHint.Clear() End Sub
Private Sub showUserName(username As String) lblLoggedInAs.Text = "Logged in as: " & username lblLoggedInAs.Left = (pnlSuccess.ClientSize.Width - lblLoggedInAs.Width) \ 2 End Sub
As some of the Buttons and TextBoxes used across the four Panels have duplicated or similar purposes i’ve used a naming convention where controls are named [Panel name]_[Control name].
Download here (VB2013)