Tutorial: MSSQL- Windows Form Development
Multi-User Application With Custom Record Lockout #3
[Login, RecoordLock DB, Authentication, Configure Dynamic Data Combo, Password Fields, CheckBoxes, & Tab Control]
Dr. Thomas E. Hicks
Computer Science Department
Trinity University
The purpose of this series of tutorials is to show how to
configure a Windows Application Form, using Visual Studio, to process a
multi-form, multi-user network application. This tutorial will briefly review
many of the concepts demonstrated in some of the other tutorials.
Prerequisite Condition: each of our major database tables must have a unique,
auto-incremented, integer field called ID.
This portion of the tutorial will examine and configure a combobox filled by a database table, checkbox controls, password textboxes, and tabcontrol configuration.
This tutorial is fourth in a series of
Windows Form Development Multi-User Application With Custom Record Lockout #1 [Database Configuration, Database Authentication, Generic Database Utilities, Etc.]. The topics in part 1 are as follows:Part I-A:
Part I-C:
Part I-D:
Part I-E
Part I-F:
Part I-G:
Part I-H:
This tutorial is fourth in a series of Windows Form Development Multi-User Application With Custom Record Lockout #2 [Query Set Views & Navigation Buttons & Filters & Order By]. The topics in part 2 are as follows:
Part II-A:
Part II-B
Part II-C:
Part II-D:
Part II-E:
Part II-F:
Part II-G
Part II-H:
Part II-I:
Part II-J:
Part III-A: Use ComboBoxes To Reduce Data Entry Errors [Database Enables Dynamic ComboBoxes]
Part III-B: Use CheckBoxes To Reduce Data Entry Errors
Part III-C
: Hide The Password DataPart III-D:
Configuring Tab Control For The User FormPart III-E:
LibraryApp AuthenticationPart III-F:
Login ProcessingPart III-G:
Carriage Return/Enter Form ProcessingPart III-H: Final Testing
Part III-I: Temporary Main.cs Change For Rapid Development
Part III-J:
For You To Do
If You Have Difficulty With This Tutorial, You Might Find These Help
Prepare You For This Tutorial
I Recommend That My Students Complete The Tutorials In This Order!
Tutorial: MSSQL-Forms - Windows Application & Basic Form Properties
Tutorial: MSSQL-Forms - Windows Application - Labels & Textboxes
Tutorial: MSSQL-Forms - Windows Application - Buttons
Tutorial: MSSQL-Connection\MSSQL-Connections
Tutorial: MSSQL-Forms - Windows Application - Menu - Multiple Forms
Tutorial: MSSQL-Visual Studio MenuStrip Application With Multiple Windows
1] Copy folder Library-Net-Multi-User-2 to folder Library-Net-Multi-User-3. I always recommend making a backup copy about ever hour or so.
1] It goes without saying that a combobox would be better for the MajorID selection. Why should the user have to look up the number that is appropriate for the respective major? Why not avoid the problem that occurs when a user enters the MajorID incorrectly?
2] We could enter all of the major choices directly into the combobox; this means that the code would have to be altered if the selection of majors either increases or decreases. Using the database to fill the combobox is a much more professional choice; changes can be made to the database and your form will always be updated each time the Users form is created.
1] Using the mouse, drag a ComboBox control from the Toolbox menu to your Users form. (See Below!)

1] Using the mouse, expand the ComboBox control. (See Below!)

2] Check the Use data bound items. (See Below!)

3] Hold down the Data Source drop-down control. (See Below!)

4] Select Add Project Data Source. (See Below!)

5] Using the mouse, select the Next button. (See Below!)

6] Using the mouse, select the New Connection.. button. (See Below!)

7] Select Microsoft SQL Server. Using the mouse, select the Continue button. (See Below!)

8] Enter the name SQL Server Data Source (NET1\SQLEXPRESS for me). Enter, or select, LibraryApp. Using the mouse, select the Test Connection button. (See Below!)

9] Using the mouse, select the OK button. (See Below!)





2] Using the mouse, select the Display Member drop-down control. (See Below!)

3] Select Major as the Display Member. (See Below!)








3] Delete txtMajorID from the Users form and slide cbMajorID up in it's place. Aldo delete the line of code associated with filling txtMajorID. (See Below!)

5] Execute the form again. Note the choices that can be selected with ComboBox cbMajorID. (See Below!)


