test

progging - To wander about and beg; to seek food or other supplies by low arts; to seek for advantage by mean shift or tricks.
progging - Programmer slang for writing computer code.

søndag 11. september 2011

Bazaar Bug

At one point Bazaar started acting up. The command line tool worked just fine, but I like to use TortoiseBzr when commiting to be able to easily check all the changes I've done before the commit. But both TortoiseBzr and Bazaar Explorer only returned an error when starting up.

So I filed a bug at launchpad, and within less than an hour after I had registers at launchpad (unfortunately, you have to register to be able to submit a bug report), I got an email with a solution. It was simply a config file (qbzr.conf) that was corrupt and all that was needed was to delete it. Great support work though!!!


By the way, the problem occurred after a windows blue screen...

Delphi: General method to check if key is pressed

To check if a specific key is pressed you can use the GetKeyState method.

type
  TUtility = class
  public
    class function CheckKeyPressed(key: Integer): Boolean;
    class function ShiftKeyPressed(): Boolean;
    class function CtrlKeyPressed(): Boolean;
    class function AltKeyPressed(): Boolean;
  end
One method can check any key you want using virtual key codes:
// Helper methods to check various key states
class function TUtility.CheckKeyPressed(key: Integer): Boolean;
begin
  //The return value of GetKeyState specifies the status of the specified virtual key, as follows:
  //
  //If the high-order bit is 1, the key is down; otherwise, it is up.
  //If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled
  //if it is turned on.
  // The key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any)
  // on the keyboard will be on when the key is toggled, and off when the key is untoggled.
  //
  // Check high-order of state
  Result := (GetKeyState(key) and $80) <> 0;
end;
...and specific methods for the most interesting keys can be made:
// Helper methods to check various key states
class function TUtility.ShiftKeyPressed(): Boolean;
begin
  Result := CheckKeyPressed(VK_SHIFT);
end;

class function TUtility.CtrlKeyPressed(): Boolean;
begin
  Result := CheckKeyPressed(VK_CONTROL);
end;

class function TUtility.AltKeyPressed(): Boolean;
begin
  Result := CheckKeyPressed(VK_MENU);
end;

tirsdag 6. september 2011

Position the legend in the upper right corner of a TeeChart chart


The TeeChart has many ways of position the Legend on the chart. You can set it to custom and make it a percentage of the width of the chart, but simply putting it on the right side of the chart and make it stay there when the form was resized, was not straight forward.






The solution was to handle the ChartGetLegendPos and ChartGetLegendRect events.
// The following two event handlers are here to position the legend (and it's text)
// in the upper right corner of the chart. This was not possible to simply configure
// in the TeeChart editor.
procedure TFrm.ChartGetLegendPos(Sender: TCustomChart;
  Index: Integer; var X, Y, XColor: Integer);
var
  diff: Integer;
begin
  diff := X - XColor;
  XColor := Sender.Legend.Left + 7;
  X := XColor + diff;
end;

procedure TFrm.ChartGetLegendRect(Sender: TCustomChart;
  var Rect: TRect);
var
 w, h: Integer;
begin
  w := Rect.Right - Rect.Left;
  h := Rect.Bottom - Rect.Top;
  Rect.Top := Sender.ChartRect.Top;
  Rect.Bottom := Rect.Top + h;
  Rect.Left := Sender.ChartRect.Left + (Sender.ChartWidth - w);
  Rect.Right := Rect.Left + w;
end;

Define your custom TColor type in Delphi

Do define your own custom color in Delphi, do this:

const
  clChartRed = TColor($B3B3FF);       // 70 % gradient Red on White
  clChartYellow = TColor($B3FFFF);    // 70 % gradient Yellow on White
But remember that Delphi defines the colors as BGR and not the usual RGB so remember to swap numbers.
So the color red would be:
const
  clRed = TColor($0000FF);       // Red

tirsdag 30. august 2011

Dynamically adding TQRChart at run-time on a QuickReport

Handle the BeforePrint method in the TQuickRep class to clone the TeeChart graphs...

procedure TQReport.QReportBeforePrint(Sender: TCustomQuickRep;
  var PrintReport: Boolean);
var
  i: Integer;
  LV_Height: Integer;
  LV_Top: Integer;
  LV_Chart : TChart;
  tmp: TQRChart;
begin
  // Divide height between the N charts
  LV_Height := Round(DetailBand1.Height / ChartList.Count);
  LV_Top := 0; // Starting point

  for LV_Chart in ChartList do begin
    { Create the QRChart }
    tmp:=TQRChart.Create(Self);
    { Create the QRDBChart }
    With TQRDBChart.Create(tmp) do
    begin
      Parent:=TWinControl(tmp);
      Name:=TeeGetUniqueName(Owner,'QRChart');
      Title.Text.Clear;
      Title.Text.Add(tmp.ClassName);
    end;
    { add the QRChart to the QuickReport... }
    With tmp do
    begin
      ParentReport := Self;
      Parent := DetailBand1;
      Width := DetailBand1.Width;
      Height := LV_Height;
      Left := 0;
      Top := LV_Top;
      // Move next chart down
      LV_Top := LV_Top + LV_Height;
      { Copy Series and do special formatting}
      Chart.FreeAllSeries;
      Chart.Assign(LV_Chart);
      for i:=0 to LV_Chart.SeriesCount-1 do
        CloneChartSeries(LV_Chart[i]).ParentChart := Chart;

      Chart.Color := clWhite;
      // To include all data - and not only the last 10 years
      Chart.BottomAxis.Automatic := True;
      // Remove gradient background for printing
      Chart.BackWall.Gradient.Visible := False;
    end;
  end;
end;
The QReport tips are taken from here:
http://www.steema.com/support/faq/NewVCL/FAQ_VCL_QUICKREPORT.htm

Delphi Enumerable List - example

Just an example of a list of a specific type in delphi.
unit MyUtility;

interface

uses
  Classes,  {TList}
  Chart;    {TChart}

type
  TChartList = class;
  TChartListEnumerator = record
  private
    FIndex: Integer;
    FList: TChartList;
  public
    constructor Create(AList: TChartList);
    function MoveNext: Boolean; inline;
    function GetCurrent: TChart; inline;
    property Current: TChart read GetCurrent;
  end;
  TChartList = class(TList)
  public
    procedure Add(AChart: TChart);
    function GetItem(i:Integer): TChart;
    function GetEnumerator: TChartListEnumerator;
    property Items[i:Integer]: TChart read GetItem; default;
  end;

implementation

{TChartList}

procedure TChartList.Add(AChart: TChart);
begin
  inherited Add(AChart);
end;

function TChartList.GetItem(i:Integer): TChart;
begin
  Result := List[i];
end;

function TChartList.GetEnumerator: TChartListEnumerator;
begin
  Result := TChartListEnumerator.Create(Self);
end;

{ TChartListEnumerator }

constructor TChartListEnumerator.Create(AList: TChartList);
begin
  FIndex := -1;
  FList := AList;
end;

function TChartListEnumerator.GetCurrent: TChart;
begin
  Result := FList.List[FIndex];
end;

function TChartListEnumerator.MoveNext: Boolean;
begin
  Result := FIndex < FList.Count - 1;
  if Result then
    Inc(FIndex);
end;

end.
And it's use:
var
  Chart: TChart;
begin
  for Chart in ChartList do begin
    Chart.DoSomeThing();
  end;

Print/Preview of Quick Report problem

While testing some code that creates a Quick Report with a TeeChart on it, I changed the Print() call to a PreviewModal(). All though this seems to work at first, two problems occurred.

  1. The second time I clicked to see the preview dialog, the report did not show (only some black blocks)
  2. When I closed the application, I got several error messages.
Calling Preview() instead seem to fix the problem. Not sure what the problem was but this is from the QReport documentation:
Prepare
procedure Prepare.
Use ‘Prepare’ if you want to generate a report without automatically bring up a preview window or print it.
Preview
procedure Preview
Use ‘Preview’ to generate the report and bring up an on screen preview of it. From the preview window the user can choose to print the report.
PreviewModal
procedure PreviewModal
As Preview with the following differences -
When calling PreviewModal the report is generated in a background thread. Some database drivers are not thread safe and this might cause unexpected behavior or program crashes. Only use PreviewModal in situations where it is sure that a thread safe database driver will be used.

mandag 29. august 2011

Syntax highlighting in blog

Even though this has nothing to do with Delphi progging, I add a little post to test different syntax highlighting methods in blogger.

SyntaxHighlighter
Format: <pre class="brush:delphi">...</pre>

type
  TMyClass = class;
  if i < 0 then begin
    with DMSystem do
      Table.Create();
  end;
google code prettify Format: <pre class="prettyprint">...</pre>
type
  TMyClass = class;
  if i < 0 then begin
    with DMSystem do
      Table.Create();
  end;
