asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search








Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Extending the Win Forms Binding Source Component
By David Catherman
Rating: 4.6 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    The binding source component in Visual Studio 2005/2008 Windows Forms provides a valuable service for data driven applications by providing automatic data binding between controls and the data source. This article shows how the functionality can be extended to add a few features to make it more useful. The most important is marking records as dirty when edited and prompting the user to save when changing records.

    In a previous article, I covered Extending the Binding Navigator which included a discussion of the binding source component. This article will demonstrate the need for binding source component to be extended as a custom component as well.

    Overview of the Binding Source Component

    In .NET 1.1, the Binding Context and the Currency Manager components were used to bind controls to a data source and were fairly complex to use. In .NET 2.0 the Windows Forms projects have a Binding Source component that encapsulates the Currency Manager to make the binding process more straight forward and less complex.

    The Binding Source provides a level of indirection between the data source and the controls bound to it. If the data source changes, just change the Data Source property and all the bound controls will be bound to the same properties and fields in the new data source. When data changes in a control, the binding source communicates that change back to the data source.

    The first step in using a Binding Source is to identify the data source. Any object that implements IList will work and even complex data sources can be used by setting the Data Member property as well (table in a dataset or child relation of another binding source). The List property can be used to access all of the data or the Current property will access one row in the list. Navigation methods allow changing the position pointer, which changes the row which is current. The Sort and Filter properties apply a DataView to the list to manipulate the data.

    Controls can be bound to the Binding Source by setting a property on the control. Internally, the Currency Manager tracks which controls are bound. When current property changes, each control in the set performs a read against the data row and the values are displayed in the control. When the value of a bound control is edited, the change is communicated back to the binding source. When the EndEdit method is called, the current row with its changed values is written back to the data source.

    Improvements Needed

    One of the problems with the Binding Source is that there is no way to tell if the data has been edited. It is quite common for the user to make edits to the controls and move the binding source to another position without knowing whether or not the edits were saved (usually not). It would be nice if there were an IsCurrentDirty property to flag when the current record has been edited.

    The second improvement needed is easy access to a list of the controls that are bound and which property of the control is bound to which field. This list could be accessed through the Currency Manager but an easier way might be needed.

    Third, the component needs to have a reference to the form it is on. This is not as straight forward as with controls and turned out to be a quite a trick to accomplish.

    Custom Component

    To make the changes to the component, create a new custom component in a Windows Forms project. Right click the project, Add, New Item, Component Class and name it exBindingSource. Visual Studio will open a design canvas with a link to open the code page. On the code page, make the class inherit from the BindingSource component.

    Imports System.ComponentModel.Design

    Public Class exBindingSourceComponent
        Inherits System.Windows.Forms.BindingSource

    Is Current Dirty Flag

    Add a property to the class for the dirty flag. (The Snippets has an entry that will quickly build the structure.)

        Private _isCurrentDirtyFlag As Boolean = False

        Public Property IsCurrentDirty() As Boolean
            Get
                Return _isCurrentDirtyFlag
            End Get
            Set(ByVal value As Boolean)
                If _isCurrentDirtyFlag <> value Then
                    _isCurrentDirtyFlag = value
                    If value = True Then 'call the event when flag is set
                        OnSetCurrentDirty(New EventArgs)
                    End If
                End If
            End Set
        End Property

    There are times when the developer needs to know when the dirty flag is set, so the component implements an event that can be subscribed to. This would allow the form to enable the save button when the dirty flag is set and disable it after the record is saved.

        Public Event SetCurrentDirty As SetCurrentDirtyEventHandler

        ' Delegate declaration.
        Public Delegate Sub SetCurrentDirtyEventHandler(ByVal sender As Object, ByVal e As EventArgs)

        Protected Overridable Sub OnSetCurrentDirty(ByVal e As EventArgs)
            RaiseEvent SetCurrentDirty(Me, e)
        End Sub

    Setting the Dirty Flag

    When a value is edited, the dirty flag needs to be set. The binding source has an event called OnBindingComplete that is raised when data is written to or back from a control to the data source. This is a very expensive event to handle because it fires quite often--once for each control every time the form is painted or when the current position changes. When handling the event, the event arguments parameter has some context settings that help distinguish which events are important to this process--specifically, the direction of the binding (updated back to the data source) and that it was successful.

        Private Sub _BindingComplete(ByVal sender As System.Object, _
                ByVal e As System.Windows.Forms.BindingCompleteEventArgs) _
                Handles Me.BindingComplete

            If e.BindingCompleteContext = BindingCompleteContext.DataSourceUpdate Then
                If e.BindingCompleteState = BindingCompleteState.Success And _
                        Not e.Binding.Control.BindingContext.IsReadOnly Then

                    'Make sure the data source value is refreshed (fixes problem mousing off control)
                    e.Binding.ReadValue()
                    'if not focused then not a user edit.
                    If Not e.Binding.Control.Focused Then Exit Sub

                    'check for the lookup type of combobox that changes position instead of value
                    If TryCast(e.Binding.Control, ComboBox) IsNot Nothing Then
                        'if the combo box has the same data member table as the binding source, ignore it
                        If CType(e.Binding.Control, ComboBox).DataSource IsNot Nothing Then
                            If CType(CType(e.Binding.Control, ComboBox).DataSource, _
                                    BindingSource).DataMember = (Me.DataMember) Then Exit Sub
                        End If
                    End If
                    IsCurrentDirty = True 'set the dirty flag because data was changed
                End If
            End If
        End Sub

    The ReadValue() method overcomes problem with earlier versions of the binding source that did not update the value in a control if the user tabbed out of the control (see the references at the end of the article). Checking for Focused() makes sure that only changes by the user are flagged--changes made by code should handle their own update concerns. There are also a couple other situations where false positives need to be captured, specifically combo boxes that are used for navigating to a new record rather than updating a value. Once the binding event meets all of these conditions, the dirty flag will be set.

    Saving Changed Records

    Knowing that the record was edited leads to the next topic of making sure the edits are updated to the persisted data store. Most of the time, edits should be persisted before the user moves to a different record and building that into the binding source component helps maintain the integrity of the data.

    There are two options here: reminding the user that the data needs to be saved, or auto saving in the background. Then decision is handled through another property called AutoSave.

        Private _autoSaveFlag As Boolean

        Public Property AutoSave() As Boolean
            Get
                Return _autoSaveFlag
            End Get
            Set(ByVal value As Boolean)
                _autoSaveFlag = value
            End Set
        End Property

    When the binding source component is added to the form, the developer can set the AutoSave property to true or the property can be tied to a checkbox or configuration parameter to allow the user to choose.

    The PositionChanged() event of the binding source is handled to check for changed data.

        Private Sub _PositionChanged(ByVal sender As Object, ByVal e As EventArgs) _
                Handles Me.PositionChanged
            If IsCurrentDirty Then
                If AutoSave Or MessageBox.Show(_msg, "Confirm Save", _
                        MessageBoxButtons.YesNo) = DialogResult.Yes Then
                    Try
                        'cast table as ITableUpdate to get the Update method
                        CType(_dataTable, Biz._Interface.ITableUpdate).Update()
                    Catch ex As Exception
                        Win.Logger.LogError(_form, ex, _dataTable.TableName & " Update")
                    End Try
                Else
                    Me.CancelEdit()
                    _dataTable.RejectChanges()
                End If
                IsCurrentDirty = False
            End If
        End Sub

    When the position changes, if the dirty flag has been set, the data needs to be saved. If the AutoSave property is not set, the user will be prompted to save or reject the changes.

    To understand how the data is updated, you will need to have read my other articles about building a data access layer (see references below). This architecture uses datasets as the data source and custom code is added to make sure each data table implements an interface with an Update method on it. Casting the data table as the interface will allow access to the Update method on the table generically.

    Referencing the Data Table

    The above code leads to a discussion on how to get a reference to the data table the binding source is bound to as a data source. (Note: If you are using collections or other list data for your data source, this code will need to be customized.) Datasets are complex and the data source property may refer to an actual data table (or data view) in the dataset, but it may also be set to a child relationship of another binding source object, which is a relation between two tables in the database, in which case, finding the name of the data table requires a bit of looking up in the dataset.

    First, a couple private fields are added to contain the information.

        Private _displayMember As String
        Private _dataTable As DataTable
        Private _dataSet As DataSet
        Private _parentBindingSource As BindingSource

    The binding source exposes events that are raised when the DataSource and DataMember properties are changed. When the form loads and the properties are set in the designer code, these events will be raised and the following code executed.

        Private Sub _DataSourceChanged(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles Me.DataSourceChanged
            _parentBindingSource = Nothing
            If Me.DataSource Is Nothing Then
                _dataSet = Nothing
            Else
                'get a reference to the dataset
                Dim bsTest As BindingSource = Me
                'try to cast the data source as a binding source
                Do While Not TryCast(bsTest.DataSource, BindingSource) Is Nothing
                    'set the parent binding source reference
                    If _parentBindingSource Is Nothing Then _parentBindingSource = bsTest
                    'if cast was successful, walk up the chain until dataset is reached
                    bsTest = CType(bsTest.DataSource, BindingSource)
                Loop
                'since it is no longer a binding source, it must be a dataset
                If TryCast(bsTest.DataSource, DataSet) Is Nothing Then
                    'Cast as dataset did not work
                    Throw New ApplicationException("Invalid Binding Source ")
                End If
                _dataSet = CType(bsTest.DataSource, DataSet)
                'is there a data member - find the datatable
                If Me.DataMember <> "" Then
                    _DataMemberChanged(sender, e)
                End If 'CType(value.GetService(GetType(IDesignerHost)), IDesignerHost)
                If _form Is Nothing Then GetFormInstance()
            End If
        End Sub

    The data source property can either be set to a dataset or another binding source, in which case, examine its data source. There could be multiple parent-child relationships in the data source, so use a loop to walk up the chain until the top parent is reached. Then the top parent data source will be a reference to a dataset.

    When the data member is changed then check to see if the name is in the list of the tables in the dataset. If it is not, then assume it is a relation and look in the set of relations in the dataset for the relation name. For a relation, the tablename is the child table in the relation.

        Private Sub _DataMemberChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                Handles Me.DataMemberChanged
            If Me.DataMember = "" Or _dataSet Is Nothing Then
                _dataTable = Nothing
            Else
                'check to see if the Data Member is the name of a table in the dataset
                If _dataSet.Tables(Me.DataMember) Is Nothing Then
                    'it must be a relationship instead of a table
                    Dim rel As System.Data.DataRelation = _dataSet.Relations(Me.DataMember)
                    If Not rel Is Nothing Then
                        _dataTable = rel.ChildTable
                    Else
                        Throw New ApplicationException("Invalid Data Member")
                    End If
                Else
                    _dataTable = _dataSet.Tables(Me.DataMember)
                End If
            End If
        End Sub

    Now the data table field should be populated by the time the PositionChanged event fires.

    List of Bound Controls

    Another nice-to-have feature of the extended binding source is a list of all the controls that are bound to the binding source. After adding a section added the name of the control to a collection from the BindingComplete event, I found that the base CurrencyManager object has a Bindings list of the controls.

    Reference to the Host Form for a Component

    At one point while building this component, I needed to get a reference to the hosting form. Looking back at it two months later, I cannot remember why I needed the reference, but I spent a lot of time trying to figure it out and the solution is interesting, so I will include it in the article. This may be optional for the binding source, but if you do much work with components you will need this someday.

    Components don't have a .Parent property like controls do. They do have a container property, but it does not have a parent property either. Sometimes casting the container as a ContainerControl works (it does have a parent property), but in this case it won't cast correctly.

    All Windows Designer forms add a container called "components" and all components are added to this container and show at the bottom of the screen in design mode. From inside the component, it is easy to get a reference to the container, but I could not get a reference to the parent form of the container?

    Then I found a clue. It turns out that the Error Provider component manages to get a reference to the parent form and exposes it in a property called "ContainerControl". The secret the Error Provider uses is to override the Site function of the component and capture the IDesignerHost type of service.

        Public Overrides Property Site() As System.ComponentModel.ISite
            Get
                Return MyBase.Site
            End Get
            Set(ByVal value As System.ComponentModel.ISite)
                'runs at design time to initiate ContainerControl
                MyBase.Site = value
                If value Is Nothing Then Return
                ' Requests an IDesignerHost service using Component.Site.GetService()
                Dim service As IDesignerHost = _
                        CType(value.GetService(GetType(IDesignerHost)), IDesignerHost)
                If service Is Nothing Then Return
                _form = CType(service.RootComponent, Form)
            End Set
        End Property

    Each component has a Site property that contains information about the container and is added by the IContainer interface. Overriding the property allows some information to be collected when the site is set by the container.

    One of the methods of the site object is GetService. The Site may be filled several times with different types of services, but we are only interested in the IDesignerHost type. If the cast of the service as an IDesignerHost works, then the reference to the form can be found in a property called RootComponent.

    As cool as this looks, there is a solution that is way simpler. In the same way that a list of controls is available on the CurrencyManager, once you have a reference to a control, just get the form reference from the control.

            If _form Is Nothing And Me.CurrencyManager.Bindings.Count > 0 Then
                _form = Me.CurrencyManager.Bindings(0).Control.FindForm()
            End If

    Some times it is so frustrating to spend days struggling to find a solution to a problem and then it turns out to be so easy. Oh well, that's life.

    Applying the Extended Binding Source Component

    After building the project, the custom control is listed in the Toolbox pane, usually near the top. Just drag and drop the component to a form and it will be added to the component container at the bottom of the screen. With the component selected, set the Data Source and Data Member properties in the properties pane. Then on each control, add a binding to the field in the binding source control. Clicking the down arrow next to the desired binding property (i.e. Text for a Textbox control), will display tree view list box. Expand the desired binding source and select the field.

    When using Visual Studio, most binding source components are added using the Data Sources pane. When a data source object is dragged from the Data Source pane and dropped on a form, the wizard will automatically add a binding source component to bind the controls created to the data source represented on the Data Sources pane. By default, Visual Studio will use a normal binding source component. (If anyone knows how to change this, please let me know.) To convert it to the new extended binding source component, you will need to open the designer code and change the line of code where the component is declared (near the bottom) and where it instantiated (near the top). In both lines, change the System.Windows.Forms.BindingSource type to the custom binding source as "ProjectName.exBindingSource". This is a bit cumbersome, but does work.

    Conclusion

    Extending the binding source component mostly solves the problem of ensuring that data edits are properly saved. This solves the number one complaint of users of Window Forms or Smart Client applications. This is a reusable component that may be copied to and easily implemented in other applications.

    About the Author

    David Catherman

    Email: David (dot) Catherman (at) Hotmail (dot) com

    David Catherman has 20+ years designing and developing database applications with specific concentration for the last 5-6 years on Microsoft .NET and SQL Server. He is currently working as an Application Architect and Senior Developer using Visual Studio 2005 and SQL Server 2005. He has several MCPs in .NET and is pursuing MCSD.

    References:

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Jul 21, 2005 - N-Tier Web Applications using ASP.NET 2.0 and SQL Server 2005 - Part 1
    While the .NET Framework made building ASP.NET applications easier then it had ever been in the past, .NET 2.0 builds on that foundation in order to take things to the next level. This article shows you to how to construct an N-Tier ASP.NET 2.0 Web application by leveraging the new features of ASP.NET 2.0 and SQL Server 2005.
    [Read This Article]  [Top]
    Apr 28, 2005 - New Files and Folders in ASP.NET 2.0
    With the release of ASP.NET 2.0, Microsoft has greatly increased the power of ASP.NET by introducing a suite of new features and functionalities. As part of this release, ASP.NET 2.0 also comes with a host of new special files and folders that are meant to be used to implement a specific functionality. This article examines these new files and folders in detail and provides examples that demonstrate how to utilize them to create ASP.NET 2.0 applications.
    [Read This Article]  [Top]
    Mar 10, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 2, Cont'd
    Alex Homer continues his detailed look at the major changes to the DataSet class. In this part, he looks at two features that allow developers to work with data in a more structured and efficient way when using the DataSet with a SQL Server 2005 database server.
    [Read This Article]  [Top]
    Mar 9, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 2
    Alex Homer continues his detailed look at the major changes to the DataSet class. In this part, he looks at two features that allow developers to work with data in a more structured and efficient way when using the DataSet with a SQL Server 2005 database server.
    [Read This Article]  [Top]
    Mar 3, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 1, Cont'd
    In this article, Alex Homer looks at the changes between the version 1.x and version 2.0 DataSet and their associated classes, showing you how you can take advantage of the new features to improve your applications' capabilities and performance.
    [Read This Article]  [Top]
    Mar 2, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 1
    In this article, Alex Homer looks at the changes between the version 1.x and version 2.0 DataSet and their associated classes, showing you how you can take advantage of the new features to improve your applications' capabilities and performance.
    [Read This Article]  [Top]
    Feb 16, 2005 - Writing a Custom Membership Provider for the Login Control in ASP.NET 2.0
    In ASP.NET 2.0 and Visual Studio 2005, you can quickly program custom authentication pages with the provided Membership Login controls. In this article, Dina Fleet Berry examines the steps involved in using the Login control with a custom SQL Server membership database.
    [Read This Article]  [Top]
    Dec 29, 2004 - ClickOnce Deployment in .NET Framework 2.0
    In this article, Thiru Thangarathinam examines .NET 2.0's new ClickOnce deployment technology that is designed to ease deployment of Windows forms applications. This new technology not only provides an easy application installation mechanism, it also eases deployment of upgrades to existing applications.
    [Read This Article]  [Top]
    Dec 15, 2004 - A Sneak Peek at ASP.NET 2.0's Administrative Tools
    With ASP.NET 2.0, Microsoft has made great strides in increasing developer productivity and has made implementing previously complex solutions relatively easy. Where this version of ASP.NET really shines, however, is in its new administrative tools that allow developers to spend less time managing the configuration of the servers and software and more time developing great code.
    [Read This Article]  [Top]
    Nov 17, 2004 - The ASP.NET 2.0 TreeView Control
    Thiru Thangarathinam introduces ASP.NET 2.0's new TreeView control which provides a seamless way to consume and display information from hierarchical data sources. The article discusses this new control in depth and explains how to use this feature rich control in your ASP.NET applications.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers

    Solutions
    Whitepapers and eBooks
    IBM eBook: Planning a Service Oriented Architecture
    IBM eBook: Choosing the Right Architecture--What It Means for You and Your Business
    Microsoft Article: Will Hyper-V Make VMware This Decade's Netscape?
    Avaya Article: Using Intelligent Presence to Create Smarter Business Applications
    Intel Go Parallel Article: Getting Started with TBB on Windows
    Microsoft Article: 7.0, Microsoft's Lucky Version?
    Avaya Article: How to Feed Data into the Avaya Event Processor
    IBM Article: Developing a Software Policy for Your Organization
    Microsoft Article: Managing Virtual Machines with Microsoft System Center
    Intel Go Parallel Article: Intel Threading Tools and OpenMP
    HP eBook: Storage Networking , Part 1
    Microsoft Article: Solving Data Center Complexity with Microsoft System Center Configuration Manager 2007
    MORE WHITEPAPERS, EBOOKS, AND ARTICLES
    Webcasts
    HP Video: StorageWorks EVA4400 and Oracle
    HP Webcast: Storage Is Changing Fast - Be Ready or Be Left Behind
    Microsoft Silverlight Video: Creating Fading Controls with Expression Design and Expression Blend 2
    MORE WEBCASTS, PODCASTS, AND VIDEOS
    Downloads and eKits
    Red Gate Download: SQL Toolbelt and free High-Performance SQL Code eBook
    Iron Speed Designer Application Generator
    MORE DOWNLOADS, EKITS, AND FREE TRIALS
    Tutorials and Demos
    Silverlight 2 App and Walkthrough: Leverage Silverlight 2 with SQL Server and XML
    IBM Article: Enterprise Search--Do You Know What's Out There?
    HP Demo: StorageWorks EVA4400
    Microsoft Article: The Progress and Promise of Deep Zoom
    Microsoft How-to Article: Get Going with Silverlight and Windows Live
    MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES