Recent

Author Topic: Record entries are going Wonky...  (Read 486 times)

OC DelGuy

  • Full Member
  • ***
  • Posts: 221
Record entries are going Wonky...
« on: May 29, 2026, 05:25:53 pm »
Hello guys.  I need some help trying to figure out why the Date of Birth is skewed.  When entering the DOB for the first record, I'm not sure where it's going.  When entering the DOB for the second record, it goes into the first record.  When entering the DOB for the third record, it goes into the second record.  And continues, on and on and on...
In addition, when I go up and down the list (clicking on each record), the events list stays the same.  But my Procedure UpdateEventGrid; looks good.  I can't see the problem!

Thanks in advance for any help y'all can give me!

Code: Pascal  [Select][+][-]
  1. unit AgesU;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  6.   LCLType, DateUtils, Grids, EditBtn, ComCtrls, FileUtil;
  7. type
  8.   TEventRecord = record   // Record for a single event
  9.     EventName  : string;
  10.     EventDate  : TDateTime;
  11.     AgeAtEvent : Integer;
  12.   end;
  13.   TEventList = array of TEventRecord;    // Dynamic array of events
  14.   TPersonRecord = record  // Main record for the person
  15.     FullName      : string;
  16.     DateOfBirth   : TDateTime;
  17.     Description   : string;
  18.     ImageFileName : string;
  19.     Events        : TEventList;
  20.   end;
  21.   TPersonList = array of TPersonRecord;  // Dynamic array of Persons
  22.   { TForm1 }
  23.   TForm1 = class(TForm)
  24.     BtnLoad         : TButton;
  25.     BtnLoadImage    : TButton;
  26.     BtnAddEvent     : TButton;
  27.     BtnQuit         : TButton;
  28.     BtnNewPerson    : TButton;
  29.     BtnSave         : TButton;
  30.     DteDOB          : TDateEdit;
  31.     DteEventDate    : TDateEdit;
  32.     EdtName         : TEdit;
  33.     EdtEventName    : TEdit;
  34.     ImgPicture      : TImage;
  35.     LblCurrentAge   : TLabel;
  36.     LbNames         : TListBox;
  37.     MemDescription  : TMemo;
  38.     SGEvents        : TStringGrid;
  39.     Display         : TTimer;
  40.     StatusBar1      : TStatusBar;
  41.     procedure BtnAddEventClick(Sender: TObject);
  42.     procedure BtnLoadClick(Sender: TObject);
  43.     procedure BtnLoadImageClick(Sender: TObject);
  44.     procedure BtnNewPersonClick(Sender: TObject);
  45.     procedure BtnQuitClick(Sender: TObject);
  46.     procedure BtnSaveClick(Sender: TObject);
  47.     procedure DisplayTimer(Sender: TObject);
  48.     procedure DteDOBChange(Sender: TObject);
  49.     procedure FormCreate(Sender: TObject);
  50.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  51.     procedure FormShow(Sender: TObject);
  52.     procedure LbNamesClick(Sender: TObject);
  53.   private
  54.     PersonDataList: TPersonList;
  55.     CurrentPersonIndex: Integer;
  56.     FileName: string;
  57.     IsLoading: Boolean;
  58.     function CalculateAgeOnDate(DOB, EventDate: TDateTime): Integer;
  59.     function CalculateCurrentAge(DOB: TDateTime): Integer;
  60.     function ReadString(Stream: TStream): string;
  61.     procedure WriteString(Stream: TStream; const S: string);
  62.     procedure UpdateEventGrid;
  63.     procedure SortEvents;
  64.     procedure DisplayCurrentRecord;
  65.     procedure LoadListBox;
  66.     procedure SaveDataToFile;
  67.     procedure LoadDataFromFile;
  68.     procedure UpdateCurrentRecord;
  69.   public
  70.   end;
  71. var
  72.   Form1: TForm1;
  73. implementation
  74. {$R *.lfm}
  75.  
  76. procedure TForm1.WriteString(Stream: TStream; const S: string);
  77. var
  78.   StrLen: Integer;
  79. begin
  80.   StrLen := Length(S);
  81.   Stream.Write(StrLen, SizeOf(Integer));
  82.   if StrLen > 0 then Stream.Write(S[1], StrLen * SizeOf(Char));
  83. end;
  84.  
  85. function TForm1.ReadString(Stream: TStream): string;
  86. var
  87.   StrLen: Integer;
  88.   Buffer: array of Char;
  89. begin
  90.   Stream.Read(StrLen, SizeOf(Integer));
  91.   if StrLen > 0 then
  92.   begin
  93.     SetLength(Buffer, StrLen);
  94.     Stream.Read(Buffer[0], StrLen * SizeOf(Char));
  95.     Result := string(Buffer);
  96.   end
  97.   else
  98.     Result := '';
  99. end;
  100.  
  101. procedure TForm1.UpdateCurrentRecord;
  102. begin
  103.   if (CurrentPersonIndex >= 0) and (CurrentPersonIndex <= High(PersonDataList)) then
  104.   begin
  105.     PersonDataList[CurrentPersonIndex].FullName := EdtName.Text;
  106.     PersonDataList[CurrentPersonIndex].DateOfBirth := DteDOB.Date;
  107.     PersonDataList[CurrentPersonIndex].Description := MemDescription.Text;
  108.   end;
  109. end;
  110.  
  111. procedure TForm1.SaveDataToFile;
  112. var
  113.   Stream        : TFileStream;
  114.   i, SvEventIdx : Integer;
  115.   StrLen        : LongInt;
  116.   PersonCount   : Integer;
  117. begin
  118.   UpdateCurrentRecord;
  119.   try
  120.     Stream := TFileStream.Create(FileName, fmCreate);
  121.     try
  122.       PersonCount := Length(PersonDataList);
  123.       Stream.Write(PersonCount, SizeOf(PersonCount));
  124.       for i := 0 to PersonCount - 1 do
  125.       begin
  126.         WriteString(Stream, PersonDataList[i].FullName);  // Save FullName
  127.         Stream.Write(PersonDataList[i].DateOfBirth, SizeOf(TDateTime));  // Save DOB
  128.         WriteString(Stream, PersonDataList[i].Description);  // Save Description
  129.         WriteString(Stream, PersonDataList[i].ImageFileName);  // Save Image Path
  130.         // Save Events Array
  131.         SvEventIdx := Length(PersonDataList[i].Events);
  132.         Stream.Write(SvEventIdx, SizeOf(SvEventIdx));
  133.         for SvEventIdx := 0 to High(PersonDataList[i].Events) do
  134.         begin
  135.           WriteString(Stream, PersonDataList[i].Events[SvEventIdx].EventName);
  136.           Stream.Write(PersonDataList[i].Events[SvEventIdx].EventDate, SizeOf(TDateTime));
  137.           Stream.Write(PersonDataList[i].Events[SvEventIdx].AgeAtEvent, SizeOf(Integer));
  138.         end;
  139.       end;
  140.     finally
  141.       Stream.Free;
  142.     end;
  143.   except
  144.     on E: Exception do ShowMessage('Critical Error during Save: ' + E.Message);
  145.   end;
  146. end;
  147.  
  148. procedure TForm1.LoadDataFromFile;
  149. var
  150.   Stream        : TFileStream;
  151.   Count         : LongInt;
  152.   i, LdEventIdx : Integer;
  153.   NameLen, DescLen, ImageLen: LongInt;
  154. begin
  155.   CurrentPersonIndex := -1;
  156.   if not FileExists(FileName) then
  157.   begin
  158.     SetLength(PersonDataList, 0);
  159.     Exit;
  160.   end;
  161.   try
  162.     Stream := TFileStream.Create(FileName, fmOpenRead);
  163.     try
  164.       Count := 0;
  165.       Stream.Read(Count, SizeOf(Count));
  166.       if (Count < 0) or (Count > 10000) then Raise Exception.Create('Invalid record count.');
  167.       SetLength(PersonDataList, Count);
  168.       for i := 0 to Count - 1 do
  169.       begin
  170.         PersonDataList[i].FullName := ReadString(Stream);  // Read FullName
  171.         Stream.Read(PersonDataList[i].DateOfBirth, SizeOf(TDateTime));  // Read DOB
  172.         PersonDataList[i].Description := ReadString(Stream);  // Read Description
  173.         PersonDataList[i].ImageFileName := ReadString(Stream);  // Read ImageFileName
  174.         // Read Events
  175.         Stream.Read(LdEventIdx, SizeOf(LdEventIdx));
  176.         SetLength(PersonDataList[i].Events, LdEventIdx);
  177.         for LdEventIdx := 0 to High(PersonDataList[i].Events) do
  178.         begin
  179.           PersonDataList[i].Events[LdEventIdx].EventName := ReadString(Stream);
  180.           Stream.Read(PersonDataList[i].Events[LdEventIdx].EventDate, SizeOf(TDateTime));
  181.           Stream.Read(PersonDataList[i].Events[LdEventIdx].AgeAtEvent, SizeOf(Integer));
  182.         end;
  183.       end;
  184.     finally
  185.       Stream.Free;
  186.     end;
  187.   except
  188.     on E: Exception do
  189.     begin
  190.       ShowMessage('Error loading file: ' + E.Message);
  191.       SetLength(PersonDataList, 0);
  192.     end;
  193.   end;
  194. end;
  195.  
  196. procedure TForm1.DisplayCurrentRecord;
  197. begin
  198.  IsLoading := True;
  199.  try
  200.    Try
  201.      if (CurrentPersonIndex < 0) or (CurrentPersonIndex > High(PersonDataList)) then
  202.      begin
  203.        EdtName.Text := '';
  204.        DteDOB.Date := 0;
  205.        MemDescription.Text := '';
  206.        ImgPicture.Picture.Clear;
  207.        LblCurrentAge.Caption := 'Age: N/A';
  208.        SGEvents.RowCount := 1;
  209.        Exit;
  210.      end;
  211.      EdtName.Text := PersonDataList[CurrentPersonIndex].FullName;
  212.      DteDOB.Date := PersonDataList[CurrentPersonIndex].DateOfBirth; // Ensure DteDOB is updated
  213.      MemDescription.Text := PersonDataList[CurrentPersonIndex].Description;
  214.      if (PersonDataList[CurrentPersonIndex].ImageFileName <> '') and
  215.         FileExists(PersonDataList[CurrentPersonIndex].ImageFileName) then
  216.        ImgPicture.Picture.LoadFromFile(PersonDataList[CurrentPersonIndex].ImageFileName)
  217.      else
  218.        ImgPicture.Picture.Clear;
  219.      SortEvents;
  220.      UpdateEventGrid;
  221.      LblCurrentAge.Caption :='Age: ' + IntToStr(CalculateAgeOnDate(DteDOB.Date, Date));
  222.    except
  223.      on E: Exception do ShowMessage('Error displaying record: ' + E.Message);
  224.    end;
  225.  finally
  226.    IsLoading := False;
  227.  end;
  228. end;
  229.  
  230. procedure TForm1.LbNamesClick(Sender: TObject);
  231. begin
  232.   UpdateCurrentRecord; // Save changes before switching
  233.   if (LbNames.ItemIndex >= 0) and (LbNames.ItemIndex <= High(PersonDataList)) then
  234.   begin
  235.     CurrentPersonIndex := LbNames.ItemIndex;
  236.     DisplayCurrentRecord; // This updates DteDOB and calculations
  237.   end;
  238. end;
  239.  
  240. procedure TForm1.UpdateEventGrid;
  241. var
  242.   i: Integer;
  243. begin
  244.   if (CurrentPersonIndex < 0) or (CurrentPersonIndex > High(PersonDataList)) then
  245.   begin
  246.     SGEvents.RowCount := 1;
  247.     Exit;
  248.   end;
  249.   SGEvents.RowCount := Length(PersonDataList[CurrentPersonIndex].Events) + 1;
  250.   for i := 0 to High(PersonDataList[CurrentPersonIndex].Events) do
  251.   begin
  252.     PersonDataList[CurrentPersonIndex].Events[i].AgeAtEvent :=
  253.       CalculateAgeOnDate(PersonDataList[CurrentPersonIndex].DateOfBirth,
  254.                          PersonDataList[CurrentPersonIndex].Events[i].EventDate);
  255.     SGEvents.Cells[0, i + 1] := PersonDataList[CurrentPersonIndex].Events[i].EventName;
  256.     SGEvents.Cells[1, i + 1] := DateToStr(PersonDataList[CurrentPersonIndex].Events[i].EventDate);
  257.     SGEvents.Cells[2, i + 1] := IntToStr(PersonDataList[CurrentPersonIndex].Events[i].AgeAtEvent);
  258.   end;
  259. end;
  260.  
  261. function TForm1.CalculateAgeOnDate(DOB, EventDate: TDateTime): Integer;
  262. var Years: Integer;
  263. begin
  264.   if (DOB = 0) or (EventDate < DOB) then Exit(0);
  265.   Years := YearOf(EventDate) - YearOf(DOB);
  266.   if (MonthOf(EventDate) < MonthOf(DOB)) or
  267.      ((MonthOf(EventDate) = MonthOf(DOB)) and (DayOf(EventDate) < DayOf(DOB))) then
  268.     Dec(Years);
  269.   Result := Years;
  270. end;
  271.  
  272. function TForm1.CalculateCurrentAge(DOB: TDateTime): Integer;
  273. begin
  274.   Result := CalculateAgeOnDate(DOB, Date);
  275. end;
  276.  
  277. procedure TForm1.FormCreate(Sender: TObject);
  278. begin
  279.   DefaultFormatSettings.ShortDateFormat := 'dd mmm yyyy';
  280.   DefaultFormatSettings.DateSeparator := ' ';
  281.   FileName := ChangeFileExt(Application.Location, '.dat');
  282.   SGEvents.Cells[0, 0] := 'Event';
  283.   SGEvents.Cells[1, 0] := 'Date';
  284.   SGEvents.Cells[2, 0] := 'Age';
  285.   LoadDataFromFile;
  286.   if Length(PersonDataList) > 0 then
  287.     begin
  288.       LoadListBox;
  289.       LbNames.ItemIndex := 0;
  290.       CurrentPersonIndex := 0;
  291.       DisplayCurrentRecord;
  292.     end;
  293. end;
  294.  
  295. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  296. begin
  297.   SaveDataToFile;
  298. end;
  299.  
  300. procedure TForm1.FormShow(Sender: TObject);
  301. begin
  302.   EdtName.SetFocus;
  303. end;
  304.  
  305. procedure TForm1.BtnNewPersonClick(Sender: TObject);
  306. var
  307.   PersonName : string;
  308.   PersonDOB  : TDateTime;
  309.   PersonDesc : string;
  310.   NewIndex   : Integer;
  311. begin
  312.   PersonName := Trim(EdtName.Text);  // Read values from the input fields
  313.   PersonDOB  := DteDOB.Date;
  314.   PersonDesc := Trim(MemDescription.Text);
  315.   if PersonName = '' then  // Validate that at least a name was entered
  316.   begin
  317.     ShowMessage('Please enter a name.');
  318.     Exit;
  319.   end;
  320.   NewIndex := Length(PersonDataList);  // Add a new person to the dynamic array
  321.   SetLength(PersonDataList, NewIndex + 1);
  322.   PersonDataList[NewIndex].FullName := PersonName;
  323.   PersonDataList[NewIndex].DateOfBirth := PersonDOB;
  324.   PersonDataList[NewIndex].Description := PersonDesc;
  325.   PersonDataList[NewIndex].ImageFileName := '';
  326.   SetLength(PersonDataList[NewIndex].Events, 0); // Initialize empty events
  327.   CurrentPersonIndex := NewIndex;
  328.   EdtName.Clear;
  329.   DteDOB.Date := Now;
  330.   MemDescription.Clear;
  331.   LoadListBox;
  332.   DisplayCurrentRecord;
  333. end;
  334.  
  335. procedure TForm1.BtnQuitClick(Sender: TObject);
  336. begin
  337.   Close;
  338. end;
  339.  
  340. procedure TForm1.BtnSaveClick(Sender: TObject);
  341. begin
  342.   SaveDataToFile;
  343. end;
  344.  
  345. procedure TForm1.DteDOBChange(Sender: TObject);
  346. begin
  347.  if IsLoading then Exit;  // Ignore changes during display updates
  348.   if (CurrentPersonIndex >= 0) and (CurrentPersonIndex <= High(PersonDataList)) then
  349.  begin
  350.  PersonDataList[CurrentPersonIndex].DateOfBirth := DteDOB.Date;
  351.  UpdateEventGrid;
  352.  LblCurrentAge.Caption :='Age: ' + IntToStr(CalculateAgeOnDate(DteDOB.Date, Date));
  353.  end;
  354. end;
  355.  
  356. procedure TForm1.SortEvents;
  357. var i, j: Integer; Temp: TEventRecord;
  358. begin
  359.   if (CurrentPersonIndex < 0) then Exit;
  360.   for i := 1 to High(PersonDataList[CurrentPersonIndex].Events) do
  361.   begin
  362.     Temp := PersonDataList[CurrentPersonIndex].Events[i];
  363.     j := i - 1;
  364.     while (j >= 0) and (PersonDataList[CurrentPersonIndex].Events[j].EventDate > Temp.EventDate) do
  365.     begin
  366.       PersonDataList[CurrentPersonIndex].Events[j+1] := PersonDataList[CurrentPersonIndex].Events[j];
  367.       Dec(j);
  368.     end;
  369.     PersonDataList[CurrentPersonIndex].Events[j+1] := Temp;
  370.   end;
  371. end;
  372.  
  373. procedure TForm1.LoadListBox;
  374. var i: Integer;
  375. begin
  376.   LbNames.Clear;
  377.   for i := 0 to High(PersonDataList) do LbNames.Items.Add(PersonDataList[i].FullName);
  378. end;
  379.  
  380. procedure TForm1.BtnAddEventClick(Sender: TObject);
  381. var NewLen: Integer;
  382. begin
  383.   if (CurrentPersonIndex < 0) or (Trim(EdtEventName.Text) = '') then Exit;
  384.   NewLen := Length(PersonDataList[CurrentPersonIndex].Events) + 1;
  385.   SetLength(PersonDataList[CurrentPersonIndex].Events, NewLen);
  386.   with PersonDataList[CurrentPersonIndex].Events[NewLen-1] do
  387.   begin
  388.     EventName := EdtEventName.Text;
  389.     EventDate := DteEventDate.Date;
  390.   end;
  391.   SortEvents;
  392.   UpdateEventGrid;
  393. end;
  394.  
  395. procedure TForm1.BtnLoadClick(Sender: TObject);
  396. begin
  397.   LoadDataFromFile
  398. end;
  399.  
  400. procedure TForm1.BtnLoadImageClick(Sender: TObject);
  401. var OD: TOpenDialog;
  402. begin
  403.   if CurrentPersonIndex < 0 then Exit;
  404.   OD := TOpenDialog.Create(nil);
  405.   try
  406.     if OD.Execute then
  407.     begin
  408.       PersonDataList[CurrentPersonIndex].ImageFileName := OD.FileName;
  409.       DisplayCurrentRecord;
  410.     end;
  411.   finally
  412.     OD.Free;
  413.   end;
  414. end;
  415.  
  416. procedure TForm1.DisplayTimer(Sender: TObject);
  417. begin
  418.   if (CurrentPersonIndex >= 0) and (PersonDataList[CurrentPersonIndex].DateOfBirth <> 0) then
  419.     LblCurrentAge.Caption := 'Age: ' + IntToStr(CalculateCurrentAge(PersonDataList[CurrentPersonIndex].DateOfBirth));
  420.   StatusBar1.Panels[0].Text := 'Person Records: ' + IntToStr(Length(PersonDataList));
  421.   StatusBar1.Panels[1].Text := 'Current Person: ' + IntToStr(CurrentPersonIndex);
  422.   StatusBar1.Panels[2].Text := 'ListBox Index: ' + IntToStr(LbNames.ItemIndex);
  423. end;
  424.  
  425. end.
