Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for Data Binding

March 7, 2014

AngularJS in Visual Studio: Data Binding

Filed under: AngularJS,Data Binding,Visual Studio @ 12:15 pm

This is a continuation of the World’s Simplest AngularJS Example in Visual Studio post. This post adds simple data binding to the example.

Most .NET developers understand data binding from Windows Forms, Web Forms, or XAML.

For example, in Windows Forms you can bind a LastNameTextbox control’s Text property to a LastName property of a Customer class (or LastName field in a data source). When the form displays, the Textbox is automatically populated. If the user changes the contents of the Textbox, the property (or field) is automatically updated.

image

Angular provides this same type of two-way data binding. It automatically synchronizes HTML elements with data properties.

The key to data binding with Angular is the ng-Model directive. The ng-Model directive binds HTML elements to a property. If no property exists, the property will be created implicitly. We’ll use that default functionality to perform data binding without any real properties for this simple example.

Let’s try some very simple data binding:

<!DOCTYPE html>
<html xmlns=”
http://www.w3.org/1999/xhtml”>
<head>
    <title>Acme Customer Management</title>
</head>
<body ng-app>
    <p>Customer Name: <input type=”text” 
                             ng-model=”customerName” /></p>
    <div>{{customerName}} Orders as of {{ “2014-01-31”}} </div>

    <script src=”Scripts/angular.js”></script>
</body>
</html>

  • The above code sets the ng-model directive as an attribute on the input tag.
  • The value of the ng-model directive is the name of the property to which this input tag is bound. In this case, the property is named “customerName”.
    • This binds the input tag to the defined property.
    • As the user types into the input box, the customerName property is automatically updated.
    • This property is not defined anywhere, so Angular creates it implicitly.
  • The div tag displays the value of the property by including it in double curly braces.
    • Double-curly braces define a “binding” and can contain any Angular expression.
    • In this case, the expression is just the property name.
    • So the value of the property will appear where the binding is defined.
  • As the user enters text into the input box, that text is immediately displayed in the div tag.

Run it, and you get:

image

Enter some text, and it appears as follows:

image

We’ll see lots more about binding once we connect to some data.

Enjoy!

PS circle

Check out my Pluralsight courses!

February 27, 2010

Binding to a ComboBox using a DataTable and Linq

If you retrieve data into a DataTable, it is easy to bind it to a ComboBox. And before Linq, you would filter the DataTable using a DataView. But with Linq, you have some easy to use features for filtering the contents of your ComboBox.

First, here is the basic code for binding a ComboBox to a DataTable.

In C#:

private DataTable dt = Customers.Retrieve();

ComboBox1.DataSource = dt;
ComboBox1.DisplayMember = "FullName";
ComboBox1.ValueMember = "CustomerId";

In VB:

Private dt As DataTable = Customers.Retrieve

ComboBox1.DataSource = dt
ComboBox1.DisplayMember = "FullName"
ComboBox1.ValueMember = "CustomerId"

The Retrieve method on the Customers class retrieves a DataTable of customers. If you want to try this code, you can build a DataTable in code following the techniques covered here.

You can then set the DataSource to the DataTable, set the DisplayMember to the name of the field to display in the ComboBox, and set the ValueMember to the name of the field to use as the field value. This is most often the unique key.

The result looks like this:

image

With Linq you can add filtering criteria. So let’s add a second ComboBox that lists only the customers with a last name that starts with "B".

NOTE: Be sure to set a reference to System.Data.DataSetExtensions.

In C#:

var query = dt.AsEnumerable().Where(c=>
        c.Field<String>("LastName").StartsWith("B"));

ComboBox2.DataSource = query.AsDataView();
ComboBox2.DisplayMember = "FullName";
ComboBox2.ValueMember = "CustomerId";

In VB:

Dim query = dt.AsEnumerable.Where(Function(c) _
       c.Field(Of String)("LastName").StartsWith("B"))

ComboBox2.DataSource = query.AsDataView
ComboBox2.DisplayMember = "FullName"
ComboBox2.ValueMember = "CustomerId"

This code uses a Lambda expression to filter the DataTable to only those rows where the LastName starts with "B".

The AsEnumerable extension method is necessary to allow Linq/Lambda expressions to work with a DataTable. Any field in the DataTable is accessed using c.Field<T> Or c.Field(Of T) where T is the type of the field. In this example, the field is a string.

The second ComboBox is then bound to the query using the AsDataView extension method. This allows the binding to bind the result as a DataView.

The result looks like this.

image

But wait, there is more. Because of the way that binding works. adding rows to the DataTable will add rows to the first ComboBox that is bound to the DataTable. AND if the new row starts with the letter "B", it will add it to the second ComboBox as well.

In this example, code in the Add button does this:

In C#:

private void Button1_Click(object sender, EventArgs e)
{
    dt.Rows.Add(5, "Bond", "James", "Bond, James", DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, 3);
}

In VB:

