Tutorial: MSSQL- Library App Form Control
Without GUI Connections


Dr. Thomas E. Hicks
Computer Science Department
Trinity University

 


Purpose

The purpose of this tutorial is to show how to configure a Windows Application Form, using Visual Studio without using delegates, to process next, previous, delete, top, bottom, order by, and alphabetical search button controls..


 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 TU Database-Construction Using Microsoft SQL Server

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

MSSQL-Forms - Windows Application - Web Browser

MSSQL-Windows-Forms-ComboBox.html


The Database

1]  You might want to download and add the following database on the server; call the database LibraryApp. You can use import from the Microsoft Access database

LibraryApp-Access

Tutorial: MSSQL-Server-From-2003-Access-DB\MSSQL-Server-From-2003-Access-DB

Tutorial: MS-SQL-Server-From-2007-Access-DB\MS-SQL-Server-From-2007-Access-DB

or you might want to try to import the LibraryApp database directly into Microsoft SQL Server using Microsoft SQL Server Management Studio Express.

LibraryApp

Tutrial: MSSQL Import-Export-Backup-MSSQL-Database

 

Microsoft SQL Server Setup

1] Our database is shown, below,  via Microsoft SQL Server Management Studio Express.  Note that our server, to which we shall connect, is NET1\SQLEXPRESS (See Below!)

2] There are a number of good ways to connect. We are going to use two connection string options. See the link below for an Internet link that most of us should keep bookmarked.

http://connectionstrings.com

3]  Since we shall connect with both Windows and SQL server authentication, it is important that you configure your SQL Server to authenticate both ways. An easy way to do this is to use Microsoft SQL Server Management Studio Express (Right mouse click on Net1\Express and  select properties-select Security page). (See Below!)

4]  Use Microsoft SQL Server Management Studio Express to create a user, called student whose password is student. (See Below!)

5] Double-click on the student account; select User Mappings. Use Microsoft SQL Server Management Studio Express to enable the student user to use the LibraryApp database. (See Below!)

6] Continue to use Microsoft SQL Server Management Studio Express. Right-mouse click on the LibraryApp database and select Properties. Select the Permissions Page. Add the student user if necessary. The student user certainly does not need all of the permissions, but for the sake of simplicity (and because I don't want this to be a tutorial on SQL Server), grant the student user all permissions. (See Below!)

7] SQL user student should now be able to use password student to authenticate with the LibraryApp database throughout this tutorial.
 


Library Application Shell

1] My students have been asked to complete some basic forms to support this tutorial. Firs is LibraryApp.cs, which uses a MenuStrip to control the main application. The lab description may be seen below.  LibraryApp.cs, is to look something like the following:

MSSQL-Library-App\MSSQL-Library-App-1 Lab

2] They were to create a basic Administrative login form called Login.cs. It is to look something like the following:

3] They were to also create a non-functional ViewUser vorm called Users.cs that looks something like the following: [We realize that the MajorID and Administrator should eventually be combo boxes and that the password might eventually be replaced by #'s.

To get rid of the close box, add the following function right below the function public Users()

        private const int CS_NOCLOSE = 0x200;
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ClassStyle = cp.ClassStyle | CS_NOCLOSE;
                return cp;
            }
        }

4] They were supposed to place all of the fields, from the Users table, in the first tab frame of the Users form.

5] Button Close should hide the Users Form. Their Users form was to include all of the buttons seen on the form above and all of the combo boxes seen on that form.

6] A collection of images was made available to the students. They included: Top , Bottom , Next , and Previous .

BitMaps.zip

7] Below you can see, in turquoise, the names I have associated with the various TextBoxes, lLabels, and ComboBoxes on my Users.cs form.

 


Purpose Of This Tutorial!

1] The purpose of this tutorial is to connect to our database, without guis, and add functionality to many of the buttons on the Users.cs form.

 


Include The System.Data.SqlClient NameSpace

1]  You must include the System.Data.SqlClient Namespace at the top of the Users.cs form.


 


Connect To The Database  With Windows Authentication

