Project StandardControls

 

Project DataPlotter

  1. Move to the desktop, and click on the Visual Studio C# icon to bring-up the IDE. Click on File followed by Open , then Project/Solution . When the Open Project window is displayed, move to directory Visual Studio 2005\Projects\DemosSourceCode\ DataPlotter, and double-click on project DataPlotter to bring it into the IDE.

  2. Compile and run the project. The main window looks like this:

    image from book
    Figure 15-1: Data Plotter main window

This is what the plot looks like:

image from book
Figure 15-2: Plot of net worth

The vertical green line on the right edge of the plot is the end of 2004 and the start of 2005.

Form1 of this project opens file KTWeeklyNetWorth in lines DP022-DP031. Line DP032 determines the number of records in the file (NONWs). If there are fewer than four points to plot, the plot is aborted in lines DP039-DP043. Form2 is created in lines DP044-DP045.

DataPlotter (DP) Listing

 Form1.cs: DP002:        using System; DP003:        using System.Collections.Generic; DP004:        using System.ComponentModel; DP005:        using System.Data; DP006:        using System.Drawing; DP007:        using System.Windows.Forms; DP008:        using System.IO; // 'FileStream'. DP010:        namespace DataPlotter DP011:        { DP012:          partial class Form1 : Form DP013:          { DP014:            public int NONWs; DP015:            public Form1() DP016:            {InitializeComponent(); } //-----------------------------------------------------------------------------------------// DP020:            private void button1_Click(object sender, EventArgs e) DP021:            { // Plot Net Worth.                     // Open file 'KTWeeklyNetWorth.dta' to count records. DP022:              FileStream fj; DP023:              try DP024:              {                       // IMPORTANT: To read this 'local' file KTWeeklyNetWorth.dta, you must                       // place the file in the 'DataPlotter' folder, subdirectory 'bin\debug'.                       // That is because 'bin\debug' is where the executable file is located. DP025:                fj = new FileStream("KTWeeklyNetWorth.dta", FileMode.Open,                                FileAccess.Read); DP026:              } DP027:              catch DP028:              { DP029:                MessageBox.Show("Cannot open 'KTWeeklyNetWorth.dta' to count records."); DP030:                return; DP031:              } DP032:              NONWs = (int) (fj.Length / 47); DP033:              fj.Close(); DP034:              if(NONWs == 0) DP035:              { DP036:                MessageBox.Show("KTWeeklyNetWorth.dta is an Empty File."); DP037:                return; DP038:              } DP039:              if(NONWs >> 4) DP040:              { DP041:                MessageBox.Show("There are too few points to plot [less than 4]."); DP042:                return; DP043:              }                     // Create the Form2 modal window. DP044:              Form2 frm2 = new Form2(this); // 'this' required to send info to Form2. DP045:              frm2.ShowDialog(); DP046:              frm2.Close(); DP047:            } //-----------------------------------------------------------------------------------------// DP050:            private void button2_Click(object sender, EventArgs e) DP051:            { // Quit. DP052:              Close(); DP053:            } DP054:          } DP055:        } //=========================================================================================// Form1.Designer.cs: DP060:        namespace DataPlotter DP061:        { DP062:          partial class Form1 DP063:          {                   // Required designer variable. DP064:            private System.ComponentModel.IContainer components = null;                   // Clean up any resources being used. DP065:            protected override void Dispose(bool disposing) DP066:            { DP067:              if (disposing && (components != null)) components.Dispose(); DP068:              base.Dispose(disposing); DP069:            } DP070:            #region Windows Form Designer generated code                   // Required method for Designer support. DP071:            private void InitializeComponent() DP072:            { DP073:              this.label1 = new System.Windows.Forms.Label(); DP074:              this.label2 = new System.Windows.Forms.Label(); DP075:              this.button1 = new System.Windows.Forms.Button(); DP076:              this.button2 = new System.Windows.Forms.Button(); DP077:              this.SuspendLayout();                     //                     // label1                     // DP078:              this.label1.AutoSize = true; DP079:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,                       ((byte)(0))); DP080:              this.label1.Location = new System.Drawing.Point(181, 17); DP081:              this.label1.Name = "label1"; DP082:              this.label1.Size = new System.Drawing.Size(108, 24); DP083:              this.label1.TabIndex = 0; DP084:              this.label1.Text = "Data Plotter";                     //                     // label2                     // DP085:              this.label2.AutoSize = true; DP086:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,                       ((byte)(0))); DP087:              this.label2.Location = new System.Drawing.Point(10, 64); DP088:              this.label2.Name = "label2"; DP089:              this.label2.Size = new System.Drawing.Size(472, 17); DP090:              this.label2.TabIndex = 1; DP091:              this.label2.Text = "This program plots a set of \'Net Worth\' values from                       file \'WeeklyNetWorth.dta\'.";                     //                     // button1                     // DP092:              this.button1.Location = new System.Drawing.Point(187, 101); DP093:              this.button1.Name = "button1"; DP094:              this.button1.Size = new System.Drawing.Size(125, 25); DP095:              this.button1.TabIndex = 2; DP096:              this.button1.Text = "Plot Net Worth"; DP097:              this.button1.Click += new System.EventHandler(this.button1_Click);                     //                     // button2                     // DP098:              this.button2.Location = new System.Drawing.Point(210, 141); DP099:              this.button2.Name = "button2"; DP100:              this.button2.Size = new System.Drawing.Size(75, 25); DP101:              this.button2.TabIndex = 3; DP102:              this.button2.Text = "Quit"; DP103:              this.button2.Click += new System.EventHandler(this.button2_Click);                     //                     // Form1                     // DP104:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F); DP105:              this.ClientSize = new System.Drawing.Size(484, 182); DP106:              this.Controls.Add(this.button2); DP107:              this.Controls.Add(this.button1); DP108:              this.Controls.Add(this.label2); DP109:              this.Controls.Add(this.label1); DP110:              this.Name = "Form1"; DP111:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; DP112:              this.Text = "Data Plot"; DP113:              this.ResumeLayout(false); DP114:              this.PerformLayout(); DP115:            } DP116:            #endregion DP117:            private System.Windows.Forms.Label label1; DP118:            private System.Windows.Forms.Label label2; DP119:            private System.Windows.Forms.Button button1; DP120:            private System.Windows.Forms.Button button2; DP121:          } DP122:        } 