Private Sub Button1_Click2(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Button1.Click
    dt.Rows.Add(5, "Bond", "James", "Bond, James", DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, 3)
End Sub

Click the button and the result is as follows:

image

No refreshing or re-executing binding code required. It just works!!

Use this technique any time you want to bind a ComboBox to a filtered set of data from a DataTable.

Enjoy!

October 20, 2009

Populating a DataGridView from Xml Data

Filed under: C#,Data Binding,LINQ,VB.NET,WinForms,XML @ 11:49 pm

If you are using XML in a WinForms application you may find the need to display the XML data in a DataGridView.

Let’s take this XML:

<states>
    <state name="California">
        <abbreviation>CA</abbreviation>
        <year>1850</year>
        <governor>Schwarzenegger</governor>
    </state>
    <state name="Wisconsin">
        <abbreviation>WI</abbreviation>
        <year>1848</year>
        <governor>Doyle</governor>
    </state>
</states>

Displaying XML in a DataGridView sounds easy, but if you just set the DataGridView DataSource to the XML data, you will get something like this:

image

Notice how it has lots of yuck in it: attribute details, node properties, and so on for every node in the XML. So how do you get something more like this:

image

The trick is to use anonymous types.

The code is provided here in VB and C# and then described in detail below.

NOTE: Be sure to set a reference to System.Core and System.Xml.Linq

In C#:

XElement statesXml = XElement.Parse("<states>" +
    "<state name=’California’>" +
        "<abbreviation>CA</abbreviation>" +
        "<year>1850</year>" +
        "<governor>Schwarzenegger</governor>" +
    "</state>" +
    "<state name=’Wisconsin’>" +
        "<abbreviation>WI</abbreviation>" +
        "<year>1848</year>" +
        "<governor>Doyle</governor>" +
    "</state>" +
   "</states>");

var query = from st in statesXml.Descendants("state")
            select new
            {
                Name = st.Attribute("name").Value,
                Abbrev = st.Element("abbreviation").Value,
                Year = st.Element("year").Value,
                Governor = st.Element("governor").Value
            };
DataGridView1.DataSource = query.ToList();

In VB:

Dim statesXml As XElement = _
    <states>
        <state name="California">
            <abbreviation>CA</abbreviation>
            <year>1850</year>
            <governor>Schwarzenegger</governor>
        </state>
        <state name="Wisconsin">
            <abbreviation>WI</abbreviation>
            <year>1848</year>
            <governor>Doyle</governor>
        </state>
    </states>

Dim query = From st In statesXml…<state> _
            Select New With { _
                  .Name = st.@name, _
                  .Abbrev = st.<abbreviation>.Value, _
                  .Year = st.<year>.Value, _
                  .Governor = st.<governor>.Value}
DataGridView1.DataSource = query.ToList

The first part of this code builds the XML. The C# code uses the XElement.Parse function to build the XML; VB uses XML literals. This part of the code is not necessary if you are reading the XML from another source, such as a file.

The second part of the code leverages Linq to XML to process the set of state XML elements. For each element, it uses the Select New syntax to create an anonymous type. The syntax defines an unnamed (anonymous) type with properties Name, Abbrev, Year, and Governor.

[To view an overview of anonymous types, start here.]

The last line converts the results of the query to a generic list and assigns it to the DataSource property of the DataGridView. Visual Studio uses the anonymous type property names as the text for the column titles and populates the rows with each state element.

Use this technique any time you have XML that you want to display in a DataGridView.

Enjoy!

September 16, 2009

Binding Control Properties to Business Object Properties

Filed under: C#,Data Binding,VB.NET,WinForms @ 6:32 pm

As stated here, you use object binding in a WinForms application by following these steps:

1. Build the business objects for your application.

2. Define a business object data source in the Windows Application
project containing your user interface.

3. Bind properties of the controls on the form to business object
properties.

The first two steps were detailed here. This post covers the third step.

NOTE: You will not be able to follow along with the information in this post unless you perform steps 1 and 2 first from my prior post.

Binding to Existing Controls

Once you have at least one data source set up in your Data Sources window, you can bind it to your user interface. The technique you use to set up the binding is slightly different, depending on whether you are working with existing controls or creating new controls. This section describes the former, and the next section details the latter.

The process of binding existing controls on a form to a data source is
referred to as connect-the-dots binding. It is as easy as connecting point
A to point B.

To bind an existing control to a business object property displayed in
the Data Sources window:

1. Open the form you want to bind in the Forms Designer.

2. Open the Data Sources window. Position the Data Sources window so that you can easily see its contents and the Forms Designer at the same time.

3. Drag the desired property from the Data Sources window, and
drop it on the appropriate control on the form to bind them.

Repeat this process for every control on the form that needs to be
bound to the business object.

The first time you drop a property from an object data source to a form, Visual Studio adds a BindingSource component to the form’s component tray and names it based on the name of your business object class. This component manages the binding between the form controls and the
business object properties.

The default property of the control that you used as your drop target is bound to the BindingSource component for the property that was dragged from the Data Sources window. You can see this by using the Properties window to examine the control’s DataBindings property.

For example, add a TextBox to an empty form. Then drag the LastName property from the Customer object data source in the Data Sources window and drop it on the TextBox control. This binds the Text property of the TextBox control to the LastName property of the Customer class by way of the CustomerBindingSource. The result is shown below.

image

As shown in the Properties window, the Text property of the TextBox control is now bound to the LastName property by way of the CustomerBindingSource.

The binding information for the BindingSource component and for each bound control is retained in the form’s partial class. When you perform the binding, Visual Studio sets the appropriate control properties.

The code Visual Studio adds to the partial class for the BindingSource
component is as follows

In C#:

this.customerBindingSource.DataSource =
                                 
typeof(SampleBoCSharp.Customer);

In VB:

Me.CustomerBindingSource.DataSource = GetType(SampleBoVB.Customer)

This defines that the CustomerBindingSource is associated with the Customer class in the associated project.

Visual Studio adds code to the partial class for each bound control as
follows

In C#:

this.textBox1.DataBindings.Add(
      new System.Windows.Forms.Binding("Text",
      this.customerBindingSource, "LastName", true));

In VB:

Me.TextBox1.DataBindings.Add( _
     New System.Windows.Forms.Binding("Text", _
     Me.CustomerBindingSource, "LastName", True))

This code adds a binding entry for the TextBox control. The binding entry defines that the Text property of the TextBox is bound to the LastName property in the CustomerBindingSource. The last parameter defines that formatting is enabled to allow formatting of the value.

NOTE: Although you do not normally need to look at this generated code, there is one case where you may need to know about it. If you use the renaming feature, you will see that the Rename renames only direct references to the property or method, not any occurrences in quoted strings. So if you use
the rename feature and rename LastName to CustomerLastName, your binding no longer works, because it is still using LastName. You must locate any partial class code that references the property as a quoted string and manually change its name.

Before you can successfully run the application, you need to write some code. So far, you have defined that the BindingSource component binds to a business object. And you have defined which properties of the business object are to appear in which control of the form. However, you did not define which business object it binds to.

To define which instance of the business object to bind to, assign the DataSource property of the BindingSource component to a business object instance as follows:

In C#:

Customer cust = new Customer()
        {LastName = "Baggins",
        FirstName = "Bilbo"};

this.customerBindingSource.DataSource = cust;

In VB:

Dim cust= New Customer With  _
          {.LastName = "Baggins", _
          .FirstName = "Bilbo"}

Me.CustomerBindingSource.DataSource = cust

This example binds the controls in the user interface to a new instance of the Product class. Place this binding code in the form. The specific location in the form depends on when you want the binding to occur. If you want the binding to occur when the form is loaded, add this code to the form’s Load event.

By setting that instance as the DataSource for the CustomerBindingSource, the runtime uses the properties associated with that instance to populate any controls on the form bound to the CustomerBindingSource. So by writing only two lines of code, when you run your application, your form appears populated with appropriate data.

image

If the user changes a value in a control on the form, the runtime assigns the revised value to the associated property.

NOTE: The business object property is assigned to the value from the control when the user leaves the control. By default, if the user modifies a value and then closes the form before leaving the control, the bound property is not changed. However, if you call the Validate method, the current control’s value is assigned to its associated property even if the user does not leave the control.

Binding to New Controls

Now for the really fun part. In addition to binding to existing controls you can use the features of the Data Sources window to add new controls to a form. Or you can use it to automatically create controls for all of the business object properties.

Adding New Controls Using the Data Sources Window

If you want to add a new control to a form, you can add the control from the Forms Designer toolbox, add the associated Label control, and then bind the new control using the techniques from the preceding section.

But the Data Sources window provides a shortcut for this process. If you drag a property from the Data Sources window and drop it on the Forms Designer, Visual Studio creates the control, binds it, and creates the associated Label control. All you need to do is drag and drop!

To add a new control to a form:

1. Open the form in the Forms Designer.

2. Open the Data Sources window. Position the Data Sources window so that you can easily see its contents and the Forms Designer at the same time.

If a Forms Designer window is active, the Data Sources window changes so that each object data source and each property under the object data source displays an icon to the left of the name. This icon indicates the type of control that Visual Studio creates if you drag the item to the Forms Designer.

image

3. Drag a property from the Data Sources window, and drop it on the
Forms Designer
.

Visual Studio automatically creates the control associated with the dropped property and binds it. It even gives the control a valid name using the property name and the control type, such as FirstNameTextBox—no lame TextBox1 name. In addition, Visual Studio creates an appropriate Label control. Visual Studio is smart about generating the text for the Label control. It breaks the property name into separate words based on either alphabetic casing or underscores.

For example, if you drag the FirstName property from the Data Sources window and drop it in the CustomerWin form, Visual Studio creates a TextBox control for the First Name and an appropriate Label control.

image

But what if you don’t want the property to render as a TextBox control? You can change the type of control that is rendered using the Data Sources window.

To change the default control type associated with a property:

1. Ensure that a Forms Designer is active.

2. Open the Data Sources window.

3. Click a property in the Data Sources window. The property item changes to a drop-down control list.

4. Drop down the control list and select the desired control type as shown in the prior screen shot of the Data Sources Window.

Select Customize from the list to select a control type that is not on the list. You can select just about any type of control, including third-party and
custom controls.

NOTE: Before a third-party control can be added as a control type in the Data Sources window, it must first be added to the Forms Designer toolbox.

When you drag the control from the Data Sources window to the Forms Designer, Visual Studio renders the type of control you selected as the default control type for the property. The property retains its default control type in the Data Sources window.

Creating a Form from the Data Sources Window

But wait—there’s more! You can use the Data Sources window to create all the controls for a business object and bind them.

To create a new bound form:

1. Add a new Windows Form to your project.

2. Open the Data Sources window. Position the Data Sources window so that you can easily see its contents and the Forms Designer window at the same time.

3. In the Data Sources window, click the object data source you want
to bind to this form. The item changes to a drop-down control list.

4. Use the drop-down control list to define how Visual Studio renders controls on the form for this object data source.

The class object data source can be rendered as Details, DataGridView, or None, or you can select Customize from the control list to select a control type that is not on the list.

For a data entry form, set the control type for the object data source to Details. Visual Studio renders individual controls for each property in the object data source, along with associated Label controls. Select DataGridView to render the properties in the object data source as columns in a grid.

5. Drag the object data source node from the Data Sources window and drop it on the form.

NOTE: If you plan to use Panel or other container controls on the form, place the Panel controls or other containers on the form first, and then drag the object data source node and drop it on the desired container.

Bang! Visual Studio automatically creates the controls as you specified and binds them. If you selected Details rendering, Visual Studio creates a control based on the default control type for each object data source property in the Data Sources window. It sets each control’s Name property based on the name of the associated business object property and control type. Visual Studio also defines an appropriate Label control for each control it creates. If you selected DataGridView rendering, Visual Studio defines a grid with appropriate columns and column header text.

For example, if you set up the Customer object data source to render as Details and then drag the Customer node from the Data Sources window and drop it on a form, Visual Studio creates a TextBox control for every property, binds it, names it, and creates a Label control, as shown below.

image

Notice that this process created two components in the form’s component tray. The BindingSource is the component that manages the binding. The BindingNavigator added VCR-style controls to the top of the form. This type of user interface is not recommended for most business applications. (How often does a user want to sequentially navigate through every customer, for example?) You can delete the BindingNavigator component, and Visual Studio automatically deletes the VCR-style controls.

The labels created for the controls are generated based on the property names. Visual Studio is smart enough to add spaces between words based on underscores or alphabetic casing. This provides a great starting point for your user interface.

The controls are added in alphabetical order, which may not be the most logical order or placement for your users. You can move and size the controls as desired. After you have the controls in their desired order, use the Tab Order view to reset the tab order.

All you need then are those two lines of code in the form’s Load event to
define the instance of the business object that is bound to the controls:

In C#:

Customer cust = new Customer()
        {LastName = "Baggins",
        FirstName = "Bilbo"};

this.customerBindingSource.DataSource = cust;

In VB:

Dim cust= New Customer With  _
          {.LastName = "Baggins", _
          .FirstName = "Bilbo"}

Me.CustomerBindingSource.DataSource = cust

When you run the application, the runtime automatically populates the
controls on the form.

image

The Data Sources window allows you to create all the controls on a form and bind them to a business object with one drag-and-drop operation and two lines of code. It has the flexibility to allow you to define the type of control to render for each property in the object data source.

Using these techniques can make quick work of building and maintaining
your user interface.

(Based on an except from "Doing Objects in Visual Basic 2005".)

Enjoy!

September 8, 2009

Using Object Binding

Filed under: C#,Data Binding,VB.NET,Visual Studio,WinForms @ 5:23 pm

As stated here, you use object binding in a WinForms application by following these steps:

1. Build the business objects for your application.

2. Define a business object data source in the Windows Application
project containing your user interface.

3. Bind properties of the controls on the form to business object
properties.

Building the Business Objects

The idea with object binding is to bind your business objects to your user interface controls. So the first step is to create the business objects. This example builds a Customer class.

NOTE: Add a Class Library project to your solution and create the Customer class in the Class Library project. You could add the Customer class to your WinForms project, but it is not recommended.

In C#:  

public class Customer
{
    public int CustomerId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string EmailAddress { get; set; }
}

The above code takes advantage of automatically implemented properties. VB does not yet have this feature, but it is expected with VB 10.

In VB:

Public Class Customer

    Private _CustomerId As Integer
    Public Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
        Set(ByVal value As Integer)
            _CustomerId = value
        End Set
    End Property

    Private _FirstName As String
    Public Property FirstName() As String
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value
        End Set
    End Property

    Private _LastName As String   
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value
        End Set
    End Property

    Private _EmailAddress As String
    Public Property EmailAddress () As String
        Get
            Return _EmailAddress
        End Get
        Set(ByVal value As String)
            _EmailAddress = value
        End Set
    End Property
End Class

Configuring a Data Source

In Visual Studio, a data source defines the source of data, such as a business object or a database. To use a data source to bind your user interface, you need to configure the data source for the project containing your user interface, such as your Windows Application project.

For object binding, each data source represents a single business object class. This means that you configure a data source for each business object class that you want to bind. Luckily, this process is quick and easy.

When you use object binding, the data source is referred to as an object data source. This is technically accurate because at runtime, the binding binds to a specific business object. However, this can look a little confusing at design-time because each data source represents a single business object class. The Data Sources window displays each object data source with the class name and lists the public properties of the class. You then bind each property of the class to a control on the form.

To set up an object data source for your user interface:

1. Build your business object Class Library project.
Only compiled business object classes are recognized by the Data
Source Configuration Wizard and the Data Sources window.

2. Select the Windows Application project in Solution Explorer.
NOTE: Always ensure that the Windows Application project is selected in
Solution Explorer before you work with the Data Sources window.

3. Select Data | Show Data Sources from the main menu bar.
The Data Sources window is displayed.

4. Click the Add New Data Source link in the Data Sources window, or click the Add New Data Source button on the Data Sources window toolbar, or select Data | Add New Data Source from the main menu bar.

NOTE: The Add New Data Source link only appears when the Data Sources
window is empty.

This launches the Data Source Configuration Wizard. The first page, shown below, allows you to select the source of the data.

image

5. Select Object for object binding, and click Next.

The second page of the Data Source Configuration Wizard, shown below, provides the list of classes for your selection.

image

The tree view only lists the classes in the Windows Application
project and classes in any component referenced by the Windows
Application project. If you already have a reference to your business object Class Library component, the component appears in the Data Source
Configuration Wizard. Otherwise, you can add a reference using the Add Reference button.

6. Use the tree view to navigate to the class you wish to use for object binding.

image

7. Select the desired class, and click Finish.
The object data source for the selected class is added to the Data
Sources window, as shown below.

image

NOTE: The Data Sources window lists only public class properties.

Use the Data Source Configuration Wizard to define a data source for every business object class you plan to use for binding.

If you are curious about where the Data Sources window stores its information, it is in a set of XML files. Select the Windows Application project in Solution Explorer, and click the Show All Files button on the Solution Explorer toolbar. All the system files for the project are then accessible from Solution Explorer. Open the Properties node (My Project node in VB) under the Windows Application project, and open the DataSources node to see the XML files for the data sources in the Data Sources window.

Creating data sources is quick and easy. Add a data source to the Windows Application project for any business object class that you want to use for binding to your user interface.

Bind the User Interface Controls

There are many ways to bind the resulting object data source to your user interface. These are covered in a later post.

(Based on an except from "Doing Objects in Visual Basic 2005".)

Enjoy!

Understanding Object Binding

Filed under: C#,Data Binding,OOP,VB.NET,Visual Studio @ 2:26 pm

Before going through the details of how to use object binding, it is important to understand exactly what it is—and what it is not. Object binding is binding your business object properties to user interface elements. Object binding is not database binding in the strict sense of the term. It does not directly collect or bind any data from your database.

When you are using business object classes without object binding, the flow of data from the database to your user interface and back again requires these steps:

1. The business object calls the data access component to get the data from the database and sets the business object properties using that data.

For example, the Product class calls the data access component, which uses a query or stored procedure to fill a DataTable from the Product table. The data access component returns the DataTable to the Product class, which assigns each field from the table to a property of the object. To illustrate, the line of code required to get the ProductName field from the DataTable and set the
ProductName property is as follows:

In C#:

myProduct.ProductName = dt.Rows[0]["ProductName"];

In VB:

myProduct.ProductName = dt.Rows(0).Item("ProductName")

2. The user interface component accesses the business object properties to fill the values of the controls on the form.

For example, each control on the ProductWin form is assigned to the value of the appropriate Product business object property. To illustrate, the line of code required to set the Text property of the Name TextBox control to the ProductName property of the Product business object is as follows:

In C#:

NameTextBox.Text = myProduct.ProductName;

In VB:

NameTextBox.Text = myProduct.ProductName

3. After the user makes any changes, the user interface component assigns the current values in the controls back to the business object properties.

For example, the value in each control on the ProductWin form is assigned back to its associated Product business object property. To illustrate, the line of code required to set the ProductName property to the current value in the Name TextBox control is as follows:

In C#:

myProduct.ProductName = NameTextBox.Text;

In VB:

myProduct.ProductName = NameTextBox.Text

4. The business object component updates the DataTable using the property values and passes it back to the data access component, which updates the database with the changed data.

For example, the value of each Product business object property is assigned to the associated field in the DataTable, and the result is passed to the data access component, which updates the Product table. To illustrate, the line of code required to set the ProductName field in the DataTable to the value of the ProductName business object property is as follows:

In C#:

dt.Rows[0]["ProductName"] = myProduct.ProductName;

In VB:

dt.Rows(0).item("ProductName") = myProduct.ProductName

Using object binding allows you to skip steps 2 and 3. Object binding automatically populates the controls on the user interface from the business object properties. As the users change the contents of the controls, object binding updates the associated business object properties, keeping them in synchronization.

That still leaves steps 1 and 4 for you. This link provides information on building a data access component to handle steps 1 and 4.

In summary, object binding is the process of binding control properties directly to properties of your business objects. For example, you could bind the Text property of a TextBox control to the ProductName property of a Product business object. When the form is displayed, the runtime automatically displays the value of the ProductName property in the TextBox. And if the user changes the text in the TextBox control, the runtime modifies the ProductName property accordingly. This saves you from writing the code required to transfer data back and forth between the controls on the user interface and the business object properties.

Visual Studio provides design-time tools for working with your business objects as data sources for your user interface, making it easy to bind each control to its associated business object property. The only requirement for your business objects to work with these tools is that the business object class needs at least one public property. No specific constructors, interfaces, or attributes are needed.

Object Binding Versus Data Binding

Don’t confuse the term object binding with the more generalized term data binding. Data binding is the broad term for binding control properties to data from any data source. Object binding is just one type of data binding. Some common types of data binding are as follows:

  • Binding to tables in a database (Visual Studio generates code to
    define a typed DataSet and TableAdapters)
  • Binding to stored procedures in a database (Visual Studio generates
    code to define a typed DataSet and TableAdapters)
  • Binding to a business object (object binding does not generate code;
    it just sets control properties)
  • Binding to an array or collection of data
  • Binding to a Web service

When binding to a database, Visual Studio generates a significant amount of code and then binds the user interface to that generated code. Object binding binds to your code. That gives you much more control and greatly simplifies the maintenance of your application.

Using Object Binding

You use object binding by following these steps:

1. Build the business objects for your application.

2. Define a business object data source in the Windows Application
project containing your user interface.

3. Bind properties of the controls on the form to business object
properties.

Although it is much easier to think about object binding as a direct binding of a control’s property to a specific business object’s property, object binding frequently uses a BindingSource component as an intermediary. A BindingSource is a component on a form that binds the controls on the form to the business object. Each control is bound to the BindingSource component, which in turn is bound to the business object. This makes it much easier to change the binding for all controls by changing the BindingSource without having to separately rebind each control.

You set the BindingSource to an individual business object instance in your code. The runtime then binds all the properties associated with that instance to the controls, thereby displaying the business object property values in the controls. And as the user changes the content of any controls, the business object property values are changed accordingly.

A form can contain multiple BindingSource components. For example, a ProductWin form can contain product data and display a drop-down list of product types. You can define a BindingSource component for the product data and a second BindingSource component for the product type data.

(Based on an except from "Doing Objects in Visual Basic 2005".)

For more information on object binding, see these links:

Enjoy!

August 14, 2009

Using Linq with Microsoft Word and Excel

Some of the collections in the Microsoft Office object models implement IEnumerable. The IEnumerable interface provides the ability to perform a for/each against the collection. With .NET 3.5, a Cast extension method of IEnumerable allows you to work with these collections using Linq.

Microsoft Word

For example, say you want to bind the set of open Word document names in a ComboBox.

First, set a reference to the desired version of the Microsoft Word Object Library from the COM tab of the Add Reference dialog. The resulting reference appears as Microsoft.Office.Interop.Word.

In C#:

// Add to the top of the code file
using Word = Microsoft.Office.Interop.Word;

// Add to a subroutine
Word.Application Wd;
Word.Document doc;
Word.Document doc2;
object missingValue = Missing.Value;

// Start Word and get Application object
Wd = new Word.Application();

// Define documents
doc = Wd.Documents.Add(ref missingValue,ref missingValue,
ref missingValue,ref missingValue );
doc2 = Wd.Documents.Add(ref missingValue, ref missingValue,
ref missingValue, ref missingValue);

// Use Linq to access the document names.
var query = from d in Wd.Documents.Cast<Word.Document>()
select d.Name;
comboBox1.DataSource = query.ToList();

// Or use Lambda expressions
var query2 = Wd.Documents.Cast<Word.Document>().Select(d=> d.Name);
comboBox1.DataSource = query2.ToList();

// Close
doc.Close(ref missingValue, ref missingValue, ref missingValue);
doc = null;
doc2.Close(ref missingValue, ref missingValue, ref missingValue);
doc2 = null;
Wd.Quit(ref missingValue, ref missingValue, ref missingValue);

// Clean up
// NOTE: When in release mode, this does the trick
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect() ;

In VB:

‘ Add to the top of the code file
Imports Word = Microsoft.Office.Interop.Word

‘ Add to a subroutine
Dim Wd As Word.Application
Dim doc As Word.Document
Dim doc2 As Word.Document

‘ Start Word and get Application object
Wd = New Word.Application

‘ Define documents
doc = Wd.Documents.Add
doc2 = Wd.Documents.Add

‘ Use Linq to access the document names.
Dim query = From d In Wd.Documents.Cast(Of Word.Document)() _
Select d.Name
ComboBox1.DataSource = query.ToList

‘ Or use Lambda expressions
Dim query2 = Wd.Documents.Cast(Of Word.Document) _
.Select(Function(d) d.Name)
ComboBox1.DataSource = query2.ToList

‘ Close
doc.Close()
doc = Nothing
doc2.Close()
doc2 = Nothing
Wd.Quit()

‘ Clean up
‘ NOTE: When in release mode, this does the trick
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()

In both of these examples, the code starts Word, creates two Word documents, uses either Linq or a Lambda expression to define a query and then binds the resulting set of document names to a Combo Box.

Notice the missingValue variable in the C# code that is not in the VB code. VB supports default parameters, but C# does not. So any time a parameter is defined for a Word method, C# must provide it. VB will use the default parameter values.

NOTE: A new feature in C# 4.0 (Visual Studio 2010) allows for default parameters in C# as well, dramatically simplifying the C# code that interacts with Word or Excel.

As another example, the following code retrieves all of the words from the defined Word document.

In C#:

var query = from w in doc.Words.Cast<Word.Range>()
select w.Text;
comboBox1.DataSource = query.ToList();

In VB:

Dim query = From w In doc.Words.Cast(Of Word.Range)() _
Select w.Text
ComboBox1.DataSource = query3.ToList

This code retrieves all of the words in the document defined by the doc variable. Instead of selecting the list of words, you could use any Linq feature such as finding only a specific set of words that match a criteria or counting the number of occurrences of a given word.

Microsoft Excel

This technique works with Excel as well. Say you want to bind the list of spreadsheets in an Excel workbook.

First, set a reference to the desired version of the Microsoft Excel Object Library from the COM tab of the Add Reference dialog. The resulting reference appears as Microsoft.Office.Interop.Excel.

In C#:

// Add to the top of the code file
using Excel = Microsoft.Office.Interop.Excel;

// Add to a subroutine
Excel.Application oXL;
Excel.Workbook oWB;
Excel.Worksheet oSheet;

// Start Excel and get Application object.
oXL = new Excel.Application();

// Get a new workbook.
oWB = oXL.Workbooks.Add(Missing.Value);

// Get the active sheet and change its name
oSheet = (Excel.Worksheet)oWB.ActiveSheet ;
oSheet.Name = “Test”;

// Use Linq to access the spreadsheet names.
var query = from s in oXL.Worksheets.Cast<Excel.Worksheet>()
select s.Name;
comboBox1.DataSource = query.ToList();

// Or use Lambda expressions.
var query2 = oXL.Worksheets.Cast<Excel.Worksheet>()
.select(s => s.Name);
comboBox1.DataSource = query2.ToList();

// Close
oSheet = null;
oWB.Close(Missing.Value, Missing.Value, Missing.Value);
oWB = null;
oXL.Quit();

// Clean up
// NOTE: When in release mode, this does the trick
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

In VB:

‘ Add to the top of the code file
Imports Excel = Microsoft.Office.Interop.Excel

‘ Add to a subroutine
Dim oXL As Excel.Application
Dim oWB As Excel.Workbook
Dim oSheet As Excel.Worksheet

‘ Start Excel and get Application object.
oXL = New Excel.Application

‘ Get a new workbook.
oWB = oXL.Workbooks.Add

‘ Get the active sheet and change its name
oSheet = DirectCast(oWB.ActiveSheet, Excel.Worksheet)
oSheet.Name = “Test”

‘ Use Linq to access the spreadsheet names.
Dim query = From s In oXL.Worksheets.Cast(Of Excel.Worksheet)() _
Select s.Name
ComboBox1.DataSource = query.ToList

‘ Or use Lambda expressions
Dim query2 = oXL.Worksheets.Cast(Of Excel.Worksheet) _
.Select(Function(s) s.Name)
ComboBox1.DataSource = query2.ToList

‘ Close
oSheet = Nothing
oWB.Close()
oWB = Nothing
oXL.Quit()

‘ Clean up
‘ NOTE: When in release mode, this does the trick
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()

In both examples, the code starts Excel, changes the name of the active sheet, uses either Linq or a Lambda expression to define a query and then binds the resulting set of sheet names to a Combo Box.

Enjoy!

EDITED 11/16/09: Added information on setting the appropriate reference.

July 21, 2009

Building a Business Object Base Class

Filed under: C#,Data Binding,OOP,VB.NET @ 1:49 pm

If you are building applications in .NET to manage data for a business, you are most likely creating business object classes. Depending on the business, these classes could include Customer, Product, Order, Invoice, PurchaseOrder, Employee, TimeCard and so on.

A simple sample Customer class is shown here.

There are some features that all business objects need to support. For example:

  • Entity State: The business object needs to track whether it is new, updated, or deleted so it can make the appropriate change to the database.
  • Validation: Is the current data defined for the business object valid?
  • Save: Saves the changes to the database.

Notice that data retrieval functionality is not included in this list. That is because in most cases, you may never retrieve a single business object. Rather, you would retrieve a set of them. For example, all active orders or all customers with overdue invoices. So the retrieve functionality is not included in the class that manages a single object. (Most on this in a later post.)

Instead of repeating this common functionality in each business object, it makes sense to build a base class. Each business object can then inherit from this base class to share this common functionality.

So let’s get started.

In C#:

using System;
using System.ComponentModel;

public abstract class BoBase :
        IDataErrorInfo,
        INotifyPropertyChanged
{

}

In VB:

Imports System.ComponentModel

Public MustInherit Class BOBase
    Implements IDataErrorInfo
    Implements INotifyPropertyChanged

End Class

The class is abstract (MustInherit in VB) to indicate that it is meant to be a base class and not to be used on its own. An abstract class cannot be instantiated directly, so no other code can create an instance of the class.

The class then implements two interfaces:

  • IDataErrorInfo: This interface provides error information that the user interface can use to report validation errors to the user. It works well with the ErrorProvider control provided in WinForms and supports ASP.NET and WPF features.
  • INotifyPropertyChanged: This interface ensures that the user interface is notified when a property value changes, keeping your business object and user interface in sync.

We’ll look at the implementation of these interfaces shortly.

First, define the valid set of entity states.

In C#:

protected internal enum EntityStateType
{
    Unchanged,
    Added,
    Deleted,
    Modified
}

In VB:

Protected Friend Enum EntityStateType
    Unchanged
    Added
    Deleted
    Modified
End Enum

The Enum is declared Protected because the entity state should only be accessible from the business object itself. However, Internal (Friend in VB) was added so that related objects (such as Order and OrderLineItem) could reference the related object state.

The business object state is retained using an EntityState property.

In C#:

protected EntityStateType EntityState { get; private set; }

In VB:

Private _EntityState As EntityStateType
Protected Property EntityState() As EntityStateType
    Get
        Return _EntityState
    End Get
    Private Set(ByVal value As EntityStateType)
        _EntityState = value
    End Set
End Property

This property is protected, but the setter is private. So the business objects that inherit from this class can read the entity state, but only the base class can set the value.

Additional properties provide a way to get the entity’s state in an easier fashion. These properties are not required, but they make the base class a little easier to use.

In C#:

[BindableAttribute(false)]
[BrowsableAttribute(false)]
public bool IsDirty
{
    get { return this.EntityState != EntityStateType.Unchanged; }
}

[BindableAttribute(false)]
[BrowsableAttribute(false)]
public bool IsNew
{
    get { return this.EntityState == EntityStateType.Added; }
}

In VB:

<BindableAttribute(False)> _
<BrowsableAttribute(False)> _
Public ReadOnly Property IsDirty() As Boolean
    Get
        Return Me.EntityState <> EntityStateType.Unchanged
    End Get
End Property

<BindableAttribute(False)> _
<BrowsableAttribute(False)> _
Public ReadOnly Property IsNew() As Boolean
    Get
        Return Me.EntityState = EntityStateType.Added
    End Get
End Property

The IsDirty and IsNew properties are public, so they can be accessed from anywhere. They are marked with two attributes:

  • Bindable: Defines whether the property should be used for binding. In this case the value is false because the user interface should not be able to bind to this property.
  • Browsable: Defines whether the property should be displayed in the Properties window. Again, the value is false.

Two other properties handle the validation.

In C#:

[BindableAttribute(false)]
[BrowsableAttribute(false)]
public bool IsValid
{
    get { return (ValidationInstance.Count == 0); }
}

protected Validation ValidationInstance { get; set; }

In VB:

<Bindable(False)> _   
<BrowsableAttribute(False)> _
Public ReadOnly Property IsValid() As Boolean
    Get
        Return (ValidationInstance.Count = 0)
    End Get
End Property

Private _ValidationInstance As Validation
Protected Property ValidationInstance() As Validation
    Get
        Return _ValidationInstance
    End Get
    Private Set(ByVal value As Validation)
        _ValidationInstance = value
    End Set
End Property

Again, the public property is marked with the Browsable and Bindable attributes. The ValidationInstance property retains an instance of the Validation class for the business object. The code for the Validation class is here.

The constructor creates an instance of the Validation class.

In C#:

protected BoBase()

   ValidationInstance = new Validation();
}