Free Pascal Lazarus Version #: 4.6
Date: 16 MAR 2026
FPC Version: 3.2.2
Revision: Lazarus_4_6
x86_64-win64-win32/win64

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: Record entries are going Wonky...
« Reply #1 on: May 29, 2026, 07:40:52 pm »
I think the error is in TForm1.DteDOBChange.

Say you added a person.
Then you change the date in the control.
This will change the DOB of PersonDataList[CurrentPersonIndex], which at that point points to the last person you added.

Example, assuming there are no persons yet in the array.

Fill in name and DOB
Click the BtnNewPerson button
This will create PersonDataList[0] with the correct DOB.
CurrentPersonIndex will be 0

Now fill in a new name and a new DOB
Setting DOB now alters PersonDataList[CurrentPersonIndex].DateOfBirth, but CurrentPersonIndex still is 0, so it will overwrite the previously entered DOB
Click the BtnNewPerson button
This will create PersonDataList[1] with the correct DOB.
CurrentPersonIndex will be 1

And so on, and so on....


So, you should not update DOB in TForm1.DteDOBChange, you should only do so in BtnNewPersonClick.

Bart

PS. In future better attach a sample project (sources only, zipped). That makes it a lot easier to do the bug hunting.
I did not investigate the EventsList issue, mainly because of that.
« Last Edit: May 29, 2026, 07:44:18 pm by Bart »