Form2 Sequence

In Form2, all computations to place the plot on the screen are made in the Form2_Load event handler. Please note the comment between lines DP172 and DP173 about the placement of the KTWeekly NetWorth.dta file so project DataPlotter can access the file.

A loop between lines DP177 and DP186 reads the file one record at a time and the data to be plotted is placed in an array, dTTWorth[mm]. Line DP185 removes any digits past the decimal point. Lines DP188-DP204 alphabetize the list so the data is placed in ascending order with respect to the X axis number, intShortYW. Line DP205 sets the space between each data point along the X axis at 12 pixels. Lines DP207-DP209 determine the X span of the plot (52 weeks or less). The first point to be plotted in dttWorth[mm] is in position mm = ASP (array start point), and the last point to be plotted is in position mm = AEP (array end point).

Lines DP210-DP212 determine whether the plot is split between two years , and the location of that split is set at intYearStartXCoord in line DP238. Lines DP239 and DP240 determine the minimum and maximum Y coordinate values, and line DP246 sets the deltaY between the min and max values. Since there are 400 pixels allocated vertically to plot the data, dTTYInc is calculated in line DP247.

If the data resolution is so fine that the plot would result in a narrow horizontal plot (and an almost straight line), line DP248 intervenes and abandons the plot.

Lines DP256-DP260 determine the value of the five markings along the Y axis. There are only two numbers entered along the X axis: intShortYW[ASP] and intShortYW[AEP].