1] If you are working on your SQL Server, using Windows Authentication will probably be the easiest.

2] Using the mouse, double-click  someplace on your users form that has no objects; this will create a User_Load function that will be fired each time our form is loaded. We are going to place our temporary testing code in that function.

3] In accordance with ConnectionStrings.com, let us define our Connection String to load the Library database on Net1\SQLExpress. Note the need  to use the \\ escape sequence in the connection string. Create a string object, called ConnectionString, and fill it in accordance with your SQL Server Name.  Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet. (See Below!)

String ConnectionString = "Data Source=net1\\SQLExpress;Integrated Security=True;" +
                          "Initial Catalog=LibraryApp";

4] Create a connection object, called Conn.  Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet. (See Below!)

SqlConnection Conn = new SqlConnection(ConnectionString);

5] Create a string object, called Query.  We could read all of the User records into a record set and cycle through them. If we had thousands of records, this would take both time and memory. If this were a single user application with a small number of records, this might be ok.

We are designing an application strategy that will be extremely fast for small and large data sets. We are going to read only a single record at a time. We are about to design a record to read the Crisanto record (ID  = 1) from our Users table. Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet. (See Below!)

String Query = "Select * From Users where ID = 1";

6] We have established a connection and created a query command. We are now going to create a data adapter object, called da

The SqlDataAdapter holds the SQL commands and connection object for reading and writing data.  We are going to initialize it by passing our SQL Query and our Connection object.

SqlDataAdapter da = new SqlDataAdapter(Query, Conn);

The code above creates a new SqlDataAdapter, da.  The SQL select statement specifies what data will be read into a DataSet.  The connection object, Conn, has already been instantiated, but not opened.  As you can see in the diagram above, It is the SqlDataAdapter's responsibility to open and close the connection when Fill and Update method are called.

Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet.

7]  The results of our query command are going to be returned to the data table in our application. We are now going to create a data table object, called dt. Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet. (See Below!)

DataTable dt = new DataTable();

8]  Once we have created the data table, we can use the data adapter to fill it with data in accordance with our diagram above. Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet. (See Below!)

da.Fill(dt);

9]  We now have data and can display it! Add the line below to your form and test it!

MessageBox.Show(dt.Rows[0]["First"].ToString());
Your form should load and the message box should display the following: (See Below!) 

10]  User_Load can be seen below!


 


Connect To The Database  With SQL Authentication

1] In accordance with ConnectionStrings.com, let us define our Connection String to load the Library database on Net1\SQLExpress for user student whose password is student. Note the need  to use the \\ escape sequence in the connection string. Create a string object, called ConnectionString, and fill it in accordance with your SQL Server Name.  Add the line below to your form and test it; your form should load, but there will be no evidence that the database stuff is working yet. (See Below!)

String ConnectionString = "Data Source=NET1\\SQLEXPRESS;Initial Catalog=LibraryApp;" +
                          "Persist Security Info=True;User ID=student; Pwd=student";
 
2] Your form should once again load and the message box should display the following: (See Below!) 
3]  Your Users.cs form should work equally well. The compelte code is shown below!

 


Modularize The Connection Functions

1] As we begin to modularize our code, we will find that we are going to use the Conn object and the dt object in a number of modules. Rather than create multiple connections, or pass it, let us make them available to the entire form. Declare Conn and dt as shown below. Add the other variables as well; we shall use them later.

    public partial class Users : Form
    {
        long RecNo, NoRecords;
        SqlConnection Conn;
        DataTable dt;

2] A number of the functions in this application can be re-used in multiple applications if generalized a bit. Include the code for functions WindowsAuthenticationConnection and SQLAuthenticationConnection  immediately below the declarations for Conn and dt (above)..

3] Test function WindowsAuthenticationConnection with the code below. Substitute your SQLServer!

        private void Users_Load(object sender, EventArgs e)
        {
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp"); 
            String Query = "Select * From Users where ID = 1";
            SqlDataAdapter da = new SqlDataAdapter(Query, Conn);
            DataTable dt = new DataTable();
            da.Fill(dt);
            MessageBox.Show(dt.Rows[0]["First"].ToString());
        }