OC DelGuy

  • Full Member
  • ***
  • Posts: 221
Re: Record entries are going Wonky...
« Reply #2 on: May 31, 2026, 03:07:21 am »
PS. In future better attach a sample project (sources only, zipped).

Thanks Bart.  I'll get right on that.  Can you tell me what you mean by this quoted part?  I included the actual project not just a sample.  Above is the entire unit.  I didn't change anything in the project file (.lpr).  What, exactly, do I need to attach?
Free Pascal Lazarus Version #: 4.6
Date: 16 MAR 2026
FPC Version: 3.2.2
Revision: Lazarus_4_6
x86_64-win64-win32/win64

Thausand

  • Hero Member
  • *****
  • Posts: 560
Re: Record entries are going Wonky...
« Reply #3 on: May 31, 2026, 05:00:17 am »
@OC DelGuy: https://wiki.lazarus.freepascal.org/Forum#Sharing_large_pieces_of_code

You example is 1x unit and not have project file and no lfm. Then can not compile and must remake exact same form and project you have. Link is show how can make share what files is need for compile.
A docile goblin always follow HERMES.md

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: Record entries are going Wonky...
« Reply #4 on: May 31, 2026, 10:50:53 am »
Thanks Bart.  I'll get right on that.  Can you tell me what you mean by this quoted part?  I included the actual project not just a sample.  Above is the entire unit.  I didn't change anything in the project file (.lpr).  What, exactly, do I need to attach?