1] ComboBox controls should be used to reduce data entry problems whenever possible; if we can feed the choices with a database, all the better. If we were to have a text field for Major, then users might enter cs, CS, Computer Science, Comp Sci, computer science, comp sci, etc. for the field; in addition to abbreviations, and case differentially, some of the users will spell things incorrectly. This lack of consistency would make searching and order by difficult and inadequate at best.
2] In
this same train of thought, something needs to be done to field
txtAdministrator; among the things users might enter would be Y, y,
N, n, T, t, F, f, True, true, False, false, yes, Yes, no, No, YES,
NO, etc. We could use a Yes or No radio button. We could use a Yes
or No ComboBox control. Since we have already demonstrated a
combobox control, I shall use a checkbox control to replace
txtAdministrator.
1] In an effort to reduce data entry problems, we shall use a checkbox control to replace txtAdministrator.
1] Using the mouse, drag a CheckBox control from the Toolbox menu to your Users form. (See Below!)



1] Change FillFormVariables to place a check mark for administrators. (See Below!)

2] Execute the program. Note that Tom Hicks is an administrator. Administrator = T and the box is checked. (See Below!)

3] Also note that Noreen Gilbert is not an administrator. Administrator = F and the box is not checked. (See Below!)

4] Examine the database and confirm that the checkbox is working properly for all users. (See Below!)
5] Change FillFormVariables; remove the assignment for txtAdministrator. (See Below!)

6] Remove txtAdministrator from the form and shift cbAdministrator into it's place. (See Below!)

1] We now
know that the password data is properly loaded into our form; it is time to
secure the contents.
1] Select the Properties of the field txtPassword; change the PasswordChar to *. (See Below!).

2] You can see that the Password is a bit more secure, but knowing that it is four characters often reduces the security potential; I personally tend to blank pad the password, programmatically, to 20 characters to increase security. When comparing the password, for login purposes, it is quite simple to strip those trailing blanks. (See Below!).

1] It is tab order that is used to determine which control gets focus. Focus may be transferred to and from input fields, buttons, radio buttons, tabs, checkboxes, and comboboxes; we call this tab order because you can change focus from control to control using the tab key on the Windows form.
2] Some type of tab order is an important aspect in form usability.
3] By default, Tab order for individual controls is usually set to true, thus making the control usable.
4] Tab order can be set with the properties control or through program code.
5] The initial
tab order is determined by the order in which the controls are created on
the form.
1] Your tab order may well differ from mine. When I run the application and place the cursor in field txtFirst and hit the tab key,

2] I am transferred to field txtLast; when hit the tab key,

3] I am transferred to field txtUserName; when hit the tab key,

4] I am transferred to field txtInitial; when hit the tab key,

5] I am transferred to field txtPassword; when hit the tab key,

6] I am transferred to button btnTesting; when hit the tab key,

7] I am transferred to field txtMajorID etc.

1] A form might include a Delete button. Suppose the tab control arrives on the delete button; this would make it easy for the user to hit the enter key and accidently delete a record. By and large I do not want buttons included it the Tab Control.
2] Ideal Tab Control ==> I would like my ideal tab control to process from First to Initial to Last to UserName to Password to Gender to Administrator and then back to First.
3] Using the mouse, hold down the View Menu and select Tab Order. (See Below!)

4] The yellow lines below help to graphically illustrate the current Tab Order.

1] Select the btnA. Change the TabStop Property to False so that the button is not included in the Tab Control sequence. (See Below!)

2] Do likewise for each and every button. Set the TabStop Property to False for all buttons.
3] Do likewise for comboboxes cbOrderBy and cbSelect. Set the TabStop Property to False for these two comboboxes.
4] Do likewise for lbTrace. Set the TabStop Property to False for lbTrace.
1] Note that those items, that had the TabStop Property set to False , still appear (and are numbered), in the tab control list below.
2] Each item on the form has a potential tab control associated with it; some of these items, such as the labels, may not be tabbed.
3] The TabControl will have a number assigned to it; My current number is 95, but yours might be different. Using the mouse, touch the TabControl number one time. (See Below!)

3] The TabControl will now have the number 0 assigned to it. (See Below!)

4] Using the mouse, touch the number associated with txtFirst (mine is 0.3.0). (See Below!)

5] The value did not change, but yours may have changed. TxtFirst is now the first field in our ideal tab solution. The number associated with txtFirst is 0.3.0. (See Below!)