int foo=0; NSLog(@"%i", foo); 
Links:
http://stackoverflow.com/questions/1852537/how-to-use-prettify-with-blogger-blogspot
http://www.cyberack.com/2007/07/adding-syntax-highlighter-to-blogger.html (but remember to add link to the delphi script)

Some SyntaxHighlighter config examples:
Format: <pre class="brush: delphi; gutter: false; toolbar: false; highlight: 5">...</pre>
type
  TMyClass = class;
  if i < 0 then begin
    with DMSystem do
      Table.Create();
  end;

How to clone a Delphi Form

In Visual Studio 2005 it's pretty easy to make a copy of an existing from directly from the project manager in the IDE (simply copy-paste with Ctrl-C and Ctrl-V). This is of course very useful instead of starting from scratch when you have a form that looks a lot like your new form will do.
The trick to do this in Delphi is:

  1. Do a "Save As" of the form in the file menu (.pas and .dfm will be copied and the unit name will be changed)
  2. Edit the saved as form to change the class name (easiest to do it in the designer)
  3. Add back the old form using "Add to Project...(Shift + F11)

Bazaar Bug

I encountered this bug in my Bazaar repository.
I'm not sure how the master branch got out of date with the bound branch, but what had happened was that some files got accidentally added at the root, then when I did a bzr rm things got screwed up. The error I got was:
bzr: ERROR: Bound branch BzrBranch7(file:///C:/Documents%20and%20Settings/xx/) is out of date with master branch BzrBranch7(file:///U:/Utvikling%20Backup/srv/bzr/xx/).
To commit to master branch, run update and then commit.
You can also pass --local to commit to continue working disconnected.

Doing a bzr update just said that
Tree is up to date at revision 1 of branch U:/Utvikling Backup/srv/bzr/xx

When I did a bzr log at the master branch it said it was up to date at revision 0.
So I knew the master branch was out of date and did a bzr push U:/Utvikling Backup/srv/bzr/xx and after that everything was fine :)

søndag 28. august 2011

Bazaar - Joining

One of the challenges when putting your project under version control is how to organize all the different projects/solutions/DLLs/what-have-your...Should everything be under one project in your VCS or should them be separate. It could be all independent programs that are released independently but still are part of the same 'package' and might even share some code.

When I first set up my projects I put them each under separate trees. I first learned this was a mistake when I wanted to move a file from one project to another. "Can't move files between different branches" - says Bazzar.

But luckily it is pretty easy to 'join' two branches into one!

My project was set up like this:

\MainDir
\MainDir\ProjectDisplay
\MainDir\ProjectLog
\MainDir\CommonSource

Because of a bug, the join command does not work if the main/root branch has no commits in it already. So if you don't have any files commited in the main directory yet, it's time to do so. First, the different project/branches was created like this:

Create common repository at main dir and make it a bazaar branch too
C:\MainDir>bzr init-repo .
C:\MainDir>bzr init .
Create a separate branch in each project directory
C:\MainDir\Dir1>bzr init .
...add and commit files in sub dir 1
C:\MainDir\Dir2>bzr init .
...add and commit files in sub dir 2
Then do a join and commit
C:\MainDir\bzr join Dir1
C:\MainDir\bzr commmit -m "joining Dir1"
The commands with output for joining two directories are shown here:


After the complete join the log history looks like this:

By default, only the main (important) revisions are shown.

Bazaar nicely handles revision history in different branches.
The solution was first found here: https://answers.launchpad.net/bzr/+question/71563

tirsdag 23. august 2011

Fun with Tooltip - Delphi THintWindow

On most occasions getting a tooltip on a control is as simple as clicking enable and typing a string.

In the case of TeeChart you can get a tooltip on a Series by adding a tool called "Mark Tips".

After assigning this to a series, a tooltip will automatically show up when you hold the mouse over the series. BUT, It will not show up when holding the mouse over the series' mark, which is what I needed. So then I had to implement the tooltip functionality my self.



I used the THintWindow class to show a tooltip that looks the same as the other tooltips, although I had to set the (background) color property to clInfoBk in order for it to look the same. To find out what part of the chart the mouse was in I used the TeeChart function procedure TCustomChart.CalcClickedPart(Pos: TPoint; Var Part: TChartClickedPart);. This will return a TChartClickedPart record that will give you a lot of useful information, like if its a Series or SeriesMarks etc. It will the also have a reference to the series itself as well as the index of the series.