3] Test function SQLAuthenticationConnection  with the code below. Substitute your SQLServer!

        private void Users_Load(object sender, EventArgs e)
        {
            SQLAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp", "student", "student"); 
            String Query = "Select * From Users where ID = 1";
            SqlDataAdapter da = new SqlDataAdapter(Query, Conn);
            DataTable dt = new DataTable();
            da.Fill(dt);
            MessageBox.Show(dt.Rows[0]["First"].ToString());
        }

 


Create txtTrace

1] Using your mouse, drag a ListBox from the ToolBox off to the side of your Users form. Using a MessageBox to display very much is a hassle. Name the ListBox lbTrace.


Function DisplayRecord

1] We are generally going to have only one record in our DataTable dt; it will be located at Function DisplayRecord shall be used to display the data table field name and content of the the first, and only, record in our DataTable. It is generic and will work for other tables as well. Add the following utility function to your application.

 2] Test Function DisplayRecord with the following:
       private void Users_Load(object sender, EventArgs e)
        {
            SQLAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp", "student", "student"); 
            String Query = "Select * From Users where ID = 1";
            SqlDataAdapter da = new SqlDataAdapter(Query, Conn);
            dt = new DataTable();
            da.Fill(dt);
            RecNo = 1;
            NoRecords = 56;
            DisplayRecord("Contents Of Record (ID=1)");
        }

3] The output in txtTrace should be:

4] As we move to the top, next, previous, and bottom records, it is essential that we be able to display the DataTable information.


Function TableLength

1] Add generic Function TableLength to your Users.cs form. If we are going to create a button to go to the Next Record, it will be essential that we know how many records are in the table. Note that this number might continually change in a multi-user (concurrent) database application.

2]   Test Function TableLength with the following:

       private void Users_Load(object sender, EventArgs e)
        {
            SQLAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp", "student", "student"); 
            String Query = "Select * From Users where ID = 1";
            SqlDataAdapter da = new SqlDataAdapter(Query, Conn);
            dt = new DataTable();
            da.Fill(dt);
            RecNo = 1;
            NoRecords = TableLength("Users");
            DisplayRecord("Contents Of Record (ID=1)");
        }

3] The output in txtTrace is once again as follows:

4] Note that the value in the CNoRecords variable was calculated this time; in the previous section, it was simply assigned..


Function FillFormVariables

1] Add Function FillFormVariables to your Users.cs form. The responsibility of FillFormVariables is to fill the User Form variables with the data from record 0 in the DataTable Record. This function is non-generic, but there will be a similar function on other forms, such as Books.cs.

2]   Test Function FillFormVariables with the following:

        private void Users_Load(object sender, EventArgs e)
        {
            TableName = "Users";
            PrimaryKey = "ID";
            OrderBy = "FullName";
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp");
            RecNo = 1;
            NoRecords = TableLength(TableName);
            dt = FillDataTable("Select * From Users where ID = 2");
            DisplayRecord("Contents Of Record (ID=2)");
            FillFormVariables();
        }

3] The output should be as follows:


Function FillDataTable

1] Add Function FillDataTable to your Users.cs form. The responsibility of FillFormVariables explicitly return a DataTable that has been filled with the passed Query.

2]  Test Function FillDataTable with the following:       

        private void Users_Load(object sender, EventArgs e)
        {
            TableName = "Users";
            PrimaryKey = "ID";
            OrderBy = "FullName";
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp");
            RecNo = 1;
            NoRecords = TableLength(TableName);
            dt = FillDataTable("Select * From Users where ID = 2");
            FillFormVariables();
        }

3] The output should be as follows:


Button btnClose

1] In accordance with our other tutorials, the btnClose should be as follows:


Code To Remove Form Close Button From User Form

1] In accordance with our other tutorials, the ViewUser MenuStrip control seen below

should open your User form with the Show method.

2] Should you choose to close our exiting user form by clicking the X in the top right corner

