
Button Control in VB Database

Hello friends! In this tutorial, we will add Add, Save, Remove and navigation buttons to a Visual Basic database application. We will continue with the contact form from our previous lesson and use its existing dataset, binding source and TableAdapter.
If you have not built that form yet, first follow creating a database in Microsoft Visual Studio. This lesson retains the original Visual Studio 2010 Windows Forms example, with clearer event handlers and explanations of what each button changes.
The key distinction is that editing a bound form changes in-memory data first. Adding a row, removing a row and moving to another contact do not necessarily write anything to SQL Server. Our Save button is responsible for sending the pending changes to the database.
Start with the Existing Contact Form
The first lesson created a contact table and displayed it in both a grid and a details view. Your form should already load contacts successfully before you add the new buttons.
We will extend it with seven controls: Add, Save, Remove, Previous, Next, Move To Top and Move To End.
The examples assume these generated components:
| Component | Purpose |
|---|---|
ContactsDataSet | Holds the contact rows in memory. |
Contact_FormBindingSource | Tracks the current row and supplies data to the controls. |
Contact_FormTableAdapter | Reads and updates the contact table through configured commands. |
TableAdapterManager | Coordinates saves for its configured TableAdapters when present. |
FullNameTextBox | The bound details control for the required contact name in the updated example schema. |
Check the actual names in your designer. If your original table uses a different name field, adapt the validation control name accordingly. Renaming a button's caption does not rename a binding source or regenerate the database schema.
Place the following methods inside your existing form class. Do not paste a second complete form class into the designer-generated file. If Visual Studio already created an event handler, replace its body or use the supplied handler once; duplicate handlers can run the same operation twice.
Step 1: Add the Add, Save and Remove Buttons
Open the form designer and drag three Button controls from the Toolbox to a convenient area of the form.
Set their Name and Text properties separately. Name is the identifier used in code; Text is the caption the user sees.
| Name | Text | Intended action |
|---|---|---|
btnAdd | Add | Create a new contact in the bound in-memory list. |
btnSave | Save | Validate and persist pending contact changes. |
btnRemove | Remove | Remove the selected contact from the working data, pending Save. |
For the button workflow below, set each custom button's CausesValidation property to False. Add, Save and navigation will explicitly validate through a shared helper. Remove deliberately allows an incomplete new contact to be discarded. Keep validation enabled on the data-entry text boxes.
To keep this example focused, use the details controls for editing and make the grid read-only. Also disable its user-add and user-delete options. This prevents the grid from becoming a second editing path with different validation behavior. Existing BindingNavigator buttons should either be removed from the form or routed through the same actions if you keep them.
Step 2: Add Meaningful Validation
Calling ValidateChildren() is only useful when the form has validation rules. It returns a Boolean result that must be checked before the application proceeds. Ignoring that result can allow a save to continue after a control has rejected its value. See the ValidateChildren reference.
Add an ErrorProvider component named ErrorProvider1 from the Toolbox. For the updated contact schema, set FullNameTextBox.MaxLength to 100 and add this handler:
Private Sub FullNameTextBox_Validating(
sender As Object,
e As System.ComponentModel.CancelEventArgs
) Handles FullNameTextBox.Validating
ErrorProvider1.SetError(FullNameTextBox, "")
If Contact_FormBindingSource.Count = 0 Then
Return
End If
Dim enteredName As String = FullNameTextBox.Text.Trim()
If enteredName.Length = 0 OrElse enteredName.Length > 100 Then
e.Cancel = True
ErrorProvider1.SetError(
FullNameTextBox,
"Enter a contact name from 1 to 100 characters.")
End If
End Sub
This handler checks the name and displays an error beside its control. It does not write a database row. The Validating event is the point where a control can reject its current value through the event arguments.
Add equivalent rules for fields your application requires. Keep phone numbers as text to preserve formatting and leading zeros. Do not require every optional field merely because a text box exists on the form.
Create a shared helper for finishing an edit
Private Function FinishCurrentEdit() As Boolean
Try
If Contact_FormBindingSource.Count = 0 Then
Return True
End If
If Not Me.ValidateChildren() Then
MessageBox.Show(
"Please correct the highlighted contact details.",
"Check contact")
Return False
End If
Contact_FormBindingSource.EndEdit()
Return True
Catch ex As Exception
MessageBox.Show(
"The current edit could not be completed: " & ex.Message,
"Contact edit")
Return False
End Try
End Function
The empty-list check is intentional. After deleting the last contact, no row remains to validate, but its pending deletion still needs to be saved.
BindingSource.EndEdit completes the edit in the underlying bound data. In this application that data is held in a dataset. EndEdit does not, by itself, execute an SQL update.
Step 3: Implement the Add Button
Private Sub btnAdd_Click(
sender As Object,
e As EventArgs
) Handles btnAdd.Click
If Not FinishCurrentEdit() Then
Return
End If
Try
Contact_FormBindingSource.AddNew()
ErrorProvider1.Clear()
FullNameTextBox.Focus()
Catch ex As Exception
MessageBox.Show(
"A new contact could not be started: " & ex.Message,
"Add contact")
End Try
End Sub
After clicking Add, enter the contact's details and then click Save. The new row belongs to the application's working data until a database update succeeds.
The AddNew method also interacts with any pending edit on the current item. Calling our validation helper first prevents repeatedly starting new contacts while leaving a required name empty.
Do not assign ContactID by taking the number of visible rows and adding one. The example table uses a database identity column. Its generated dataset may temporarily use an unsaved identity value; the final key must come from the configured insert and refresh behavior.
Adding a contact creates a row. It does not add a column to the database. Columns describe fields such as name and phone, and changing them is a schema operation.
Step 4: Implement the Save Button
The original form used a TableAdapterManager. If that component exists in your project, make sure its Contact_FormTableAdapter property is assigned to the adapter that loads your contacts. The handler below makes that assignment explicit.
Private Sub btnSave_Click(
sender As Object,
e As EventArgs
) Handles btnSave.Click
If Not FinishCurrentEdit() Then
Return
End If
Try
Me.TableAdapterManager.Contact_FormTableAdapter =
Me.Contact_FormTableAdapter
Dim affected As Integer =
Me.TableAdapterManager.UpdateAll(Me.ContactsDataSet)
If affected = 0 Then
MessageBox.Show("There were no row changes to save.", "Contacts")
Else
MessageBox.Show(
affected.ToString() & " row changes saved.",
"Contacts")
End If
Catch ex As System.Data.DBConcurrencyException
MessageBox.Show(
"A contact changed in the database. Review the conflict before retrying.",
"Save conflict")
Catch ex As Exception
MessageBox.Show(
"The contacts could not be saved: " & ex.Message,
"Save failed")
End Try
End Sub
TableAdapterManager is generated for the dataset; it is not a universal component present in every Windows Forms project. Its configured adapters determine which tables it can update. The TableAdapter and TableAdapterManager documentation explains their roles.
If your single-table project has no manager, replace the manager assignment and UpdateAll call with this line inside the same Try block:
Dim affected As Integer =
Me.Contact_FormTableAdapter.Update(
Me.ContactsDataSet.Contact_Form)
Choose one save path. Do not add both calls and assume that more update calls make the operation safer.
Understand what Save includes
A dataset tracks row states. Added contacts need inserts, changed contacts need updates and persisted contacts marked for deletion need deletes. Saving the dataset is therefore broader than saving only the row currently displayed.
Suppose you edit contact A, add contact B and remove contact C before pressing Save. The save operation may process all three pending changes. The displayed count represents affected row operations, not the total number of contacts in the table. Microsoft's dataset persistence guide explains this two-stage update model.
Do not call ContactsDataSet.AcceptChanges() before the adapter update. That accepts changes in memory and alters the tracked row states; it does not write them to SQL Server. Clearing that information too early can leave the adapter with nothing to persist. See AcceptChanges and RejectChanges.
Step 5: Implement the Remove Button
Private Sub btnRemove_Click(
sender As Object,
e As EventArgs
) Handles btnRemove.Click
If Contact_FormBindingSource.Count = 0 OrElse
Contact_FormBindingSource.Current Is Nothing Then
MessageBox.Show("There is no contact selected.", "Remove contact")
Return
End If
Dim answer As DialogResult = MessageBox.Show(
"Remove the selected contact? Press Save afterward to store the change.",
"Remove contact",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2)
If answer <> DialogResult.Yes Then
Return
End If
Try
Contact_FormBindingSource.RemoveCurrent()
ErrorProvider1.Clear()
Catch ex As Exception
MessageBox.Show(
"The contact could not be removed: " & ex.Message,
"Remove failed")
End Try
End Sub
RemoveCurrent acts on the current item in the bound list. With our dataset binding, removing an existing persisted row creates a pending deletion. The database row remains until Save sends that deletion successfully.
Removing a new contact that was never saved is different: there is no corresponding database row to delete. The action simply discards that pending addition.
We intentionally do not validate required fields before Remove. A user should be able to discard an incomplete new contact without first inventing a name for it. The confirmation makes the selected action explicit, while the empty-list check handles the case where there is nothing to remove.
Step 6: Add Previous and Next Buttons
Place two more buttons below the editing controls.
Name them btnPrevious and btnNext, with the captions Previous and Next. Set CausesValidation to False because the shared navigation handler will validate explicitly.
The underlying operations are MovePrevious() and MoveNext(). They change the current position in the binding source. They do not rearrange database rows or save pending changes.
We will connect these buttons after adding the first and last controls so that all four use the same handler.
Step 7: Add First and Last Record Buttons
Add the final two buttons in the form designer.
Name them btnFirst and btnLast. You can keep the original captions Move To Top and Move To End, although First and Last are shorter alternatives. Set CausesValidation to False for these buttons as well.
Now add one handler for all four navigation buttons:
Private Sub Navigation_Click(
sender As Object,
e As EventArgs
) Handles btnPrevious.Click, btnNext.Click, btnFirst.Click, btnLast.Click
If Contact_FormBindingSource.Count = 0 Then
Return
End If
If Not FinishCurrentEdit() Then
Return
End If
Try
Select Case DirectCast(sender, Button).Name
Case "btnPrevious"
Contact_FormBindingSource.MovePrevious()
Case "btnNext"
Contact_FormBindingSource.MoveNext()
Case "btnFirst"
Contact_FormBindingSource.MoveFirst()
Case "btnLast"
Contact_FormBindingSource.MoveLast()
End Select
Catch ex As Exception
MessageBox.Show(
"The selected contact could not be changed: " & ex.Message,
"Navigation")
End Try
End Sub
The Handles clause connects the same method to four Click events. The button's Name selects the movement. Do not also add a second Click handler containing another movement call, or a single click may skip a contact.
For an empty list, the handler returns immediately. With one contact, first and last refer to the same item. With several contacts, the order follows the current bound view, including any active sorting or filtering.
Position is different from ContactID
BindingSource.Position is zero-based. If Position is 2, the third item is selected. Its ContactID could be 3, 17 or another value.
A human-readable position label can use Position + 1 when Count is greater than zero, and display “No contacts” for an empty list. That label describes the current view, not necessarily the number of rows in the whole database.
Handle Failures Without Hiding Them
An empty Catch block makes a failed save look like a successful click. The examples display error messages so that a learning project can reveal missing commands, connection problems or invalid values. A deployed application should provide a suitable user message and record detailed diagnostics through its logging system.
A concurrency conflict can occur when another user changes or removes a row after your form loads it. The configured update command may then find no matching original row. Catching the exception informs the user, but it does not resolve the conflict automatically. Microsoft's optimistic concurrency guide explains why original values matter.
Do not immediately refill the entire dataset in every Catch block. That can replace the user's working data before the conflict is understood. Likewise, do not retry a batch blindly after a partial failure without checking the update and transaction behavior of your chosen save path.
Review the Button Behavior with a Small Example
- Open the form and confirm that the existing contacts load.
- Click Add, enter a fictional name and save it.
- Close and reopen the application to check persistence.
- Edit the name, navigate to another record and remember that navigation alone does not save it.
- Click Save, reopen the application and inspect the edited contact.
- Remove the test contact, then click Save and check that the deletion persists.
- Start a new contact with a blank required name and confirm that Save requests a correction.
- Use Remove to discard that incomplete contact.
These are exercises to run in your own project, not a claim that the screenshots demonstrate the revised handlers. Use test contacts so that you can repeat the actions without affecting useful records.
| Symptom | Likely area to inspect |
|---|---|
| A button does nothing. | Its Name, Click event and any displayed validation error. |
| One click adds two contacts. | Duplicate event subscriptions or duplicate AddNew calls. |
| Save reports no changes unexpectedly. | Binding configuration, premature AcceptChanges calls and manager adapter assignments. |
| Saved records seem to disappear. | The runtime database connection and any MDF copy in the build-output folder. |
| The details and grid select different contacts. | Whether both views use the same BindingSource. |
| Remove fails on an empty table. | The current-item and row-count checks. |
For a fuller application, add a deliberate unsaved-changes policy when closing the form. Let users save, discard or cancel closing. Also apply the same validation policy to any extra navigation route you enable, such as direct grid selection or retained toolbar buttons.
Review: What Each Button Actually Does
Add starts a new working row, Remove changes the working list, and navigation changes which contact is current. Save validates the active edit and submits tracked changes through the configured data-access components. Keeping those responsibilities clear makes the form easier to extend and its failures easier to diagnose.
In the next part, we continue with updating database tables through VB code. Understanding the difference between a control value, a dataset row and a persisted database record is the foundation for that work.
Frequently Asked Questions
Does AddNew immediately insert a database record?
No. In this dataset-based example, it creates a new item in the bound working data. The adapter update persists it later.
Why does a removed contact return after restarting?
The deletion may not have been saved, the save may have failed, or the application may be opening a different database copy. Check those possibilities before changing the RemoveCurrent call.
Does EndEdit save to SQL Server?
No. It completes the current edit in the bound data. A TableAdapter update or configured TableAdapterManager operation performs persistence.
Can I use the original Button1 and Button2 names?
Yes, but every Handles clause and name comparison must match them. Descriptive names reduce confusion as the form grows.
Will Save update only the selected contact?
The examples save pending changes in the supplied table or managed dataset. Earlier edits and deletions can be included even when another contact is currently selected.
Can CancelEdit undo everything since the last save?
Do not assume so. Canceling a current binding edit and rejecting all tracked dataset changes have different scopes. Define the intended undo behavior before adding a Cancel button.
Marvellous post
1 reply
Thanks bro
send me the code
1 reply
I have provided the code but if you need the complete project post your email (must be subscribed to our newsletter) here and I will send it to you.
I was following the steps until "Step 1 : Add, Save & Remove Buttons" where there was an error that states 'Contact_Form BindingSource' not part of my project, please help.
Good work... Please send it to me.
1 reply
Thanks, I have deleted the files so can't send them.
Good Day.
Please send me the code
Nice

























Comments
14