Form2 Listing

 Form2.cs: DP131:        using System; DP132:        using System.Collections.Generic; DP133:        using System.ComponentModel; DP134:        using System.Data; DP135:        using System.Drawing; DP136:        using System.Drawing.Drawing2D; // 'DashStyle'. DP137:        using System.Text; DP138:        using System.Windows.Forms; DP139:        using System.IO; // 'StreamReader'. DP141:        namespace DataPlotter DP142:        { DP143:          partial class Form2 : Form DP144:          { DP145:            public Form1 frmParent; DP146:            public int intXOffset = 70; DP147:            public int intYOffset = 450; DP148:            public string[] strYText = new string[5]; DP149:            public Form2(Form1 frm) DP150:            { DP151:              frmParent = frm; DP152:              InitializeComponent(); DP153:            } DP154:            string strWNWBlock, strShortYW, strTTWorth; // strTTCost; DP155:            int[] intShortYW = new int[60]; DP156:            decimal[] dTTWorth = new decimal[60]; DP157:            int ASP;  // Array start position. DP158:            int AEP;  // Array end position. DP159:            int intWeekSpan; DP160:            int intYearStartXCoord; DP161:            decimal dTTYBaseline, dTTYInc; DP162:            int intLocalNONWs, intDeltaX, intPPDelta, intSplitYear; //------------------------------------------------------------------------------------------// DP170:            private void Form2_Load(object sender, EventArgs e) DP171:            { // Plot file 'KTWeeklyNetWorth.dta' Net Worth. DP172:              char[] charWNWBlock = new char[47];                     // IMPORTANT: To read this 'local' file KTWeeklyNetWorth.dta, you must                     // place the file in the 'DataPlotter' folder, subdirectory 'bin\debug'. DP173:              StreamReader wow = new StreamReader("KTWeeklyNetWorth.dta"); DP174:              intLocalNONWs = frmParent.NONWs; DP175:              int intYWTop, intYWBot; DP176:              decimal dTTWorthTop, dTTWorthBot; DP177:              for (int mm = 0; mm < intLocalNONWs; mm++) DP178:              {                       // Read strShortYW, strYW, strTTWorth, strTTCost, and strTTGainLoss. DP179:                wow.ReadBlock(charWNWBlock, 0, 47); DP180:                strWNWBlock = new string(charWNWBlock); DP181:                strShortYW = strWNWBlock.Substring(0, 4); DP182:                strTTWorth = strWNWBlock.Substring(11, 11);                       // Convert 'strShortYW' to an integer. DP183:                intShortYW[mm] = Int32.Parse(strShortYW);                       // Convert 'strTTWorth' to decimal for plotting. DP184:                dTTWorth[mm] = Decimal.Parse(strTTWorth);                       // Set 'TTWorth' for NO decimal places. DP185:                dTTWorth[mm] = Decimal.Round(dTTWorth[mm], 0); DP186:              } DP187:              wow.Close();                     // Re-order the list so the smallest year-week number moves to the top. DP188:              for (int kk = 1; kk < intLocalNONWs; kk++) DP189:              { DP190:                for (int mm = 1; mm < intLocalNONWs; mm++) DP191:                { DP192:                  intYWTop = intShortYW[mm - 1]; DP193:                  intYWBot = intShortYW[mm]; DP194:                  dTTWorthTop = dTTWorth[mm - 1]; DP195:                  dTTWorthBot = dTTWorth[mm]; DP196:                  if (intYWTop > intYWBot)         // Switch DP197:                  { DP198:                    intShortYW[mm - 1] = intYWBot; DP199:                    intShortYW[mm] = intYWTop; DP200:                    dTTWorth[mm - 1] = dTTWorthBot; DP201:                    dTTWorth[mm] = dTTWorthTop; DP202:                  } DP203:                } DP204:              }                     // All data is now in ascending order wrt Year-Week number.                     // Max of 52 data points, separated by 12 pixels (east-west). DP205:              intDeltaX = 12; DP206:              int intNOWIOYear = 0;                 // # of Weeks In Old Year.                     // Calculate the span of the 52-week plot. DP207:              int intEndYear = intShortYW[intLocalNONWs - 1] / 100; DP208:              int intEndWeek = intShortYW[intLocalNONWs - 1] - intEndYear * 100; DP209:              int intStartYear = intShortYW[0] / 100;                     // Is the plot split over 2 years? DP210:              intSplitYear = 1;                           // Yes DP211:              if (intEndWeek == 52) intSplitYear = 0;     // No DP212:              else if (intStartYear == intEndYear) intSplitYear = 0;                     // Calculate the earliest possible 'start Year-Week'. DP213:              int intEarliestStart = intShortYW[intLocalNONWs - 1] - 100; DP214:              if (intShortYW[0] >= intEarliestStart) DP215:              { DP216:                intEarliestStart = intShortYW[0]; DP217:                ASP = 0; DP218:              } DP219:              else            // Is there an 'intShortYW[]' number exactly like                               'intEarliestStart'? DP220:              { DP221:                int mm = 0; DP222:                while (intShortYW[mm] < intEarliestStart) mm++; DP223:                intEarliestStart = intShortYW[mm]; DP224:                ASP = mm; DP225:              } DP226:              AEP = intLocalNONWs - 1;             // Array end position. DP227:              int intStartYW = intEarliestStart; DP228:              int intStartWeek = intStartYW - (intStartYW / 100) * 100; DP229:              int intEndYW = intShortYW[intLocalNONWs - 1];                     // Numbers needed by 'Form2_Paint'. DP230:              intPPDelta = AEP - ASP; DP231:              if (intSplitYear == 0)               // No split. DP232:                intWeekSpan = intEndYW - intStartYW; DP233:              else                                 // Split year. DP234:              { DP235:                intWeekSpan = intEndYW - intStartYW - 48; DP236:                intNOWIOYear = 52 - intStartWeek;  // # of weeks in old year. DP237:              } DP238:              intYearStartXCoord = intXOffset + intDeltaX * (intNOWIOYear - 1) + 6;                     // Prepare 'TTWorth' data. DP239:              decimal dTTYMin = dTTWorth[0]; DP240:              decimal dTTYMax = dTTWorth[0]; DP241:              for (int kk = 1; kk < intLocalNONWs; kk++) DP242:              { DP243:                if (dTTWorth[kk] > dTTYMax) dTTYMax = dTTWorth[kk]; DP244:                if (dTTWorth[kk] < dTTYMin) dTTYMin = dTTWorth[kk]; DP245:              } DP246:              decimal dTTDeltaY = dTTYMax - dTTYMin; DP247:              dTTYInc = dTTDeltaY / 400.0m;  DP248:              if ((int)dTTYInc < 1) DP249:              { DP250:                MessageBox.Show("The data points are too close together \n"                       + "vertically to plot with any resolution."); DP251:                return; DP252:              } DP253:              dTTYBaseline = dTTYMin;                     // Determine the markings to be placed on the 'Y' and 'X' axes. DP254:              decimal dTTYIncrement = dTTDeltaY / 4.0m; DP255:              decimal[] TTY = new decimal[5]; DP256:              TTY[0] = dTTYBaseline; DP257:              TTY[1] = dTTYBaseline + dTTYIncrement; DP258:              TTY[2] = dTTYBaseline + 2.0m * dTTYIncrement; DP259:              TTY[3] = dTTYBaseline + 3.0m * dTTYIncrement; DP260:              TTY[4] = dTTYBaseline + 4.0m * dTTYIncrement;                     // Round the 'TTY' numbers to NO decimals. DP261:              for (int jb = 0; jb < 5; jb++) TTY[jb] = Decimal.Round(TTY[jb], 0); DP262:              for (int jc = 0; jc < 5; jc++) strYText[jc] = TTY[jc].ToString(); DP263:            } //-----------------------------------------------------------------------------------------// DP270:          private void button1_Click(object sender, EventArgs e) DP271:            { // Close the Chart. DP272:              Close(); DP273:            } 