you would get an error message next time you tried to open it from the main Application form. Even though I have included this code in previous tutorials, I am going to include the code it agian below. Add the code to remove the close functionality from your User form.

        //================================================================================//
        //                                CreateParams                                    //
        //================================================================================//
        //  Purpose: Block of code to disable the close box on a form and yet control     //
        //           miniize and maximize functionality.                                  //
        //                                                                                //
        //  Written By : Dr. Thomas E. Hicks                   Environment : .NET 2005    //
        //        Date : 3/1/2008                                 Language : C#           //
        //================================================================================//
        private const int CS_NOCLOSE = 0x200;
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ClassStyle = cp.ClassStyle | CS_NOCLOSE;
                return cp;
            }
        }

3] I would also add this code to the LibraryApp.cs form.


Function DisplayFormVariables

1] Add the diagnostic Function DisplayFormVariables to your Users.cs form. This function will send a copy of all Form Variables to txtTrace. It should prove to be a valuable diagnostic routine when we begin to have problems with our navigation functions later in this tutorial.

        //================================================================================//
        //                           DisplayFormVariables                                 //
        //================================================================================//
        //  Purpose: Display the Form Variables in the listbox called lbTrace.            //
        //                                                                                //
        //  Written By : Dr. Thomas E. Hicks                   Environment : .NET 2005    //
        //        Date : 3/1/2008                                 Language : C#           //
        //================================================================================//
        public void DisplayFormVariables()
        {
            lbTrace.Items.Add("-----------------------------------------------------");
            lbTrace.Items.Add("               Form Variables                        ");
            lbTrace.Items.Add("-----------------------------------------------------");
            lbTrace.Items.Add("TableName.........= " + TableName.ToString());
            lbTrace.Items.Add("PrimaryKey........= " + PrimaryKey.ToString());
            lbTrace.Items.Add("OrderBy...........= " + OrderBy.ToString());
            lbTrace.Items.Add("NoRecords.........= " + NoRecords.ToString());
            lbTrace.Items.Add("RecNo.............= " + RecNo.ToString());
            lbTrace.Items.Add("OrderBy...........= " + OrderBy.ToString());
            lbTrace.Items.Add("txtFirst..........= " + txtFirst.Text);
            lbTrace.Items.Add("txtMI.............= " + txtMI.Text);
            lbTrace.Items.Add("txtLast...........= " + txtLast.Text);
            lbTrace.Items.Add("txtMajorID........= " + txtMajorID.Text);
            lbTrace.Items.Add("txtAdministrator..= " + txtAdministrator.Text);
            lbTrace.Items.Add("txtUserName.......= " + txtUserName.Text);
            lbTrace.Items.Add("txtPassword...... = " + txtFirst.Text);
            lbTrace.Items.Add("dt->[ID]..........=" + dt.Rows[0]["ID"].ToString());
            lbTrace.Items.Add("dt->[Deleted].....=" + dt.Rows[0]["Deleted"].ToString());
            lbTrace.Items.Add("dt->[FullName]....=" + dt.Rows[0]["FullName"].ToString());
            lbTrace.Items.Add("-----------------------------------------------------");
        }

2]  Test Function DisplayFormVariables with the following:       

        private void Users_Load(object sender, EventArgs e)
        {
            TableName = "Users";
            PrimaryKey = "ID";
            OrderBy = "FullName";
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp");
            RecNo = 1;
            NoRecords = TableLength(TableName);
            dt = FillDataTable("Select * From Users where ID = 2");
            FillFormVariables();
            DisplayFormVariables();
        }

3] The output should be as follows:


Function LoadToolTips

1] Using your mouse, drag a ToolTip from the ToolBox to your User.cs form. Name the ToolTip toolTip1.

