Persisted Data in C#






2.77/5 (24 votes)
Jan 25, 2005
4 min read

68535

894
This article builds a persisted data application in C#.
Introduction
I have built a Persisted data program for the Amateur Radio community. The reason I chose Amateur Radio is because it is something I live with on a day to day basis. I can therefore use the program everyday, and edit the code as and when necessary. This particular program is directed towards Amateur Radio contesting. This is an event in which each participating station tries to make as many radio contacts as possible, within a limited amount of time. The rules are complicated and are beyond the scope of this article. This therefore is a "bare bones program", and is not necessarily a finished product. The data is formatted in "Cabrillo format" which is specified by the Amateur Radio Relay League. There are several Cabrillo format styles, so this program could have reusable classes and methods to support several Windows Forms. This article merely serves to demonstrate Persisted data.
Persisted data
Persisted data is the backbone of the computer industry, and is used in every aspect of the computing world. Scientific communities, banking, government, and commerce all use Persisted data. This is what made IBM what it is today with its mainframe computing. .NET makes it possible to adapt mainframe technology to the modern PC.
Data is written to an external file, in this case, with a .dab suffix. All files written by this program will use the user created suffix. This program will open no other files, and all files created by this program will be proprietary. I have included a test file Test1.dab for your convenience. There is no internal data in the program, so upon starting the program you must either create your own file, or open the test file provided. When you create a file you will be prompted to restart the program.
When .NET was introduced in 2002, a new era of Persisted data began for the .NET community. Prior versions of this platform, such as VB-6 used the msFileCreate
method, and created a text file with "end of line" characters as the only division between records. Version 7.0 of .NET introduced "field" structure, so that not only could data be retrieved by a record index number, fields could also be identified by name. Therefore, arrays could have a horizontal structure, in that they could contain a record, or a vertical structure in that every field could be written to a different array. This is useful for full text searches where records have met the search criteria. An intCounter
variable can be incremented, and can tell us how many records that meet the search requirements have been returned.
This program presents "double click editing." Once a file has been opened, double click on a record, and it will split the fields into the appropriate textboxes for editing. The user can then make necessary changes, and press "Enter" to write them to file. There is no "delete record" function in this program, as this would destroy the sequential record index numbering, and would therefore corrupt the file. Entering a record is easy, and after entering the first record, useful fields are given default values, and subsequent records are easier, and faster to enter using the "Enter" key. In .NET, events are owned by the control, so in this particular program, records can only be entered from the last "Comment" textbox, when that control receives "focus." By using reusable subroutines, this capability can be expanded. There is no point in this article to include a lot of repetitious events. Enter a Callsign that you see on the screen in Received Callsign, and a full text Callsign search is made, returning the matching records in the lower textbox. At least one record must have first been written to the file before the Callsign search is unlocked. Boolean variables make excellent conditionals for locking, or unlocking program features. I have utilized reusable subroutines to allow the editing of any field in the record from any textbox.
Sample code
private void miOpen_Click(object sender, System.EventArgs e)
{
AddRecOpen = true;
// Disable menu items
miOpen.Enabled = false;
miNew.Enabled = false;
// Start backgound thread for ProgressBar
if( timedProgress.ThreadState.ToString() == "Unstarted")
{
timedProgress.Start();
}
// Configure progbar
progbar.Minimum = 1;
progbar.Maximum = 1000;
progbar.Step = 1;
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//Open fileInput, Iterate thru file, print to txtBox, set txtNumber to
//last record number'
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// Set Filters
fileChooser.Filter = "Davco File (*.dab)|*.dab";
fileChooser.FilterIndex = 1;
// create dialog box enabling user to open file
DialogResult result = fileChooser.ShowDialog();
// get file name from user
fileName = fileChooser.FileName;
// exit eventhandler if user clicked Cancel
if ( result == DialogResult.Cancel )
return;
// show error if user specified invalid file
if ( fileName == "" || fileName == null )
MessageBox.Show( "Invalid File Name", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error );
else
{
fileInput = new FileStream( fileName,
FileMode.Open, FileAccess.Read );
// use FileStream for BinaryWriter to read bytes from file
binaryInput = new BinaryReader( fileInput );
// Calls and writes information function to txtRoot.Text
txtRoot.Text = information();
currentRecordIndex = 0;
}
for(int i = 0; i < 1000; i++)
{
Record record = new Record();
// read record and store data in TextBoxes
try
{
// get next record available in file
while( record.Number == 0 )
{
//Reset progressBar to start if required
if (progbar.Value == progbar.Maximum)
{
progbar.Value = progbar.Minimum ;
}
else
{
progbar.PerformStep();
}
// set file position pointer to next record in file
fileInput.Seek(
currentRecordIndex * Record.SIZE, 0 );
currentRecordIndex += 1;
// read data from record
record.Number = binaryInput.ReadInt32();
record.Frequency= binaryInput.ReadString();
record.Mode = binaryInput.ReadString();
record.Date = binaryInput.ReadString();
record.Time = binaryInput.ReadString();
record.SntCallsign = binaryInput.ReadString();
record.SntRst= binaryInput.ReadString();
record.SntXchg = binaryInput.ReadString();
record.RecCallsign = binaryInput.ReadString();
record.RecRst = binaryInput.ReadString();
record.RecXchg = binaryInput.ReadString();
record.Comment = binaryInput.ReadString();
}
// store record values in temporary string array
string[] values = {
record.Number.ToString(),
record.Frequency,
record.Mode,
record.Date,
record.Time,
record.SntCallsign,
record.SntRst,
record.SntXchg,
record.RecCallsign,
record.RecRst,
record.RecXchg,
record.Comment
};
// copy string array values to txtView values
SetTextBoxValues( values );
stringValues = "QSO: {1, 5} {2, -3}{3, -11}{4, -5}" +
"{5, -14}{6, -4}{7, -7}{8, -14}{9, -4}{10, -7}{11, -1} \r\n";
Sb.AppendFormat( stringValues, values );
lstRecord.Items.Add(Sb.ToString());
txtView.AppendText(Sb.ToString());
// Erase stringBuilder
Sb.Remove(0, 86);
// if work offline is set then set focus to txtTime.Text
if(timer1.Enabled == false)
{
txtFrequency.TabStop = false;
txtMode.TabStop = false;
txtDate.TabStop = false;
txtTime.TabStop = false;
txtSntCallsign.TabStop = false;
txtSntRst.TabStop = false;
txtRecRst.TabStop = false;
// Shift focus to txtFrequency
txtFrequency.Focus();
}
else
{
// if working online set focus to cmboCallsign.Text
txtRecCallsign.Focus();
}
}
// handle exception when no records in file
catch( IOException )
{
ClearTextBoxes();
// break out of loop; it is within sight so don't sweat it
goto Breakout;
}
finally
{
// increment last record number by 1
count += 1;
txtNumber.Text = count.ToString();
}
}
Breakout:
// close streams if no records in file
fileInput.Close();
binaryInput.Close();
txtRecCallsign.Focus();
timer1.Enabled = true;
// show error if user specified invalid file
if ( fileName == "" || fileName == null )
MessageBox.Show("Invalid File Name", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
else
{
// open file if file already exists
try
{
//create FileStream (ReadWrite) to hold records
fileOutput = new FileStream( fileName,
FileMode.OpenOrCreate, FileAccess.ReadWrite );
// create object for writing bytes to file
binaryOutput = new BinaryWriter( fileOutput );
binaryInput = new BinaryReader( fileOutput );
timer1.Enabled = true;
runOnce += 1;
// Updates information to txtRoot.Text
txtRoot.Text = information();
txtRecCallsign.Text = "";
txtRecXchg.Text = "";
txtComment.Text = "";
}
finally
{
// snap lstView to last record
if( Int32.Parse(txtNumber.Text) > 16 )
this .lstRecord.SetSelected((count - 2), true);
// Initializes txtBoxes
// Set TabStops
Reset();
progbar.Visible = true;
// Stop Progress Bar Thread
timedProgress.Abort();
}
}
}
Summary
Build your own program with the functions that you would like to use. I use many text searches in mine to give me information about radio propagation. I have also incorporated charts in my program at home here to show useful information such as "Number of contacts per month" in any given year, or "Number of contacts on each Band" in any given year. You might include a chart that shows "Number of contacts per hour." There are many more Cabrillo formats available for other contests, or we might decide that we like our own formatting better.
You have the ability to build a project that suits your own taste.