6] Using the mouse, touch the number associated with txtInitial (mine is 0.3.1). (See Below!)

7] The value did not change, but yours may have changed. TxtFirst is now the second field in our ideal tab solution. The number associated with TxtFirst is 0.3.1. (See Below!)

8] Using the mouse, touch the number associated with txtLast (mine is 0.3.3). (See Below!)

9] The value did change, but yours may not have changed. TxtLast is now the third field in our ideal tab solution. The number associated with TxtLast is 0.3.2. (See Below!)

10] Using the mouse, touch the number associated with txtUserName (mine is 0.3.7). (See Below!)

11] The value did change, but yours may not have changed. txtUserName is now the fourth field in our ideal tab solution. The number associated with txtUserName is 0.3.3. (See Below!)

12] Using the mouse, touch the number associated with txtPassword (mine is 0.3.11). (See Below!)

13] The value did change, but yours may not have changed. txtPassword is now the fifth field in our ideal tab solution. The number associated with txtPassword is 0.3.4. (See Below!)

14] Using the mouse, touch the number associated with cbGenderID (mine is 0.3.17). (See Below!)

15] The value did change, but yours may not have changed. cbGenderID is now the sixth field in our ideal tab solution. The number associated with cbGenderID is 0.3.5. (See Below!)

16] Using the mouse, touch the number associated with ckAdministrator (mine is 0.3.18). (See Below!)

17] The value did change, but yours may not have changed. ckAdministrator is now the sixth field in our ideal tab solution. The number associated with ckAdministrator is 0.3.6. (See Below!)

17] It is time to store/save this configuration. Using the mouse, hold down the View Menu and select Tab Order. Save the form. (See Below!)

1] As you prepare to execute the form for final testing, note that all tab controls colored with turquoise (in the image below) have the TabStop Property to False.
2] As you prepare to execute the form for final testing, note that all tab controls colored with lime green (in the image below) are not important.
3] As you prepare to execute the form for final testing, note that all tab controls colored with blue (in the image below) have the tab stops configured with respect to our ideal tabl sequence. Test it! Make changes if necessary!

1] We are going to use SQL Server accounts for all login.
2] We are going to create a second database, called RecordLock, to store record locks for all tables and all databases.
3] The directions below will help you create database RecordLock.
1]Using Microsoft SQL Management Studio Express, create a database called RecordLock.
2] Add a table, called Locks, to database RecordLock.
3] Add the fields below to table Locks!

4] Add a table, called Users, to database RecordLock.
5] Add the fields below to table Users! Note that UserID is an auto-incrementing primary integer key.

1] Add a user, called RecordLockAdmin, whose default database shall be RecordLock. (See Below!)
2] User Map RecordLockAdmin to the RecordLock database. (See Below!)
3] Enable User RecordLockAdmin to Connect, Authenticate, Update,
Insert, Delete, and Select. (See Below!)
4] We shall use user RecordLockAdmin to read, write, and update
locks on our database. This is much simpler, and more secure,
than granting such privileges to each and every user. (See Below!)
1] We are about to work on our Login.cs form. A SQL Server database will have a collection of valid users. You should have set up student in Part II of this tutorial set. You can see a screen capture of the Permissions tab when looking at the properties of my LibraryApp below.
2] I have three users (meggen, student, and thicks) that have Authenticate and Connect privileges on my database. (See Below!)

3] Only meggen, student, and thicks shall be able to login with our login form.

4] Each user will have only those privileges asserted in the database; if meggen has update permission, he will be able to update a user record or a book record. If thicks has insert permission, he will be able to add a new book or user.
5] When one user needs access to a record immediately, it is great to know who has the record locked and how to get in touch with that person. Each SQL User who shall be able to use a multi-user application shall have an entry in the Users table of database RecordLock.
6] For each SQL User who has access to LibraryApp

add a corresponding record to RecordLock => Users which might provide

6] The RecordLock database may well have users, such as sa, who do not have access to the LibraryApp database; that is ok. The RecordLock dat
6] We will discuss locking records much more in depth in Part IV of this tutorial set, but a couple of comments are in order.
7] When one user chooses to edit a record, they shall be granted a lock on that record for some fixed period of time; other uses requesting that record shall be placed in a queue and granted access accordingly.
8] There will be times when a user absolutely needs control at a record immediately. When a user requests a locked record, we are going to tell that user the name, phone, and office information of the person who currently has the record locked; this might well provide a way to call, or visit, the person who has the record locked and ask them to release it.
9] For testing purposes, make sure that you have at least two users with access to your LibraryApp Database; I encourage you to add three or four users.
1] The SQL User system shall authenticate our users. In order for a user to log in, he/she must have a valid SQL username and password.
2] After the user is authenticated with the LibraryApp, the
UserID, for that user, will be read from the RecordLock database. We shall
use that UserID in Part IV.
1] Add LoggedIn and LoginUserID to form MainMenu.cs.