In VB:

Protected Sub New()
    ValidationInstance = New Validation
End Sub

The following is the implementation of IDataErrorInfo.

In C#:

#region IDataErrorInfo Members

[BrowsableAttribute(false)]
[BindableAttribute(false)]
public string Error
{
    get { return ValidationInstance.ToString(); }
}

[BrowsableAttribute(false)]
[BindableAttribute(false)]
public string this[string columnName]
{
    get { return ValidationInstance[columnName]; }
}

#endregion

In VB:

#Region " Properties required by the IDataErrorInfo"

    <Bindable(False)> _   
    <BrowsableAttribute(False)> _
    Public ReadOnly Property [Error]() As String _
           Implements IDataErrorInfo.Error
        Get
            Return ValidationInstance.ToString
        End Get
    End Property

    <BrowsableAttribute(False)> _
    <Bindable(False)> _
    Default Protected ReadOnly Property Item(ByVal columnName _ 
           As String) As String _ 
           Implements IDataErrorInfo.Item
        Get
            Return ValidationInstance.Item(columnName)
        End Get
    End Property

#End Region

The Error property uses the overridden ToString method of the validation class to return the full list of validation errors.

The Item property provides access to the validation errors given a property name. This property is implemented as the class indexer in C#.

The following in the implementation of INotifyPropertyChanged.