The Form2_Paint event handler takes over the plotting chores. Lines DP282-DP285 name the pen colors and fonts. Lines DP287-DP288 paint the Y axis, and line DP289 draws the X axis. Lines DP290-DP294 paint the numbers along the X axis. Lines DP300-DP301 paint the tick marks along the Y axis, and lines DP302-DP303 paint the tick marks along the X axis. If the plot includes a split year, lines DP304-DP309 paint a vertical green line where one year ends and the next begins.

The first data point is determined in lines DP310-DP315. Lines DP316-DP317 save the location of the data point because it will be needed when the next data point is plotted (and a line will be drawn between the two points). Lines DP318-DP329 create the series of straight lines between the old coordinate and the new coordinate. Line DP333 does all the drawing.

Continuation of Form2 Listing

 DP280:            private void Form2_Paint(object sender, PaintEventArgs e) DP281:            { // Paint the plot. DP282:              Graphics grfx = e.Graphics; DP283:              Pen blackPen = new Pen(Color.Black,2); DP284:              Font font1 = new Font("Arial", 14); DP285:              Font font2 = new Font("Arial", 10); DP286:              grfx.DrawLine(blackPen, 70, 50, 70, 450);                     // Paint the 'Y' axis dollar values. DP287:              for(int pu = 0; pu < 5; pu++) DP288:              grfx.DrawString(strYText[pu], font2, Brushes.Black, 20, 445 - 100*pu);                     // Paint the 'X' axis. DP289:              grfx.DrawLine(blackPen, 70, 450, 680, 450); DP290:              string strShYW; DP291:              strShYW = intShortYW[ASP].ToString(); DP292:              int SLL = strShYW.Length; DP293:              if(SLL == 3) strShYW = "0" + strShYW;       // Always show 4 characters. DP294:              grfx.DrawString(strShYW, font2, Brushes.Black, 52, 460); DP295:              int intEndPoint = 55 + intDeltaX * intWeekSpan; DP296:              strShYW = intShortYW[intLocalNONWs -1].ToString(); DP297:              SLL = strShYW.Length; DP298:              if(SLL == 3) strShYW = "0" + strShYW;       // Always show 4 characters. DP299:              grfx.DrawString(strShYW, font2, Brushes.Black, intEndPoint-3, 460);                     // Paint tick marks on the 'Y' axis. DP300:              for(int mm = 0; mm < 5; mm++) DP301:              grfx.DrawLine(blackPen, 65, 450-100*mm, 75, 450-100*mm);                     // Paint tick marks on the 'X' axis. DP302:              for(int mm = 0; mm <= intWeekSpan; mm++) DP303:              grfx.DrawLine(blackPen, 70+intDeltaX*mm, 445, 70+intDeltaX*mm, 455);                     // If a split year, paint a vertical line at New Year's week. DP304:              if(intSplitYear == 1) DP305:              { DP306:                Pen greenPen = new Pen(Color.Green, 2); DP307:                greenPen.DashStyle = (DashStyle) 1;       // Dash. DP308:                grfx.DrawLine(greenPen, intYearStartXCoord, 450, intYearStartXCoord,                                     50); DP309:              }                     // First point to be plotted. DP310:              string strAnswer1 = dTTWorth[ASP].ToString(); DP311:              string strAnswer2 = dTTYBaseline.ToString(); DP312:              string strAnswer3 = dTTYInc.ToString(); DP313:              int intAnswer = (int)((dTTWorth[ASP] - dTTYBaseline) / dTTYInc); DP314:              int intOldYW = intShortYW[ASP]; DP315:              int intGapDelta = 0; DP316:              int intOldX = intXOffset; DP317:              int intOldY = intYOffset - intAnswer; DP318:              for(int mm = 1; mm <= intPPDelta; mm++) DP319:              {                       // Check each record to make sure there are no gaps in the data. DP320:                int intNewYW = intShortYW[ASP+mm];                       // Determine if the YW has moved into a new year(Ex: from 0252 to 0301). DP321:                if(intNewYW > (intOldYW + 48))             // Year jump. DP322:                intGapDelta = intGapDelta + intNewYW - intOldYW - 49; DP323:                else // No year jump. DP324:                { DP325:                  if(intNewYW == intOldYW + 1)            // No gap. DP326:                  { } DP327:                  else // Weekly gap exists. DP328:                  intGapDelta = intGapDelta + intNewYW - intOldYW - 1;; DP329:                } DP330:                intOldYW = intNewYW; DP331:                int intNewX = intXOffset + intDeltaX*mm + intDeltaX*intGapDelta; DP332:                int intNewY =intYOffset-(int)((dTTWorth[ASP+mm]-dTTYBaseline)/dTTYInc); DP333:                grfx.DrawLine(blackPen, intOldX, intOldY, intNewX, intNewY); DP334:                intOldX = intNewX; DP335:                intOldY = intNewY; DP333:              } DP337:            } DP338:          } DP339:        } //========================================================================================// Form2.Designer.cs: DP350:        namespace DataPlotter DP351:        { DP352:          partial class Form2 DP353:          {                   // Required designer variable. DP354:            private System.ComponentModel.IContainer components = null;                   // Clean up any resources being used. DP355:            protected override void Dispose(bool disposing) DP356:            { DP357:              if (disposing && (components != null)) components.Dispose(); DP358:              base.Dispose(disposing); DP359:            } DP360:            #region Windows Form Designer generated code                   // Required method for Designer support. DP361:            private void InitializeComponent() DP362:            { DP363:              this.label1 = new System.Windows.Forms.Label(); DP364:              this.button1 = new System.Windows.Forms.Button(); DP365:              this.SuspendLayout();                     //                     // label1                     // DP366:              this.label1.AutoSize = true; DP367:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,                       ((byte)(0))); DP368:              this.label1.Location = new System.Drawing.Point(287, 8); DP369:              this.label1.Name = "label1"; DP370:              this.label1.Size = new System.Drawing.Size(123, 24); DP371:              this.label1.TabIndex = 0; DP372:              this.label1.Text = "KT Net Worth";                     //                     // button1                     // DP373:              this.button1.Location = new System.Drawing.Point(564, 8); DP374:              this.button1.Name = "button1"; DP375:              this.button1.Size = new System.Drawing.Size(100, 25); DP376:              this.button1.TabIndex = 1; DP377:              this.button1.Text = "Close the Chart"; DP378:              this.button1.Click += new System.EventHandler(this.button1_Click);                     //                     // Form2                     // DP379:              this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); DP380:              this.ClientSize = new System.Drawing.Size(704, 452); DP381:              this.Controls.Add(this.button1); DP382:              this.Controls.Add(this.label1); DP383:              this.Name = "Form2"; DP384:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; DP385:              this.Text = "Plot file \'KTWeeklyNetWorth.dta\'"; DP386:              this.Paint += new System.Windows.Forms.PaintEventHandler                       (this.Form2_Paint); DP387:              this.Load += new System.EventHandler(this.Form2_Load); DP388:              this.ResumeLayout(false); DP389:              this.PerformLayout(); DP390:            } DP391:            #endregion DP392:            private System.Windows.Forms.Label label1; DP393:            private System.Windows.Forms.Button button1; DP394:          } DP395:        } 
Note  

For proper presentation on the screen, the Form1_Paint event handler must be actuated at least 30 times per second (since the human eye detects flicker at rewriting rates less than 30). Thirty times per second means that Form1_Paint must rewrite in a time less than 1000/30, or 33.3 milliseconds. In the early days of Windows, PCs were clocking at speeds around 20,000 cycles per second, so it was easy to create code in Form1_Paint that could not possibly be completed in 33 milliseconds . At that time it was almost impossible to create a plot with two or more datasets superimposed on the screen because the Form1_Paint handler couldn t work fast enough to support this many datasets and hold down the flicker factor.

 


Unlocking Microsoft C# V 2.0 Programming Secrets
Unlocking Microsoft C# V 2.0 Programming Secrets (Wordware Applications Library)
ISBN: 1556220979
EAN: 2147483647
Year: 2005
Pages: 129

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net