I used this method to show tooltip on both the SeriesMarks and the Series, so I could skip using the "Mark Tips" tool from TeeChart.

The THintWindow was okay to use after reading the documentation. It has methods for calculating it's necessary width and height. It was too tricky to to find the size of the cursor so that it could be positioned at the bottom of the mouse cursor (like is normally done) so I simply placed it on top of the mouse cursor.
Following is the code that is called on the MouseMove event:


procedure TFrame_Plot.ShowEventSeriesToolTip();
var
  point: TPoint;
  clickedPart: TChartClickedPart;
  labelString: string;
  rect: TRect;
begin
  point := Chart.GetCursorPos();
  Chart.CalcClickedPart(point, clickedPart);
  Case clickedPart.Part of
    cpSeriesMarks, cpSeries:
    begin
      if (clickedPart.ASeries = SeriesInfoEvent) or (clickedPart.ASeries = SeriesEvent) then 
      begin
        if Assigned(CV_HintWindow) and (CV_HintWindow.Tag <> clickedPart.PointIndex) then 
        begin
          // Hide and free it!
          CV_HintWindow.ReleaseHandle();
          FreeAndNil(CV_HintWindow);
        end;
        if not Assigned(CV_HintWindow) then begin
          labelString := clickedPart.ASeries.Labels[clickedPart.PointIndex];
          CV_HintWindow := THintWindow.Create(Self);
          CV_HintWindow.Color := clInfoBk;
          // Calculate the WIDHT of the rectangle
          rect := CV_HintWindow.CalcHintRect(200, labelString, nil);
          // Find the position on screen to place the tooltip
          rect.TopLeft := Chart.ClientToScreen(point);
          // Move position so that it's above mouse cursor
          rect.Top := rect.Top - rect.Bottom;
          // Adjust Right and Bottom considering position
          rect.Right := rect.Right + rect.Left;
          rect.Bottom := rect.Bottom + rect.Top;
          // Use the tag to remember which Event we are showing tooltip for
          CV_HintWindow.Tag := clickedPart.PointIndex;
          // Show it!
          CV_HintWindow.ActivateHint(rect, labelString);
        end
      end
      else if Assigned(CV_HintWindow) then begin
        // Hide and free it!
        CV_HintWindow.ReleaseHandle();
        FreeAndNil(CV_HintWindow);
      end;
    end;
  else
    if Assigned(CV_HintWindow) then begin
      // Hide and free it!
      CV_HintWindow.ReleaseHandle();
      FreeAndNil(CV_HintWindow);
    end;
  end;
end;
The result looks like this:


That's it!

fredag 19. august 2011

Implementing enumerators on delphi records

Implementing enumerators are pretty easy in Delphi and many articles exist on the topic:

http://hallvards.blogspot.com/2007/10/more-fun-with-enumerators.html
http://www.thedelphigeek.com/2007/03/fun-with-enumerators.html

For some reason, I had a simple record to hold a small array of another type where the use was to loop round and do some action on every type. I used a record instead of a class to avoid the Create/Destroy and since it was just used to hold some simple data.
The first thing I noticed was that the Enumerator had to be a class (and not a record as recommended here) because I could not have a forward declaration to a record. Since my "list type" was a record I had to "forward declare" the enumerator before the record type.

  TLongTrendFieldsEnumerator = class;

  TLongTrendFields = record
  private
    { Private declarations }
    PlotList: array[0..MAX_LONG_TREND_FIELDS] of TPlot;
    function GetItem(index : Integer) : TPlot;
  public
    { Public declarations }
    property Items[i:Integer] : TPlot read GetItem; default;
    function GetEnumerator: TLongTrendFieldsEnumerator;
  end;

The next thing to think of is that the list type is a record, so we don't want to copy the instance when we pass it to the enumerator. So we use pointer instead, beginning with declaring a pointer type

  TLongTrendFieldsEnumerator = class;

  TLongTrendFieldsPointer = ^TLongTrendFields;
  TLongTrendFields = record
  private
  ...
So then the enumerator class will look like this:


  TLongTrendFieldsEnumerator = class
  private
    index: Integer;
    fields: TLongTrendFieldsPointer;
  public
    constructor Create(aFields: TLongTrendFieldsPointer);
    function MoveNext: Boolean;
    function GetCurrent: TPlot;
    property Current: TPlot read GetCurrent;
  end;  