In C#:

#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion

In VB:

#Region " Events required by INotifyPropertyChanged"
    Public Event PropertyChanged(ByVal sender As Object, _
        ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
        Implements INotifyPropertyChanged.PropertyChanged
#End Region

This interface only defines a single event. This event should be raised whenever the data is changed.

Since every business object will have unique requirements for the save operation, the SaveItem method is not implemented. Rather it is defined as abstract.

In C#:

public abstract Boolean SaveItem();

In VB:

Public MustOverride Function SaveItem() As Boolean

Defining an abstract (MustOverride in VB) SaveItem method ensures that every business object has a SaveItem method.

Finally, since the EntityState property setter is private, the base class needs a method to set the entity state.

In C#:

protected internal void SetEntityState(EntityStateType newEntityState)
{
    switch (newEntityState)
    {
        case EntityStateType.Deleted:
        case EntityStateType.Unchanged:
        case EntityStateType.Added:

            this.EntityState = newEntityState;
            break;

    default:
            if (this.EntityState == EntityStateType.Unchanged)
                this.EntityState = newEntityState;
            break;
    }
}

protected internal void SetEntityState(EntityStateType newEntityState, string propertyName)
{
    SetEntityState(newEntityState);
    if (PropertyChanged != null)
        PropertyChanged(this,
              
new PropertyChangedEventArgs(propertyName));
}

In VB:

Protected Friend Sub SetEntityState(ByVal dataState As EntityStateType)
    SetEntityState(dataState, Nothing)
End Sub

Protected Friend Sub SetEntityState( _
ByVal newEntityState As EntityStateType, ByVal propertyName As String)
    Select Case newEntityState
        Case EntityStateType.Deleted, _
             EntityStateType.Unchanged, _
             EntityStateType.Added

            Me.EntityState = newEntityState

        Case Else
            If Me.EntityState = EntityStateType.Unchanged Then
                Me.EntityState = newEntityState
            End If
    End Select

    If Not String.IsNullOrEmpty(propertyName) Then
        Dim e As New PropertyChangedEventArgs(propertyName)
        RaiseEvent PropertyChanged(Me, e)
    End If
End Sub

The SetEntityState method has two overloads. The first is used when changing the entity state in general and the second is used when changing the entity state because a specific property is changed.

For example, when setting an object as Unchanged, Added, or Deleted, it does not matter which property was changed. But when a particular property is changed, the code must also raise the PropertyChanged event.

In this case, the C# and VB code was implemented differently. In the C# code, the code to set the entity state is in the first overload. The second overload then calls the first and then raises the event.

In the VB code, the first overload simply calls the second overload. The second overload then sets the entity state and then raises the event as appropriate. You can use either technique in either language.

That’s it! You now have a base class that can be used with any business object class. If you have any other common functionality, you can add it to this base class.

Here is an example of how you use this base class.

In C#:

public class Customer : BoBase
{

    private string _LastName;
    public string LastName
    {
        get { return _LastName; }
        set
        {
            if (_LastName == null || _LastName != value)
            {
              string propertyName = "LastName";
              _LastName = value;

              // Validate the last name
              ValidationInstance.ValidateClear(propertyName);
              ValidationInstance.ValidateRequired(propertyName, value);

              SetEntityState(EntityStateType.Modified, propertyName);
            }
        }
    }

    public override Boolean SaveItem()
    {
        // TODO: Add code here
    }

}

In VB:

Public Class Customer
    Inherits BoBase

    Private _LastName As String
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            If _LastName Is Nothing OrElse _LastName <> value Then
              Dim propertyName As String = "LastName"
              _LastName = value

              ‘ Validate the last name
              ValidationInstance.ValidateClear(propertyName)
              ValidationInstance.ValidateRequired(propertyName, value)

              SetEntityState(EntityStateType.Modified, propertyName)
            End If
        End Set
    End Property

    Public Overrides Function SaveItem() As Boolean
        ‘ TODO: Add code here
    End Function
End Class

Notice how the class statement includes the syntax to inherit from BoBase. The LastName property uses the ValidationInstance defined in the base class to validate the value. It also sets the entity state when the last name is changed.

Enjoy!

July 14, 2009

Dates: Binding to Day Numbers

Filed under: C#,Data Binding,VB.NET,WinForms @ 4:07 pm

My prior post demonstrated how to bind to a list of month names. Once the user picks the desired month, you may want to provide a list of valid dates to pick a date in that month. For example: 1-30 for September and 1-31 for July.

You can accomplish this using a switch statement (Select Case in VB), but that hard-codes the dates, does not handle leap year, and does not support localization.

Another option is to use the culture specific calendar as shown below.

The code is first shown in both VB and C#. It is then described in detail below.

In C#:

private List<int> GetListOfDays(int yearNumber , int monthNumber)
{
    Calendar currentCalendar  = CultureInfo.CurrentCulture.Calendar;
    int numberOfDays  =
              currentCalendar.GetDaysInMonth(yearNumber, monthNumber);
    return Enumerable.Range(1, numberOfDays).ToList();
}

In VB:

Private Function GetListOfDays(ByVal yearNumber As Integer, _
                   ByVal monthNumber As Integer) As List(Of Integer)
  Dim currentCalendar As Calendar = CultureInfo.CurrentCulture.Calendar
  Dim numberOfDays As Integer = _
               currentCalendar.GetDaysInMonth(yearNumber, monthNumber)
  Return Enumerable.Range(1, numberOfDays).ToList()
End Function

This function first defines the calendar based on the current culture. It then uses the calendar’s GetDaysInMonth method to get the appropriate number of days in the month.

It then uses the Range method of Enumerable to build a list of numbers from 1 to the number of days in the month and returns it as a list of integers.

This method is called as follows:

In C#:

comboBox1.DataSource = GetListOfDays(2009, 2);

In VB:

ComboBox1.DataSource = GetListOfDays(2009, 2)

Enjoy!

Dates: Binding to Month Names

Visual Studio comes with DateTimePicker and MonthCalendar controls that provide a standard looking calendar for the user to pick a date. But there are times when these controls don’t provide the features you need.

For example, say you want to ask the user for the month and year that their credit card expires. Or you want to ask for a birth date and don’t want the user to have to scroll back to 1932. Or better yet, don’t need to know the year (not that I refuse to admit my birth year <G>.)

There is an easy way to achieve this.

The code is first shown in both VB and C#. It is then described in detail below.

In C#:

string[] MonthNames=
               CultureInfo.CurrentCulture.DateTimeFormat.MonthNames;
comboBox1.DataSource = MonthNames;

In VB:

Dim MonthNames As String() =  _
               CultureInfo.CurrentCulture.DateTimeFormat.MonthNames
ComboBox1.DataSource = MonthNames

This code gets the month names based on the current culture. This ensures that you get the correct localized names. You can then bind the array to a ComboBox or ListBox.

NOTE: In both cases, be sure to set a reference and import System.Globalization.

When looking at the list from the US (and most countries), there is a problem. There appears to be room for 13 values in the list. This is for those countries that have 13 months, such as Ethiopia.

So for most countries, there is a blank entry at the bottom of the list when it is bound. There are numerous ways of removing the empty entry. Even though it might not be the most performant, I like lambda expressions, so here is one way to remove the empty entry.

In C#:

comboBox1.DataSource = 
     MonthNames.Where(m => !string.IsNullOrEmpty(m)).ToList();

In VB:

ComboBox1.DataSource = _
    MonthNames.Where(Function(m) Not String.IsNullOrEmpty(m)).ToList()

Enjoy!

Next Page »

© 2023 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs