Tuesday, September 27, 2005

Data Grid Max Length And Formatting

The format issue I had the other day was in trying to format a value for a datagrid. It turns out I had issues beyond the format of the data itself. I had started with:

public Form1()
{
// Required for Windows Form Designer support
InitializeComponent();

// Create data table
DataTable tbl = new DataTable("Indemnity");
tbl.Columns.Add("indemnityCode", typeof(System.String));
tbl.Columns.Add("indemnityType", typeof(System.String));
tbl.Columns.Add("indemnityAmount", typeof(System.Double));

// populate with test data
AddTestRow(tbl, "AA", "Alpha", 100.00d);
AddTestRow(tbl, "BB", "Beta", 200.00d);
AddTestRow(tbl, "CC", "Gamma", 300.00d);

// Data bind
DataView dv = new DataView(tbl);
dv.AllowDelete = false;
dv.AllowNew = false;
dataGrid1.DataSource = dv;

// Set Column Information.
DataGridTableStyle tblStyle = new DataGridTableStyle();
tblStyle.AllowSorting = false;
tblStyle.MappingName = "Indemnity";
tblStyle.RowHeadersVisible = false;
tblStyle.GridLineStyle = DataGridLineStyle.Solid;
dataGrid1.FlatMode = false;
tblStyle.HeaderFont = new System.Drawing.Font(dataGrid1.Font, FontStyle.Bold);
dataGrid1.TableStyles.Add(tblStyle);

tblStyle.GridColumnStyles["indemnityType"].ReadOnly = true;
tblStyle.GridColumnStyles["indemnityType"].HeaderText = "Indemnity Type";
tblStyle.GridColumnStyles["indemnityType"].Width = 150;
tblStyle.GridColumnStyles["indemnityAmount"].Width = 110;
tblStyle.GridColumnStyles["indemnityAmount"].Alignment = HorizontalAlignment.Center;
tblStyle.GridColumnStyles["indemnityAmount"].ReadOnly = false;
tblStyle.GridColumnStyles["indemnityAmount"].HeaderText = "Amount";
DataGridTextBoxColumn dgtbc=null;
dgtbc = (DataGridTextBoxColumn)dataGrid1.TableStyles[0].GridColumnStyles["indemnityAmount"];
dgtbc.Format = "N2";
tblStyle.GridColumnStyles.Remove(tblStyle.GridColumnStyles["IndemnityCode"]);
}

private void AddTestRow(DataTable tbl, string code, string type, double amount)
{
DataRow row = tbl.NewRow();
row["indemnityCode"] = code;
row["indemnityType"] = type;
row["indemnityAmount"] = amount;
tbl.Rows.Add(row);
}


This creates a datagrid with some test data and formats the number column to be ##,###.00. This is where I needed to use the extra formatting to prevent rounding of the cents column, I just wanted to truncate anything beyond cents and assume that it was accidentally typed. So I needed to add an event to catch when the row changed. But since I was reformatting the data I also had to prevent a stack overflow from an infinite loop.
public Form1()
{
// all code previously &
tbl.RowChanged += new DataRowChangeEventHandler(IndemnityRowChanged);
}

private bool AllowIndemnityRowChangedEvent = true;
private void IndemnityRowChanged(object sender, DataRowChangeEventArgs e)
{
if( !AllowIndemnityRowChangedEvent )
return;

//update the payment activity DataRow.
DataRow indemnityTypeRow = e.Row;

string code = Convert.ToString(indemnityTypeRow["IndemnityCode"]);

double testValue = Convert.ToDouble(indemnityTypeRow["IndemnityAmount"]);
double cutValue = TrimDoubleToXDigits(testValue, 2);

AllowIndemnityRowChangedEvent = false;
indemnityTypeRow["IndemnityAmount"] = Convert.ToDouble(cutValue);
AllowIndemnityRowChangedEvent = true;
}

I didn't repost the TrimDoubleToXDigits function. See my previous post for it.

The end result is now I have a grid that will format the numbers the way that I want. But I also need to have a maximum amount. I'm not using a SQL backend, I'm using a iSeries to hold the data, and in this particular field my maximum value is 99,999,999.99

To do this I have a few more lines to add
public Form1()
{
// all code prior to the following line
dgtbc.Format = "N2";
dgtbc.TextBox.MaxLength = 11;
// all code after the previous line
}


private void IndemnityRowChanged(object sender, DataRowChangeEventArgs e)
{
// code up to the following line
AllowIndemnityRowChangedEvent = false;
// enforce the max value.
if( cutValue > 99999999.99 )
{
cutValue = 99999999.99 ;
}
indemnityTypeRow["IndemnityAmount"] = cutValue;
AllowIndemnityRowChangedEvent = true;
}


The end result is that now values greater than my max value will get set to the max. I can raise an event or show a message to the user at this point to make them aware of the limitation. I did try to use the events that I could grab from the underlying textbox, but apparently the rowchanged event happens after the text change event and I couldn't enforce the max value in the text changed event because other validation happens behind the scenes somewhere. For example. The user can type ",,," but the data will be changed back to the original underlying data. The user can also type "123,123.998" The value displayed after this will be "123,123.99" I needed all of this behind the scenes validation to be in force before comparing to the max value.