The only difference in the implementation is that when the list type creates the enumerator it has to send a reference/pointer to the enumerator's constructor.
function TLongTrendFields.GetEnumerator: TLongTrendFieldsEnumerator;
begin
  Result := TLongTrendFieldsEnumerator.Create(@Self);
end;
The rest of the implementation is identical to the other examples (without pointers) thanks to Delphi's nice pointer syntax handling!

torsdag 11. august 2011

Bazaar: copy file from one branch to another unrelated branch


Bazaar was set up so that each program was it's own branch

\DisplayProgram
\LogProgram
\CommonSource

So moving a file (Statistics.pas) from the display program to the common source was not possible since you can't bzr move from one branch to another. So the solution was to use the export/import merge solution.

..DisplayProgram>bzr fast-export . > full-branch.fi
..DisplayProgram>bzr fast-import-filter -i Statistics.pas full-branch.fi > Statistics.fi
..DisplayProgram>mkdir ..\Temp
..DisplayProgram>bzr init ..\Temp
..DisplayProgram>bzr fast-import Statistics.fi ..\Temp
..DisplayProgram>cd ..\CommonSource
..CommonSource>bzr merge ..\Temp -r0..-1
..CommonSource>bzr commit -m "Moved file from SourceDisplay using export/import then merge"
From:
http://stackoverflow.com/questions/3547493/bzr-copy-file-from-one-branch-to-another-unrelated-branch

As a final step, after you are done moving the file, you probably want to remove the file from it's original branch.
..DisplayProgram>bzr rm Statistics.pas
..DisplayProgram>bzr commit -m "Final step of moving file to common place"

Note:
When I tried to do this a second time on another file (same directories involved), I got a problem. The old file, Statistics.pas, appeared in the temporary directory. I created a brand spanking new directory, even with a different name, but the old file seem to appear magically....
Then I read the bzr fast-import help...
Restarting an import:
 At checkpoints and on completion, the commit-id -> revision-id
 map is saved to a file called 'fastimport-id-map' in the control
 directory for the repository (e.g. .bzr/repository). If the import
 is interrupted or unexpectedly crashes, it can be started again
 and this file will be used to skip over already loaded revisions.
 As long as subsequent exports from the original source begin
 with exactly the same revisions, you can use this feature to
 maintain a mirror of a repository managed by a foreign tool.
 If and when Bazaar is used to manage the repository, this file
 can be safely deleted.
So, remove the fastimport-id-map file in the .bzr/repository directory when you are done with the import! Then you are ready to do another one :)

mandag 8. august 2011

Bazaar Exampe 1 - Working alone

The simplest way of using Bazaar is to simple track changes on your own little project.

1. init
2. ignore files/directories
3. add
4. commit


In the example above I add a Visual Studio 2005 project called AnyDragDrop in 4 simple steps. First move to the project directory. The init command will create the .bzr directory where all the revision and all bazaar information will be kept. After that it's useful to avoid commiting output files and generated files like .exe and object files. If you where working with others then the local user project setting files should be ignored also. See also the useful "bzr status" and "bzr ignored" commands. When everything looks okay, add the files and do the first initial commit.

Note: If you don't 'ignore' the files they will kinda be reported with error in bazaar. Either add then, delete them or ignore them! The ignore file is a simple text file that is checked in just as any other file in the directory. It is also possible to ignore file globally for one user, but I think it's generally nice to share what is ignored :-)

To make a copy branch of your code you can do that anywhere with the "bzr branch SOURCE DEST" command. This is how the Bazaar folks think you should work on a new feature or bug fix in your project, and I think it makes good sense. Make a new branch, do your thing (with many check-ins to that branch) and then finally merge in the new feature.

I made a copy of the project in a sub-directory called 'any2 of the original project:



Next: Modify the same file in both branches, resolve conflicts and merge it all back together.


Look how nicely Bazaar shows the revision history of changes done in a separate branch.

All the changes for one feature (done i a separate branch) is shown as one revision that can be expanded to see the complete history.



fredag 5. august 2011

Version Control System - Bazaar

As I find it extremely useful to work with a version control and the current workplace didn't use one, I started to look for a suitable one. I wanted to start using Git as that seems to be the new cool one, but during my research someone recommended Bazaar, saying Git could be too complicated. And being that this was something I just wanted to test out personally, I went ahead and tried it.

It was extremely easy to get started with Bazaar. Everything went smoothly, the documentation is superb! I have experience with SVN and CVS so most is very common although some concepts are very different.

