Friday, April 25, 2008

Using Data Across Multiple Windows Forms with OK and Cancel Feature

Issue: Cannot add OK and Cancel button on other forms if DataSet Contains multiple child tables, other forms change the data directly from main DataSet
Level: Advanced
Knowledge Required:

  • Typed DataSet
  • Data Bindings


Description:

Before you continue with this article I recommend to see the Beth Massi’s article because it will clear the basic idea. My article covers a little advanced topic.

As we all have used a Typed DataSet (with multiple related tables) on a single form for Data Entry Process. Which is easy to create using Visual Studio 2005.

Sometimes we require to break this data entry process into multiple Forms but the Typed DataSet should remain same. For example consider the following DataSet,

Company DataSet


Now we can create a single form containing controls that can manipulate all three tables. But the form will look like a mess. So we can split the data into multiple forms as I have created 2 forms

Company Form

Address Form



As you can see the Address form will open when User clicks the New Address or Edit Address Buttons. The logic will be very simple for sharing the Data i.e. we will pass the CompanyDataSet in the Public Constructor (New Method) of Address Form and the Address Form will use that DataSet for editing. But if we directly use the Passed DataSet in the Address Form then changes in this form will cause directly changes in the DataSet since its reference is passed. For Example:

In Address Form we replace the binding source’s DataSurce in the New Method as,

Public Sub New(ByVal Address_ID As Integer, ByRef CompDS As CompanyDataSet)
Me.InitializeComponent()
Me.AddressBindingSource.DataSource = CompDS
Me.AddressBindingSource.Filter = "Address_ID=" & Address_ID
End Sub


Now changes in this form will directly change the Main Company DataSet and the buttons we have placed at the bottom of this form i.e. OK, Cancel and Apply will have no use. So what we do here is a simple technique:

We will make a copy of the DataSet and make changes in that copy and on pressing the OK or Apply buttons we will copy the changes on the main DataSet.

Technically speaking:
1) Create Private Save Changes method in Parent form which accepts a Parameter CompanyDataSet, this method will discard its own CompanyDataSet and copy all the things from this given DataSet
2) Create a Public Delegate in Child Form for saving the changes in DataSet
3) Pass this save changes method (created on Parent Form) to the Child Form so the Child Form call this save method by passing its own CompanyDataSet


Child Form (Address Form):
We will use the following code form Child Form

' this form will execute this method to save changes
Public Delegate Sub SaveChangesDelegate(ByRef OtherDataSet As CompanyDataSet)
' declaration of Delegate
Private _SubSaveChanges As SaveChangesDelegate

Public Sub New(ByVal Address_ID As Integer, ByRef CompDS As CompanyDataSet, ByRef SubSaveChanges As SaveChangesDelegate)
Me.InitializeComponent()
' coying data from main dataset
Me.CompanyDataSet.Merge(CompDS)
Me._SubSaveChanges = SubSaveChanges
End Sub

Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
' call the save changes method
Me._SubSaveChanges(Me.CompanyDataSet)
Me.Close()
End Sub

Private Sub btnApply_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnApply.Click
' just save changes do not close form
Me._SubSaveChanges(Me.CompanyDataSet)
End Sub

Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
Me.Close()
End Sub

Parent Form:
Following is the code for Parent Form

Private Sub btnEditAddress_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEditAddress.Click
Dim drvCurrentAddress As DataRowView
' get the current selected address
drvCurrentAddress = Me.AddressBindingSource.Current
' if NO Address is selected
If drvCurrentAddress Is Nothing Then
MsgBox("Please select an Address first", MsgBoxStyle.Exclamation, "Edit Address")
Else ' else (address is selected)
' display that address
Dim frmNew As frmAddress
frmNew = New frmAddress(drvCurrentAddress("Address_ID"), Me.CompanyDataSet, AddressOf SaveChanges)
frmNew.ShowDialog()
End If
End Sub

Public Sub SaveChanges(ByRef OtherDataSet As CompanyDataSet)
Me.CompanyDataSet.Clear()
Me.CompanyDataSet.Merge(OtherDataSet)
End Sub


Updated Log:
24-May-2008:
DataSet.Merge() method only merges the rows i.e. new rows added and old rows updated but this method does NOT remove the Rows. E.g. DS1.Merge(DS2) will NOT Delete the rows from DS1 (rows that are DELETED from DS2). Therefore the above article was NOT working for the Deleted Rows.
26-May-2008:
Changing the reference of DataSet affects the BindingSource. Since BindingSource is bound to the previous DataSet and executing Me.DataSet = OtherDataSet causes Me.DataSet to change its reference to OtherDataSet and our BindingSource still bound with the previous Reference. To overcome this issue I have changed the Logic the code in above SaveChanges() method, i.e. first clear the DataSet and then merge with the OtherDataSet.

1 comment:

Axplains said...

Thanks for your article, it was instructive. But, how would you perform the same task if your data comes from a LINQ query?

The "Filter" property would not work in this case: can you please point out how to modify your example to make it work with LINQ? Thanks a lot.