all groups > dotnet windows forms databinding > march 2008 >
You're in the

dotnet windows forms databinding

group:

Databinding format in Business Object Layer


Databinding format in Business Object Layer Jerod Houghtelling
3/4/2008 7:05:49 AM
dotnet windows forms databinding:
Hi all,

I would like to place a textbox on a form and have it bound to a
custom business object. What would be the recommended practice on
formatting and validating? My current implementation shown below works
for formatting the value in the business object, but the formatting is
not shown in the text box.

public class OrderHeader
{
private string mOrderNumber;
...

public string OrderNumber
{
get { return mOrderNumber; }
set { mOrderNumber = value.Trim().ToUpper().PadLeft( 8,
'0' ); }
}
}

txtOrderNumber.Databinding.Add( "Text", mOrderHeader, "OrderNumber",
true );

I've tried doing a INotifyPropertyChanged implementation, but the
textbox doesn't seem to be receiveing the PropertyChanged event, I'm
guessing because it knows that the property just changed so it is just
ingnoring it. I figure that I'll have to use some of the textbox
events but any help would be appriciated.

Thanks,
Jerod
Re: Databinding format in Business Object Layer Jerod Houghtelling
3/4/2008 8:35:15 AM
[quoted text, click to view]

Marc,

Thanks for your quick response. After testing your version everything
did work. I noticed that the difference was that I was setting the
fourth parameter on the textbox1.DataBindings.Add to true. When I
removed that parameter I was hitting an FormatException which was
thrown durring the load because my order number was null.

Any suggestions on where I should put the validating? I would like to
keep it in the business object so code doesn't have to be duplicated
in multiple forms.

Thanks again,
Re: Databinding format in Business Object Layer Marc Gravell
3/4/2008 12:26:51 PM
[quoted text, click to view]
Absolutely; this is good practice - it separates the model from the
UI.

If you want the validation to *prevent* invalid entry, then put it in
the setter and raise an exception. If you want it to /notify/ the user
about data errors without interrupting the flow, then implement
IDataErrorInfo. Some UI controls (most notably DataGridView) support
this automatically, but for others you might need to cobble some
things together, perhaps involving an ErrorProvider. For a hasty, not
very pretty example - see below.

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
public class OrderHeader : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
private string mOrderNumber;
private void UpdateField<T>(ref T field, T value, string
propertyName)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
}
}
}
public string OrderNumber
{
get { return mOrderNumber; }
set
{
string fmt = value.Trim().ToUpper().PadLeft(8, '0');
UpdateField(ref mOrderNumber, fmt, "OrderNumber");
}
}




#region IDataErrorInfo Members

static string GetAllErrors(IDataErrorInfo component)
{
StringBuilder sb = new StringBuilder();
foreach (PropertyDescriptor property in
TypeDescriptor.GetProperties(component))
{
string propErr = component[property.Name];
if (!string.IsNullOrEmpty(propErr))
{
sb.AppendLine(propErr);
}
}
return sb.ToString();
}

string IDataErrorInfo.this[string columnName]
{
get {
switch (columnName)
{
case "OrderNumber":
int len = mOrderNumber == null ? 0 :
mOrderNumber.Length;
if (len < 2 || len > 8){
return "Order number must be between 2 and 8
characters";
}
break;
}
return "";
}
}
string IDataErrorInfo.Error
{
get { return GetAllErrors(this); }
}

#endregion
}


static class Program
{
static void Main()
{
Application.EnableVisualStyles();
OrderHeader order = new OrderHeader();
order.OrderNumber = "0001";
BindingList<OrderHeader> orders = new
BindingList<OrderHeader>();
orders.Add(order);
orders.AllowNew = orders.AllowRemove = false;
using (Form f = new Form())
using (TextBox tb1 = new TextBox())
using (TextBox tb2 = new TextBox())
using (DataGridView dgv = new DataGridView())
using (ErrorProvider err = new ErrorProvider())
{
err.Icon = SystemIcons.Error;
tb1.Top = 20;
tb2.Top = 60;
tb1.DataBindings.Add("Text", orders, "OrderNumber");
tb2.DataBindings.Add("Text", orders,
"OrderNumber");
order.PropertyChanged += delegate
{
err.SetError(tb2, ((IDataErrorInfo)order)
["OrderNumber"]);
};
dgv.DataSource = orders;
dgv.Dock = DockStyle.Bottom;
f.Controls.Add(tb1);
f.Controls.Add(tb2);
f.Controls.Add(dgv);
Application.Run(f);
}
}
Re: Databinding format in Business Object Layer Marc Gravell
3/4/2008 3:39:01 PM
Works fine for me... (with INotifyPropertyChanged implementation)

??

Marc

public class OrderHeader : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string mOrderNumber;
private void UpdateField<T>(ref T field, T value, string propertyName)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
}
}
}
public string OrderNumber
{
get { return mOrderNumber; }
set
{
string fmt = value.Trim().ToUpper().PadLeft(8, '0');
UpdateField(ref mOrderNumber, fmt, "OrderNumber");
}
}
}

static class Program
{
static void Main()
{
OrderHeader order = new OrderHeader();
Application.EnableVisualStyles();
using(Form f = new Form())
using (TextBox tb1 = new TextBox())
using (TextBox tb2 = new TextBox())
{
tb1.Dock = tb2.Dock = DockStyle.Top;
tb1.DataBindings.Add("Text", order, "OrderNumber");
tb2.DataBindings.Add("Text", order, "OrderNumber");
f.Controls.Add(tb1);
f.Controls.Add(tb2);
Application.Run(f);
}
}
}

AddThis Social Bookmark Button