The coolest thing is that it is so flexible. It's suitable from one user to large teams, you can work only locally or with a remote 'server'. It doesn't have to be server per say, just another machine that might be backed up. Even if you work towards a server you can still commit locally for a period (without access to the server). Later, you can commit to the server and all history will still be available. And there is no install on the server (every user just installs on their machine) so there is no network setup!

Dynamically adding a GRAPHIC field to an existing Paradox table

I wanted to add a few new fields to an existing Paradox database table. We already had a method for adding new fields. This is using SQL to add new fields kinda like:

'ALTER TABLE ' + TableName + ' ADD COLUMN ' + Field + ' ' + FieldType

so the FieldType is a string and one of that parameters. So I thought GRAPHIC would be a valid type. But no, it complains with the message "Capability not supported":



According to Embarcadero:
"This error is returned by the BDE when the BDE parses an SQL string to be sent to a server and the syntax of the string is not supported by the BDE"
So, as of now I still don't have a solution to this problem.

NOTE: Using BLOB as FieldName works just fine (but testing this caused other problems...)

EDIT: 2011-08-25
Using MEMO as the type gave the same error. Using BLOB works and actually creates a MEMO field! :)

Only workaround found was to delete the table and create a new one using Delphi TTable class and field type ftGraphic - FieldDefs.Add('Icon', ftGraphic, 0, False);

Useful link for simple SQL coding:
http://www.thedbcommunity.com/index.php?option=com_content&task=view&id=138&Itemid=46
This also mentions under "Unsupported Capabilities" that "There are a number of table modifications which cannot be performed using Local SQL, such as foreign key constraints, range limitations, picture settings, etc."

Hiding fields in QuickReport

After getting the image into the report, the next task was to hide it :-) Given a certain condition, part of the report should not be shown. So I had to hide some of the report fields in run-time.

This is done by handling the xxBeforePrint() events, finding the correct controls and set the Enabled property to false. I didn't have a lot of documentation on the QuickReport so it involved a bit of trial and error. The solution:

1. First hook up the event handler at some appropriate place
  // In order to disable LT analysis if not activated
if RptFrm_Header.QuickReport.Bands.HasTitle then begin
RptFrm_Header.QuickReport.Bands.TitleBand.BeforePrint := QRFuelRepHeaderBeforePrint;
end;
2. In the event handler, find the right controls and disable them
procedure TFrm_FuelRep.QRFuelRepHeaderBeforePrint(Sender: TQRCustomBand; var PrintBand: Boolean);
const
controlNames : array[0..2] of string =
(
'ReportLabelLT_Status',
'DBImageLT_Status',
'DBTextLT_Status'
);
var
name: string;
component: TComponent;
begin
for name in controlNames do begin
component := RptFrm_Header.FindComponent(name);
if (component <> nil) and (component is TControl) then begin
(component as TControl).Enabled := False;
end;
end;
end;

Getting an Image into QuickReport


I had a big problem getting a .bmp file to show up in a QReport. Everything seemed straight forward. I had to do this dynamically during run-time, so I would read in the image to a paradox database and then have the QReport point to the correct database field.

The problem was that when I first configured the QReport, the database field was MEMO and not GRAPHIC. This information is stored in the .QR2 report file. And even though I corrected this field in the database using Database Manager, it would not be updated in the QuickReport.

The solution was to remove the dataset and add it again. This time the field correctly showed up as GRAPHIC, and the image showed up beautifully :-)

One possible solution could be to only remove the field in the dataset and then add it again.

The initial code for getting in image into the DB was:


(FieldByName('Icon') as TGraphicField).LoadFromFile('\path\to\image.bmp');

Although this works fine, the image file has to be available on the disk when the program runs. A better solution is to get the images included in the .exe as an embedded resource. The simplest way to achieve this was to include a TImageList on a form, and populate this with the images I needed. Then use the following code to put them in the database:


procedure InsertImageToDB(Field: TField; ImageIndex: Integer);
var
  stream: TStream;
  bmp: TBitmap;
begin
  bmp := TBitmap.Create();
  stream := Field.DataSet.CreateBlobStream(Field, bmWrite);
  try
    Frm.ImageList.GetBitmap(ImageIndex, bmp);
    bmp.SaveToStream(stream);
  finally
    stream.Free;
    bmp.Free;
  end;
end;
This has to be included in a Edit()...Post() block to get it posted to the DB.

Intro

Wanting to have a place to write down tips/tricks and solutions to problems I encounter during my work with delphi programming, I thought a blog might do the trick.