Friday, September 30, 2005

Dashboard design thoughts

I've been thinking about the dashboard some more. I can see it being useful if it can accurately display the status of an application. The business rules of how that status is figured out can be very complicated, so the design I've come up with is a main application with plugin .dlls that implement an interface. The plugin .dlls each monitor their own piece of the pie on a timer that is specific for each one.

Then a main applicaiton uses a listview to display a color, status, description, and other information about the application.

The main application would have to be able to minimize to the tray. If any one monitoring applicatin had trouble the application would pop up on top. Double clicking on the item that was in trouble would open up a form (entirely supported by the child .dll) that would give any additional information that it had about the problem.

I like this method because it is easily extensible. The main app just monitors status and passes off all additional responsibility to the plugin .dlls. Each plugin .dll could also have a configuration form or panel that allows the user to change settings of the timer or connection string or whatever else they need to change related to how the plugin was programmed.

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.

Monday, September 26, 2005

C# formatting double to specific number of digits

Wow! I'm shocked I even had to work on this one. I had a simple problem of having to take a value from a textbox and make sure that it was formatted with only 2 decimals. The textbox doesn't prevent users from typing more than 2 digits to right of the decimal, but it needs to strip any more than 2 off because the users know that this field is used for currency and any additional is assumed to be a mistake. For example, if the number 1234.556 is typed I need to format it to be 1,234.55

I thought that this would be a no-brainer, I'll just format Number with 2 decimal places. BUZZZ! Wrong answer!. First I tried the expected formats.

double test = 1234.556;
Console.WriteLine(test.ToString("N2")); // 1,234.56
Console.WriteLine(test.ToString("F2")); // 1234.56
Console.WriteLine(test.ToString("C2")); // $1,234.56

All 3 of these rounded up rather than dropping the extra I figured I'd use my old VB6 trick and just convert it into an integer then back, so I tried:
double test = 1234.556;
int testval = Convert.ToInt32(test * 100);
Console.WriteLine(testval.ToString()); //123456

This tells me that behind the scenes this number is being rounded up before it's being converted to an integer. I just want the actual value of whole number portion here. Grr!

After lots of searching online and wrenching my brain for nearly 2 days, I decide to go back to my roots. In C if I had to do this I'd just make it a string, then find the decimal and replace it with a null to cut the string there, then I would convert the rest back. I think I can imitate that in the more advanced C#. So I wrote my own function.

private double TrimDoubleToXDigits(double initialValue, int numDigits)
{
if( numDigits < 0 )
{
throw new Exception("should not be negative");
}

double divisor = Math.Pow(10, numDigits);
// multiply by 100 to get all keeper values left of the decimal.
double result = initialValue * divisor;
string testValNoDec = result.ToString();
// truncate everything to the right of the decimal.
if( testValNoDec.IndexOf(".") != -1 )
{
// remove the decimal and everything else.
testValNoDec = testValNoDec.Substring(0, testValNoDec.IndexOf("."));
}

result = Convert.ToDouble(testValNoDec) / divisor;
return result;
}


It's easy to use, you just call it as shown here.
double trimmedValue = TrimDoubleToXDigits( 1234.556, 2 );
Console.WriteLine("Resulting value : " + trimmedValue.ToString("N2") );
// 1,234.55

trimmedValue = TrimDoubleToXDigits( 1234.556789, 3 );
Console.WriteLine("Resulting value : " + trimmedValue.ToString("N3") );
// 1,234.556

I know I could have had a more robust function with better error checking, but I have enough control over the environment and use of this funciton that it serves it's purpose. Any more time at this point would only be putting me further behind schedule.

Thursday, September 22, 2005

New way to do trig

I also plan on listing cool links that I find over time. Here's a newly released book with a complete overhaul of how to do trig. This supposedly makes it a lot easier to figure out answers to things. If this is correct, I hope it catches on big time and schools replace their curriculum with the new methods presented here. The only downside is that it's $80 and you have to wait a month to get a copy. Ah well, I'm sure it will be around for a while.

http://web.maths.unsw.edu.au/~norman/book.htm

Tools to look at and talk about.

Tools to look at and talk about.

  • nUnit - look at CodeProject for articles.
  • nAnt

Things to investigate

  • Dependency Injection
  • Agile Development
  • Test Driven Development

Languages to continue to learn and discuss

  • ASP.net
  • SQL Server
  • C#
  • C# 2.0
  • Microsoft Workflow.

Log4Net

  • Can Log4Net write to more than one log at a time?
  • Can it be configured to write to both a DATA log and a General log?
  • How does the configuration for this work?
  • How is Log4Net different from built in Tracing.

I read a lot about companies with their dashboard applications, and how they use them to get real time information about the state of things.

I wonder what it would take for me to build my own dashboard application to monitor the applications that I am constantly checking on.

What kind of tools are available for this?