2] Add function LoadToolTips to your User.cs form.

        public void LoadToolTips()
        {
            toolTip1.SetToolTip(btnA, "Go To The First User Whose Last Name Starts With An 'A'");
            toolTip1.SetToolTip(btnB, "Go To The First User Whose Last Name Starts With An 'B'");
            toolTip1.SetToolTip(btnC, "Go To The First User Whose Last Name Starts With An 'C'");
            toolTip1.SetToolTip(btnD, "Go To The First User Whose Last Name Starts With An 'D'");
            toolTip1.SetToolTip(btnE, "Go To The First User Whose Last Name Starts With An 'E'");
            toolTip1.SetToolTip(btnF, "Go To The First User Whose Last Name Starts With An 'F'");
            toolTip1.SetToolTip(btnG, "Go To The First User Whose Last Name Starts With An 'G'");
            toolTip1.SetToolTip(btnH, "Go To The First User Whose Last Name Starts With An 'H'");
            toolTip1.SetToolTip(btnI, "Go To The First User Whose Last Name Starts With An 'I'");
            toolTip1.SetToolTip(btnJ, "Go To The First User Whose Last Name Starts With An 'J'");
            toolTip1.SetToolTip(btnK, "Go To The First User Whose Last Name Starts With An 'K'");
            toolTip1.SetToolTip(btnL, "Go To The First User Whose Last Name Starts With An 'L'");
            toolTip1.SetToolTip(btnM, "Go To The First User Whose Last Name Starts With An 'M'");
            toolTip1.SetToolTip(btnN, "Go To The First User Whose Last Name Starts With An 'N'");
            toolTip1.SetToolTip(btnO, "Go To The First User Whose Last Name Starts With An 'O'");
            toolTip1.SetToolTip(btnP, "Go To The First User Whose Last Name Starts With An 'P'");
            toolTip1.SetToolTip(btnQ, "Go To The First User Whose Last Name Starts With An 'Q'");
            toolTip1.SetToolTip(btnR, "Go To The First User Whose Last Name Starts With An 'R'");
            toolTip1.SetToolTip(btnS, "Go To The First User Whose Last Name Starts With An 'S'");
            toolTip1.SetToolTip(btnT, "Go To The First User Whose Last Name Starts With An 'T'");
            toolTip1.SetToolTip(btnU, "Go To The First User Whose Last Name Starts With An 'U'");
            toolTip1.SetToolTip(btnV, "Go To The First User Whose Last Name Starts With An 'V'");
            toolTip1.SetToolTip(btnW, "Go To The First User Whose Last Name Starts With An 'W'");
            toolTip1.SetToolTip(btnX, "Go To The First User Whose Last Name Starts With An 'X'");
            toolTip1.SetToolTip(btnY, "Go To The First User Whose Last Name Starts With An 'Y'");
            toolTip1.SetToolTip(btnZ, "Go To The First User Whose Last Name Starts With An 'Z'");
            toolTip1.SetToolTip(btnSearch,         "Launch Search User Dialog!");
            toolTip1.SetToolTip(btnAdd,            "Add A New User!");
            toolTip1.SetToolTip(btnDelete,         "Delete This User!");
            toolTip1.SetToolTip(btnUnDelete,       "Undelete Users!");
            toolTip1.SetToolTip(btnEdit,           "Edit This User!");
            toolTip1.SetToolTip(btnPrint,          "User Reports & PrintOut Options!");
            toolTip1.SetToolTip(btnNextRight,      "Go To The LoadNextRecord User!");
            toolTip1.SetToolTip(btnPreviousRight,  "Go To The LoadPreviousRecord User!");
            toolTip1.SetToolTip(btnTopRight,       "Go To The First User!");
            toolTip1.SetToolTip(btnBottomRight,    "Go To The Last User!");
            toolTip1.SetToolTip(btnNextLeft,       "Go To The LoadNextRecord User!");
            toolTip1.SetToolTip(btnPreviousLeft,   "Go To The LoadPreviousRecord User!");
            toolTip1.SetToolTip(btnTopLeft,        "Go To The First User!");
            toolTip1.SetToolTip(btnBottomLeft,     "Go To The Last User!");
            toolTip1.SetToolTip(btnClose,          "Close This Form!");
        }