1] Using the mouse, double click on btnLogin to create the btnLogin_Click event.. (See Below!)
2] Add the following code to function btnLogin_Click on the Login.cs form. (See Below!)
3]
Note that there are two Message.Box.Show (Green in color) that are
commented out in the code above. The first one should reveal the
number of the users in the LibraryApp Users table. The second should
reveal the UserID, from the RecordLock Users table, of the person
who is logging into the system. Uncomment both of these! 4] Execute the LibraryApp system. Launch the Login
form. Test the form with both valid and invalid data. Once it is
working properly, re-comment out the same two MessageBox.Show lines
of code. 

1] We would ultimately like our application to start by automatically bringing up the Login Form so that a user can log in. Change your MainMenu_Load to be as follows.

2] It is only after a user has successfully logged in that we would like the user to be able to bring up the Users form. Change your MainMenu_Load to be as follows.

3] It is only after a user has successfully logged in that we would like the user to be able to bring up the Books form. Change your MainMenu_Load to be as follows.
4] Execute the LibraryApp system. Test it! Only logged in users
should be able to access forms Books and Users. 1] Execute the LibraryApp system. Note that in order
to get from txtUserName to txtPassword, you have to either hit the
tab key or use the mouse to change fields. Most users would also
like to be able to advance by hitting the Enter (Carriage Return)
Key. 2] Use the process described above to set the tab
order for the Login form to match that below!

1] Many uses solve this by creating an KeyPressed event for each and every textbox on a form and inserting code similar to that below. I encourage you to try this. It does work, but it is time consuming when a form has dozens of textboxes.
2] Using the mouse, touch field txtUserName on form
Login.cs. Go to the Properties Panel. Touch the Event
Manager at the top of the
Properties Panel. Using the mouse, double-click on the KeyDown event
property. (See Below!) Do the same for txtPassword. This
will create a keydown event for both txtUserName and txtPassword.

3] When you add the code below, pressing the enter key in either of these two boxes will have the same effect as pressing the tab key.

4] Execute the LibraryApp system. Enter UserName = student (note I have colored the cursor red). Hit the Enter Key.

5] Note that he cursor (red) has moved to the next tab field (which is txtPassword).

6] Enter Password = student (note I have colored the cursor red). Hit the Enter Key.

7] Note that the focus has moved to the next tab field (which is the btnLogin).

1] I personally prefer a solution that does not require changes to each and every textbox.
2] Using the mouse, touch form Login.cs (not
on one of the controls). Go to the Properties Panel. Touch the
general Properties
Manager at
the top of the Properties Panel. Using the mouse, change the
KeyPreview property to True. (See Below!)

3] Using the mouse, touch form Login.cs (not on one
of the controls). Go to the Properties Panel. Touch the Event
Manager at the top of the
Properties Panel. Using the mouse, double-click on the KeyPress
event property. (See Below!) This will create a
keypressed event for entire form.

4] Remove the code you previously placed in txtUserName_KeyDown and txtPassword_KeyDown.
5] Change the Login_KeyPress code to be as follows:

6] Execute the LibraryApp system. Enter UserName = student (note I have colored the cursor red). Hit the Enter Key.

7] Note that he cursor (red) has moved to the next tab field (which is txtPassword).

8] Enter Password = student (note I have colored the cursor red). Hit the Enter Key.

9] Note that the focus has moved to the next tab field (which is the btnLogin).

10] Our system is now
much more professional and user friendly. 1]
2
3] Open form Users.cs. Write the code for function TestModule24. (See
Below!) 4
5] Make the following changes (in red) in function TestinAll on form
Users.cs.
6] Execute the LibraryApp system. Enter UserName = student
(Hit the Enter Key). Enter Password = student
(Hit the Enter Key or Push btnLogin). (See Below!)
7] Launch the Users form (using the mouse, hold down the Users Menu and
select ViewUsers). Push btnTesting. (See Below!)
8] Variable LoggedIn, on the MainMenu form, has been successfully set to
true. As you look at a snapshot of the Users table of database RecordLock
(below), you can see that strLoginUserID, on the MainMenu form, has been
correctly set to 4.
9] Check the other user. Re-Launch the Login form. Enter UserName =
???? [thicks for me] (Hit the Enter Key). Enter
Password = ???? (Hit the Enter Key or Push btnLogin).
(See Below!)
7] Once again push btnTesting on form Users. Variable LoggedIn, on the
MainMenu form, is still set to true. As you look at a snapshot of the
Users table of database RecordLock (above), you can see that strLoginUserID, on
the MainMenu form, has been correctly set to 2. 1] Our Login form is completed.
2]
3] During this development and testing phase, we do not want to bother logging
in each time we wish to test new functions. 4] Change MainMenu_Load to the
following. Assign your StrLoginUserID to be that number that corresponds with
your student UserID (as assigned in the RecrodLock Users database table) of your
system. 5]
Execute the LibraryApp system.
Push btnTesting. (See Below!)
6] Things will now proceed
logically as if student had logged in. The Users table is automatically launched
for testing. 7] You can make the necessary
changes to reverse this testing process when the project is completed.
8] Of course you can
skip these changes and login each time if you prefer?



//================================================================================//
// TestingMaster //
//================================================================================//
// Purpose: Testing Master which evokes TestModule1, TestModule2, ... //
// //
// Written By : Dr. Thomas E. Hicks Environment : .NET 2005/8 //
// Date : xx/xx/xxxx Language : C# //
//================================================================================//
public void TestingMaster()
{
int UserClassDiagnosticLevel = 24;
if (UserClassDiagnosticLevel == 0)
TestAll();
if (UserClassDiagnosticLevel == 1)
TestModule1();
if (UserClassDiagnosticLevel == 2)
TestModule2();
if (UserClassDiagnosticLevel == 3)
TestModule3();
if (UserClassDiagnosticLevel == 4)
TestModule4();
if (UserClassDiagnosticLevel == 5)
TestModule5();
if (UserClassDiagnosticLevel == 6)
TestModule6();
if (UserClassDiagnosticLevel == 7)
TestModule7();
if (UserClassDiagnosticLevel == 8)
TestModule8();
if (UserClassDiagnosticLevel == 9)
TestModule9();
if (UserClassDiagnosticLevel == 10)
TestModule10();
if (UserClassDiagnosticLevel == 11)
TestModule11();
if (UserClassDiagnosticLevel == 12)
TestModule12();
if (UserClassDiagnosticLevel == 13)
TestModule13();
if (UserClassDiagnosticLevel == 14)
TestModule14();
if (UserClassDiagnosticLevel == 15)
TestModule15();
if (UserClassDiagnosticLevel == 16)
TestModule16();
if (UserClassDiagnosticLevel == 17)
TestModule17();
if (UserClassDiagnosticLevel == 18)
TestModule18();
if (UserClassDiagnosticLevel == 19)
TestModule19();
if (UserClassDiagnosticLevel == 20)
TestModule20();
if (UserClassDiagnosticLevel == 21)
TestModule21();
if (UserClassDiagnosticLevel == 22)
TestModule22();
if (UserClassDiagnosticLevel == 23)
TestModule23();
if (UserClassDiagnosticLevel == 24)
TestModule24();
}
//================================================================================//
// TestingAll //
//================================================================================//
// Purpose: Test all of the functions: TestModule11, TestModule12, ... //
// //
// Written By : Dr. Thomas E. Hicks Environment : .NET 2005/8 //
// Date : xx/xx/xxxx Language : C# //
//================================================================================//
public void TestAll()
{
TestModule11();
TestModule12();
TestModule13();
TestModule14();
TestModule15();
TestModule16();
TestModule17();
TestModule18();
TestModule19();
TestModule20();
TestModule21();
TestModule22();
TestModule23();
TestModule24();
}








1] This is your opportunity to put into practice what you have learned. Do the Following!
1] Configure a combobox, called cbGenreID to be dynamically filled by the Database Table Genre.

2] Configure the ideal TabOrder to proceed from Title to Author to Price to
Genre and back to Title. Those items that are turquoise
should have TabStop = false. Those items
that are Navy
should have TabStop = true.
Those items that are lime green are not important.

3] Configure the Enter Key to process the TabOrder.
1] Configure the Enter Key to process the TabOrder.