Menu->Project->Publish Project
Choose an existing directory in the dialog.
Click OK

Now Lazarus copies (and by default zip's) all source files of you project (*.pas;*.pp, *.lfm; *.lpr; *.lpi).
If you attach that here, I can download and simply build your project.

Now I had to re-create (parts of) your applcation by hand.
For example: I cannt see which options your TStringGrid has.
From your code alone I also cannot knwo for sure which eventhandles (OnClick, OnChange etc) belong to which controls.
This information is strored in the .lfm file of the unit in question.
Compiler options (which can influence how bugs present themselves) ares (mostly) stored in the .lpi file.
Same issue for dependencies on used pachages.
Etc. etc.

Bart

OC DelGuy

  • Full Member
  • ***
  • Posts: 221
Re: Record entries are going Wonky...
« Reply #5 on: June 01, 2026, 01:30:50 am »
Hmmm...  Never done this.  Seen "publish" on the menu, but never used it.  Cool! 8-)😎
Free Pascal Lazarus Version #: 4.6
Date: 16 MAR 2026
FPC Version: 3.2.2
Revision: Lazarus_4_6
x86_64-win64-win32/win64

Zvoni

  • Hero Member
  • *****
  • Posts: 3397
Re: Record entries are going Wonky...
« Reply #6 on: June 01, 2026, 08:02:11 am »
I think the error is in TForm1.DteDOBChange.

*snip*
Agreed. My Money is on that, too
Quote
So, you should not update DOB in TForm1.DteDOBChange, you should only do so in BtnNewPersonClick.

Bart
Or use a blocking variable "IsAdding" or something.
Default is False
When entering AddnewPerson set to True, when hitting save set to False
in DOBChange only execute if IsAdding is False.

That's rudimentary now....
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

 

TinyPortal © 2005-2018