3] Test Function LoadToolTips with the following:       

        private void Users_Load(object sender, EventArgs e)
        {
            TableName = "Users";
            PrimaryKey = "ID";
            OrderBy = "FullName";
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp");
            RecNo = 1;
            NoRecords = TableLength(TableName);
            dt = FillDataTable("Select * From Users where ID = 2");
            FillFormVariables();
            DisplayFormVariables();
            LoadToolTips();
        }

 


Logical vs. Physical Record Order

1] Suppose we do the Query : Select * From Users. The records will be displayed in Physical Order. The users has an auto incremented ID field that helps to confirm this in the snapshot below; note that this primary key helps to confirm the order. If  one were to cycle through the records in which the ID = 1, ID =2, ID  =3, etc <== this would be referred to as Physical Order.

2] Suppose we do the Query : Select * From Users Order By FullName. The records will be displayed in Logical Order. Note that this order is different than the Physical Order above. If  one were to cycle through the records in which the ID = 8, ID = 9, ID  = 11, etc but the critical issue here is that cycling through the records would provide Agoston, Akers, Aldana, etc. (because of the Order By) <== this would be referred to as logical order.

3] Most applications display data in some type of Logical Order. It is critical that our Next and Previous buttons cycle through our records in Logical Order.


MySQL Server With Limit

1] Different databases enhance/enrich the standard query language by providing additional functionality; the Limit functionality provided by MySQL is such an enhancement. Suppose we do the Query : Select * From Users Order By FullName Limit 3,5 (See Below!)

2]  The Query : Select * From Users Order By FullName Limit 3,5 would display 5 records in logical order -  starting with the RecNo 3. The first record is RecNo = 0.

3] The Query : Select * From Users Order By FullName Limit 0,1 would display RecNo = 0.

4] The Query : Select * From Users Order By FullName Limit 4,1 would display RecNo = 4.

5] Limit makes it extremely easy to cycle through records in a MySQL database using both web pages or stand-alone applications. The limit provides a truly generic solution that will work with any table in any order.


MSSQL Has No Limit!

1] After hours of research, I have not been able to find a truly generic solution for MSSQL. I do have something that will work for tables that contain a Primary Key. There are lots of hack solutions on the Internet, but I think the solution below offers the most generic approach for MSSQL.

2] The  table must have a Primary Key. The PrimaryKey for the Users table is ID.

3] Below you can see the results of the Query =

SELECT TOP 5 *          
FROM Users               
WHERE [ID] NOT IN      
    (SELECT TOP 3 [ID]      
     FROM [Users]            
     ORDER BY FullName)      
ORDER BY FullName 

4] This much more complicated MSSQL Query would also display 5 records in logical order - starting with the RecNo 3. The first record is RecNo = 0. 

5] The Query to display the fifth logical record,  RecNo 4, is seen below.


Function FillTableRecord

1] The question becomes, "how do we extrapolate what we have seen above into a usable generic function?"

2] In the first FillTableWithRecord function I wrote,  I passed four arguments:

public DataTable FillTableWithRecord (long RecNo, String TableName, String PrimaryKey, String OrderBy)

3] The user can use the OrderBy ComboBox to control the display order on the form. his function was effective and I called it many times in the User form, but I kept passing it the same information over and over. In order to simplify this tutorial, I have declared seven variables that shall be available to all of the functions on the Users form.

    public partial class Users : Form
    {
        String TableName, PrimaryKey, OrderBy;
        long RecNo, NoRecords;
        SqlConnection Conn;
        DataTable dt;

4] My Users is going to eventually do the following:

        public Users()
        {
            InitializeComponent(); // Create the Users form
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp"); // Connect to the DB
            TableName = "Users";    //Identify Users as the primary LibraryApp table for this form
            PrimaryKey = "ID";        // Identify ID as the primary key for the Users Table
                        OrderBy = "FullName";            // Identify FullName as the initial logical order  in which to cycle through the records
                        RecNo = 0;                             // Set the current record number = 0
                        NoRecords = TableLength(TableName);  // Set the NoRecords = 56 so that we know the end point
                        LoadFirstRecord();                   // Read record 0 from the table and fill the form fields
                        LoadToolTips();                        // Load the Tool Tips for this form
        }

