Introduction
As a developer, I really enjoy working with objects in the code behind. You can call them Classes or POCO or whatever, but it's easy and fun to use them and write code. However, one of the most repetitive and annoying tasks is to display object properties in UI, and then if a user changes something, get it back as an object in your code. The purpose of this utility/framework is to provide two-way binding effortlessly, so that with a few lines of code, you can bind your object's properties to UI, and then get it back in code behind with updated values without a bunch of manual coding.
Background
Microsoft ASP.NET has really nice controls for performing UI like the GridView
or the FormView
, that allows Data Binding. They are really great for automatically plugging values into the UI and displaying them, however, the so-called two way binding is terrible overall, but it's especially bad when it comes to working with objects.
When your user edits something in the UI, and you want to get it back as an object in code-behind, it's a lot of troublesome and repetitive work. You have 2 options:
- You can use an
ObjectDataSource
, which will provide 2 way binding, but you will have to write a separate adapter
class, and then write the Select
, Update
, Insert
and Delete
methods. - You can handle the binding events in the backend, and use
FindControl
to look up each control and then assign each property of the Object
to the UI Controls value manually. Ugh! This is especially cumbersome for grids.
I love the concept of two-way binding, but I find both these techniques annoying. Plus, I hate to place an ObjectDataSource
in the markup and hardcode methods and values in to it. I still like this first technique where you write a simple Adapter
class, and I quickly realized that since the steps are the same for each adapter
class, I can use Generics to create a utility that will automatically provide the adapter
, and I don't have to write it separately for each class. I searched for and reviewed several different frameworks and utilities provided for making two-way databinding happen and easily get the Object
back from UI in the code behind, but I didn't find anything that was very simple, so I ended up writing this very simple ObjectBinding
class.
Using the Code
So basically as I stated above, what this utility does is it allows mapping an object to a GridView
of FormView
or another ASP.NET control.
As an example, I'll use the following extremely simple classes:
Public Class Customer
Property CustomerID As String
Property FirstName As String
Property LastName As String
Property Status As String
Property Email As String
Property Addresses As New List(Of Address)
End Class
Public Class Address
Property AddressID As String
Property AddressLine As String
Property City As String
Property State As String
Property ZipCode As String
End Class
So Customer
is a simple object, and it has a list of Addresses
. What we will do is that we'll bind Customer
to a FormView
, and its related addresses to a GridView
, and then demonstrate editing Customer
Fields in the UI, or adding/removing/updating an Address
in the UI using the Grid
, and then get it back as an object in the code behind with updated values from the UI, with just a few simple lines of code.
So here is the markup. First, it's a FormView
for the Customer
.
<asp:FormView ID="frmCustomer" runat="server" DataKeyNames="CustomerID">
<ItemTemplate>
First Name: <asp:Label ID="lblFirstName" runat="server"
Text='<%# Eval("FirstName") %>'></asp:Label><br />
Last Name: <asp:Label ID="lblLastName" runat="server"
Text='<%# Eval("LastName") %>'></asp:Label><br />
Email: <asp:Label ID="lblEmail" runat="server"
Text='<%# Eval("Email") %>'></asp:Label><br />
Status: <asp:Label ID="lblStatus" runat="server"
Text='<%# Eval("Status") %>'></asp:Label><br />
<asp:LinkButton ID="lnkEdit" runat="server"
CommandName="Edit">Edit</asp:LinkButton>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtFirstName" runat="server"
Text='<%# Bind("FirstName") %>'></asp:TextBox><br />
<asp:TextBox ID="txtLastName" runat="server"
Text='<%# Bind("LastName") %>'></asp:TextBox><br />
<asp:TextBox ID="txtEmail" runat="server"
Text='<%# Bind("Email") %>'></asp:TextBox><br />
<asp:DropDownList ID="ddlStatus" runat="server" SelectedValue='<%# Bind("Status") %>'>
<asp:ListItem Text="Active" Value="Active"></asp:ListItem>
<asp:ListItem Text="Inactive" Value="Inactive"></asp:ListItem>
</asp:DropDownList>
<br />
<asp:LinkButton ID="btnSave" runat="server" CommandName="Update">Update
</asp:LinkButton>
<asp:LinkButton ID="btnCancel" runat="server" CommandName="Cancel">Cancel</asp:LinkButton>
</EditItemTemplate>
</asp:FormView>
As you can see, it's a very simple and basic FormView
with some controls that are bound to the Class Properties. We have an ItemTemplate
and also have the EditItemTemplate
.
Here is the markup for the GridView
for addresses:
<asp:GridView ID="grdAddresses" runat="server"
AutoGenerateColumns="False" DataKeyNames="AddressID">
<Columns>
<asp:BoundField DataField="AddressLine" HeaderText="Address" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="State" HeaderText="State" />
<asp:BoundField DataField="ZipCode" HeaderText="Zip" />
<asp:CommandField ShowEditButton="True" />
<asp:CommandField ShowDeleteButton="True" />
</Columns>
</asp:GridView>
The GridView
is even simpler than the FormView
.
So now, here is the code-behind:
Dim dsCustomer As New ObjectBinder(Of Customer)
Dim dsAddress As New ObjectBinder(Of Address)
Here is what we will do in the Page Init
event:
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
If Not Page.IsPostBack Then
Dim c As New Customer With {.FirstName = "Razi", .LastName = "Syed", _
.Email = "<a href="mailto:emailme@razisyed.com">emailme@razisyed.com", _
.Status = "Active"}
c.Addresses.Add(New Address With {.AddressLine = "123 Main St.", _
.City = "Houston", .State = "TX", .ZipCode = "77001"})
c.Addresses.Add(New Address With {.AddressLine = "987 Second St.", _
.City = "Austin", .State = "TX", .ZipCode = "77002"})
c.Addresses.Add(New Address With {.AddressLine = "456 Third St.", _
.City = "Dallas", .State = "TX", .ZipCode = "77003"})
dsCustomer.SetInstance(c)
dsAddress.SetList(c.Addresses)
End If
Dim odsCustomer = dsCustomer.GetDataSource
phDataSources.Controls.Add(odsCustomer)
frmCustomer.DataSourceID = odsCustomer.ID
Dim odsAddresses = dsAddress.GetDataSource
phDataSources.Controls.Add(odsAddresses)
grdAddresses.DataSourceID = odsAddresses.ID
End Sub
That's pretty much it! The customer
and addresses
will be displayed to the user. If the user edits/inserts/deletes using the formview
or gridview
, it will automatically update the object. The best part is that you can retrieve the object as shown below, without searching for controls and assigning properties manually.
Here is the code to retrieve the object:
Dim c As Customer = dsCustomer.Instance
c.Addresses = dsAddress.List
Dim output As String = "Customer: <br>" & _
"First Name: " & c.FirstName & "<br>" & _
"Last Name: " & c.LastName & "<br>" & _
"Email: " & c.Email & "<br>" & _
"Status: " & c.Status & "<br>" & _
"<br>" & _
"Addresses:<br>"
For Each a As Address In c.Addresses
output &= a.AddressLine & ", " & a.City & ", " & a.State & " " & a.ZipCode & "<br>"
Next
lblOutput.Text = output
Points of Interest
The main point of interest is of course the ObjectBinder
class. The code is attached in the download. it's actually very simple, especially considering the huge task it accomplishes!
What this class does is that it creates an ObjectDataSource
and a method for List
, Update
, Insert
and Delete
. It uses .NET Generics to allow pretty much any class to work with this simple utility, instead of having to create a separate adapter class, and plug it manually into an ObjectDataSource
in the markup.
This gives you a very nice and easy way to implement MVVM architectural pattern in ASP.NET WebForms.
Cleanup
The ObjectBinder
uses the session to store information and if not properly cleared up, it can cause issues in the long run as it would and leave unnecessary items in the Session
.
To clean up and remove the items from the session
, the "Clear
" method should be called in the Page
's unload method. The alternate is to include the class file in your web app project, and change the code to use the ViewState
instead of the session
. I am working on implementing IDisposable
for ObjectBinder
so it would automatically do the cleanup, but till then, a manual call to "Clear
" is needed.
Enjoy!
History
- 25th April, 2012: Initial version