5] Because the TableName, PrimaryKey, and OrderBy are globally available, I have written a simplified FillTableWithRecord function that is passed only one argument:

public DataTable FillTableWithRecord (long RecNo)

6] Add function FillTableWithRecord to your application

7]  Test Function FillTableWithRecord with the following:       

        public Users()
        {
            InitializeComponent();
            TableName = "Users";
            PrimaryKey = "ID";
            OrderBy = "FullName";
            WindowsAuthenticationConnection("NET1\\SQLEXPRESS", "LibraryApp");
            RecNo = 1;
            NoRecords = TableLength(TableName);
            FillTableWithRecord(0);
            FillFormVariables();
            LoadToolTips();
        }

        private void Users_Load(object sender, EventArgs e)
        {
        }

8]  Test Function FillTableWithRecord with the following:       


Function LoadNextRecord

1] The purpose of LoadNextRecord is to load the next undeleted logical record. When we get to last logical record, it will be the responsibility of LoadNextRecord to wrap around and proceed to the next undeleted record starting from record 0.

2] NoRecords is repeatedly filled because any user on a concurrent system might add new records at any time. Add function LoadNextRecord  to your project.

3] Add a call to LoadNextRecord for both btnNextRight and btnNextLeft. (See Below!)

4] Run your application and cycle all the way through the records. Open the Users table and set Deleted to T/t for Steven, Michael, and John. (See Below!)

5] Cycle all the way through the records again; note that 2, 3, and 5 are skipped.


Function LoadPreviousRecord

1] The purpose of LoadPreviousRecord is to load the previous undeleted logical record. When we get to first logical record, it will be the responsibility of LoadPreviousRecord to wrap around and proceed to the previous undeleted record starting from last logical record.

2] NoRecords is repeatedly filled because any user on a concurrent system might add new records at any time. Add function LoadPreviousRecord to your project.

3] Add a call to LoadPreviousRecord for both btnPreviousRight and btnPreviousLeft. (See Below!)

4] Run your application and cycle all the way through the records; note that 2, 3, and 5 are still skipped.


Function LoadFirstRecord

1] The purpose of LoadFirstRecord is to load the first undeleted logical record. If the first record is deleted, then proceed to the next record.. Add function LoadFirstRecord to your project.

3] Add a call to LoadFirstRecord for both btnTopRight and btnPreviousTopLeft.


Function LoadLastRecord

1] The purpose of LoadLastRecord is to load the last undeleted logical record. If the last record is deleted, then proceed to the previoous record.. Add function LoadLastRecord to your project.

3] Add a call to LoadLastRecord for both btnBottomRight and btnBottomLeft. (See Below!)


Function SetRecNo

1] Filters and OrderBy increase the difficulty level of the navigation buttons we have completed thus far. The purpose of SetRecNo is to adjust the RecNo appropriately as one changes the OrderBy ComboBox.Resetting the RecNo properly for ordered lists makes this the most challenging function in the tutorial.

2] Add function SetRecNo to your project.


Function LoadFirstLetterRecord

1] The purpose of LoadFirstLetterRecord is to load the first undeleted logical record whose FieldName begins with the Letter. If that record is deleted or not there then proceed to the next record; wrap around if necessary. Note that this function uses the SetRecNo function above.

2] The process of searching for the first record whose starting letter matches the FullName, it is necessary to temporarily change the order to the FullName; the user's chosen order is returned upon completion.

3] Add function LoadFirstLetterRecord to your project.

4] Add a call to LoadFirstLetterRecord for each and every one of the 26 buttons below. (See Below!)

5] Test each of the 26 buttons.

Function LoadFirstLetterRecord

1] Double click on your OrderBy combo box. The ComboBox to change the OrderBy is pretty straight forward. Include the code in your application..

 
2] Test your application. 

Filters Add A Whole Collection Of Problems

1] Adding filters is an assignment left to my students. Good Luck!