Showing posts with label Delphi RAD Studio XE. Show all posts
Showing posts with label Delphi RAD Studio XE. Show all posts

Monday, November 7, 2011

Play Sound file without importing winmm.dll or microsoft.visualbasic.dll

Q.) I am writing a program using Delphi prism. The goal is to be able run on windows and linux (mono) from the same project. So, at this point I need to have a way of playing a sound file for windows and linux(mono) without having to import winmm.dll or microsoft.visualbasic.dll.
Since I need this to work on mono also, I don't want to use visualbasic.dll. In the past, it gave me all kinds of issues.
Are there ways to play sound file without using these dll files?

UPDATE
No matter how the soundplayer is instantiated and used, it always works fine under windows OS, whereas on Linux under mono it sometimes plays and other times it just won't play at all.

First Version:
  var thesound := new SoundPlayer;

 
if Environment.OSVersion.Platform = Environment.OSVersion.Platform.Unix then
    thesound
.SoundLocation := '/sounds/Alarms.wav'
 
else
    thesound
.SoundLocation:='\sounds\Alarms.wav';

  thesound
.Load;
  thesound
.PlayLooping;
Second Version

var sp := new SoundPlayer(new FileStream("/sounds/Alarms.wav", FileMode.Open, FileAccess.Read, FileShare.Read));
sp
.PlayLooping
 
A.) You can use System.Media.SoundPlayer. This is implemented by Mono. 

What is the difference between System.Windows.Point and System.Drawing.Point namespace?

Q.) I want to draw a line using controlpaint.DrawReversibleLine method and tried to pass system.windows.point, but it wants system.drawing.point input. I am little confused. What are the differences between them? Or How are they different besides the noticeable differences?
Thanks,

A.) System.Windows.Point is a WPF structure, whereas System.Drawing.Point is a WinForms structure. In general, if you're writing a WinForms app, the don't use stuff from any of the System.Windows namespaces (except System.Windows.Forms). The resources in these namespaces are WPF specific, so unless you intend to interface WPF and WinForms, it would be best to avoid them.

Other answers:
  • System.Windows.Point is intended for WPF applications.
    The System.Windows namespaces contain types used in Windows Presentation Foundation (WPF) applications, including animation clients, user interface controls, data binding, and type conversion
  • System.Drawing.Point is intended for WinForms applications.
    The System.Drawing namespace provides access to GDI+ basic graphics functionality.


Tuesday, July 5, 2011

delphi 7.0, delphi 2010 and binary files.

My software developed under Delphi 7.0 has been upgraded and developed under Delphi 2010 RAD Studio XE. It also saves or writes user's settings into binary files. The problem I am running into is that my Delphi 2010 application or software is expected to read binary files created by Delphi 7.0 application, but Delphi 2010 application has problem reading the binary file. Both of these software are copy of each other.
I did set the Record Field Alignment option for the project at 1 on Delphi 7.0 and byte on Delphi 2010.
I am always running into exception "read beyond end of line," when the software reads the binary file.
I read and write a binary file by doing the following.

Writing into a binary file:
procedure WriteUnitFile;
var
  unitFile
: File;
  x
:integer;
 
FileHeader:TFileHeader;
begin
 
if not DemoMode then
 
begin
   
FileHeader.id := 'UnitFile';
   
FileHeader.version := 7;

   
if fileexists(basedir+unitfilename) then
     
BackupFile(basedir+unitfilename);

   
AssignFile(unitFile,baseDir+unitfilename);

   
if UnitList.Count > 0 then
   
begin
     
Rewrite(unitFile,1);
     
BlockWrite(unitFile, FileHeader, SizeOf(FileHeader));
     
for x := 0 to UnitList.Count - 1 do
     
begin
       
TUnit(UnitList[x]).Write(unitFile);
     
end;
   
end
   
else
     
DeleteFile(baseDir+unitfilename);

   
CloseFile(unitFile);
 
end;
end;
 
Reading from a binary file:
procedure ReadUnitFile;
var
  unitFile
:File;
  newUnit
,simUnit:TUnit;
  ut
,TypeCard:TUnitType;
 
New_Ver_File:TFileHeader;
 
Address:SmallInt;
 
State:TUnitState;
 
SubAddress:SmallInt;
 
MyfDefs:array[1..20] of SmallInt;
  fDefs
:array[1..16] of SmallInt;
begin

 
if FileExists(baseDir + unitfilename) then
 
begin
    oneUnit
:= false;
   
AssignFile(unitFile,baseDir+unitfilename);
   
AssignFile(newUnitFile,baseDir+Dummyfilename);
   
Reset(unitFile,1);

   
BlockRead(unitFile,New_Ver_File,SizeOf(New_Ver_File));

   
Reset(UnitFile,1);
   
BlockRead(UnitFile,New_Ver_File,SizeOf(New_Ver_File));

   
while not Eof(UnitFile) do
   
begin
     
BlockRead(UnitFile,ut,SizeOf(ut));
     
case ut of
        tutSimulator
:newUnit := TSimulator.Create;
        tutDX2202
:newUnit := TDX2202.Create;
        tutDX8884CS
:newUnit:=TDX8884CS.Create;
        tutDX8F44F
: newUnit := TDX8F44F.Create;
        tutDX0008
:newUnit := TDX0008.Create;
        tutDX0800
:newUnit := TDX0800.Create;
        tutDX8000
:newUnit := TDX8000.Create;
        tutDX1000
:newUnit := TDX1000.Create;
        tutDX0100
:newUnit := TDX0100.Create;
        tutDX4404
:newUnit := TDX4404.Create;
        tutDX0020
:newUnit := TDX0020.Create;
        tutDX0080
:newUnit := TDX0080.Create;        
        tutDX8814
:newUnit := TDX8814.Create;
        tutDX8814CS
:newUnit := TDX8814CS.Create;
        tutDX8884
:newUnit := TDX8884.Create;
       
else
                newUnit
:= TUnit.Create;
     
end;
      newUnit
.Read(unitFile);
     
if DemoMode then
     
begin
       
if oneUnit = true then
       
begin
          simUnit
:= TSimulator.Create;
          simUnit
.Assign(newUnit);
          newUnit
.Free;
          newUnit
:= simUnit;
       
end
       
else
       
begin
          oneUnit
:= true;
       
end;
     
end;
      unitList
.Add(newUnit);
   
end;
   
CloseFile(unitfile);
   
UnitsDlg.UnitGrid.RowCount := unitList.Count+1;
   
if UnitsDlg.UnitGrid.RowCount > 1 then
     
UnitsDlg.UnitGrid.FixedRows := 1;
   
UnitsDlg.FillIn;
 
end
 
else
 
begin
       
UnitsDlg.UnitGrid.RowCount := 1;
 
end;
end;
 
UPDATE: Here is the user-defined datatypes:
  TFileHeader = record
    id
:string[32];
    version
:SmallInt;
 
end;
TUnitType = (tutUnused,tutSimulator,tutDX2202,tutDX0008,tutDX0800,tutDX8000,
                 tutDX1000
,tutDX4404,tutDX0020,tutDX8814,tutDX8814CS,
                 tutDX8884
,tutDX8884CS,tutDX8f44f,tutDX0080,tutDX0100,tutLast);
TUnitState = (tusDisabled,tusEnabled);
TUnit = class(TObject)
  changeLink
:TUnit;
 
Address: SmallInt;
 
SubAddress:SmallInt;
 
State:TUnitState;
  uType
:TUnitType;
 
RegCnt:integer;
  fRegs
:array[1..20] of SmallInt;
  fDefs
:array[1..20] of SmallInt;
 
MsgCnt:LongWord;
 
RxCnt:LongWord;
 
BreakCnt: LongWord;
 
LineErrCnt: LongWord;
 
OtherErrCnt:LongWord;
 
TimeoutCnt: LongWord;
 
BadMsgErrCnt:LongWord;
 
XSumErrCnt:LongWord;
 
OutsFlag:Boolean;
 
CommBits:LongWord;
 
OfflineCnt:integer;
 
Online:Boolean;
 
CurReg:integer;
  selectedonce
,usedIP:Boolean;
 
LastDigitalOut,LastDigitalIn,
 
CurRegOut,umsglen,urmsglen,
  dummycount
,Unitlocation,
 
CommLocation:integer;
private
public
   followed
by list of procedures and functions...
end;
 
Is there something I am missing or not understanding very well?

One thing I did forget to mention is that Delphi 2010 application reads and writes into binary files just fine without making any changes to the code, but it has problem only when reading files created by Delphi 7.0. application.

Obviously, this question is related to Record Field Alignment. So, what should I set it on my Delphi 2010 (Byte, Word, Double Word, Quad Word), if Delphi 7.0 is set at 1.

Thanks in advance.

Answer:
The key is in the TFileHeader declaration (and maybe the TUnit and TUnitType declaration). You should add these definitions to your question.
If you define a pure string in either of these records, you need to use ShortString instead of String in Delphi 2010 to make it work.
String in Delphi 7 = 1 byte per char.
String in Delphi 2010 = 2 bytes per char.

Update:

Your last update doesn't really reveal any new information, but the fDefs variable in the ReadUnitFile procedure is defined as an array[1..16] of SmallInt;, but in your TUnit class, fDefs is an array[1..20] of SmallInt;?

The fDefs variable doesn't seem to be used, but this might be the difference that causes the EOL error?
You should define arrays like this as a common Type, anyway, to ensure that they can be passed around and assigned to each other as compatible types (eg. as parameters in methods).

Yet an update:

The error is not in the strings, but in the Size of TObject.
In Delphi 2009, TObject increased the size from 4 bytes to 8 bytes.
The first 4 bytes is a pointer to the objects VMT (as it has been for a long time), and in the last 4 bytes (IIRC), it is possible to contain a reference to a synchronization monitor.
Have a look at this nice article: http://blogs.teamb.com/craigstuntz/2009/03/25/38138/
 

Deleting TList within a TList.

I am trying to free Tlist within a Tlist in a onDestroy event and FastMM4 is raising an access violation error. Here is the code snippet.
procedure TSignalFrm.FormDestroy(Sender: TObject);
var
  x
,y: integer;
begin
 
for x := 0 to signalList.Count - 1 do
 
begin
   
for y:=0 to TSignal(SignalList.Items[x]).alarmList.Count-1 do
   
begin
     
TAlarm(TSignal(SignalList.Items[x]).alarmList.Items[y]).Free;
   
end;
   
TSignal(SignalList.Items[x]).AlarmList.Free;
   
TSignal(SignalList.Items[x]).Free;
 
end;
 
SignalList.Free;
end;
I get access violation error at TSignal(SignalList.items[x]).Free; line. Freeing AlarmList items before freeing SignalList items raises the access violation error, but WHY?
Update: I am using Delphi 7.0 on Windows XP. The actual FastMM4 messages is as follows.

FastMM has detected an attempt to call a virtual method on a freed object. An access viloation will now be raised in order to abort the current operation.
Freed Object class: TList
Virtual method: Destroy
Virtual method address:427CF0
The allocation number was: 80055
Followed by a lots of memory dump.

According to this FastMM4 error, if you free an object within an another object, you automatically free the owner as well. I know that can't be true, but correct me if I am wrong.

Answer:
Does TSignal not free its AlarmList member in its destructor? (That’s how I would do this).
Update: does it work if you remove the TSignal(SignalList.Items[x]).AlarmList.Free; line?
Second update: Each TList's items need to be freed, if it contains pointers to objects.
Your problem was that TSignal is not a TList. Since it takes care of freeing its members (such as the Alarmlist), that Alarmlist should not be freed explicitly.
 

Serial Communication (RS232) in LINUX

I am currently working with Delphi Prism for Mono development. So, the software can be cross-platform mainly to be run on Linux OS. To get started, I have been implementing and test running the basic functions of my software - Serial and Network Communication.

But I can't seem to get passed serial communication implementation for Mono. It seems that there is no library or .NET framework that supports Linux environment after searching the Internet and Stackoverflow. Although there are some similar questions that were asked by others in Stackoverflow, the answers don't really show any sample. I am kind of stuck.

Here is the code I wrote for Mono serial comm. after visiting this one website.
  {$IFDEF LINUX}
   
if SerialPort1 = nil then
       
SerialPort1 := new System.Io.Ports.SerialPort();
   
SerialPort1.Close;

   
SerialPort1.BaudRate:=19200;
   
SerialPort1.DataBits:=8;
   
SerialPort1.DtrEnable:=true;
   
SerialPort1.Parity:=System.IO.Ports.Parity.Even;
   
SerialPort1.PortName:="/dev/ttyS0";
   
SerialPort1.ParityReplace:=63;
   
SerialPort1.ReadBufferSize:=4096;
   
SerialPort1.ReadTimeout:=1000;
   
SerialPort1.RtsEnable:=true;
   
SerialPort1.StopBits:=System.IO.Ports.StopBits.One;
   
SerialPort1.WriteTimeout:=1000;
   
SerialPort1.Open;

   
while (true) do
   
begin
       
CommByte[0]:=$FF;
       
CommByte[1]:=$04;
       
CommByte[2]:=$04;
       
CommByte[3]:=thechannel;
       
CommByte[4]:=mcommand;
       
CommByte[5]:=(CommByte[2] xor CommByte[3] xor CommByte[4]);

       
SerialPort1.Write(CommByte,0,6);
       
while SerialPort1.BytesToWrite>0 do;
       
Thread.Sleep(10);
       
Application.DoEvents;
   
end;
{$ENDIF}
 
But everytime I run this code under Linux, Mono raises a message box with "The requested feature is not implemented." I can't understand why. Is this even possible to do for Mono?
I need to get access to the serial port on Linux for RS232 communication.
Thanks,

Answer:
The property ParityReplace is currently not implemented. If it is not really needed, throw it away and you won't probably have this exception. And if you need it, tell me, it could be probably be done using PARMRK and changing mark to this byte. However I have also to find an idea how to unit test it ;)

Serial Communication (RTS) and Windows 7

I am developing Delphi application on Delphi 2010 XE RAD Studio under Windows 7. My application talks on the serial port non-stop. I am using AsyncPro for Delphi 2010. Serial communication and everything else on the computer I develop with works great without any problem. However, when my release version of my application is run on another Windows 7 system, serial communication completely fails. We probed the serial communication itself for an answer and found out that Request to Send (RTS) line is not dropped right after sending all the bytes, whereas on my development computer RTS line is dropped correctly.
Even when I explicitly drop the RTS line to low or false state, RTS line doesn't drop right away but after good 15 milliseconds. Thus, serial communication on my release version is failing.
Am I missing important information about Windows 7 and serial communication issues?

UPDATE: I just found the bug with my Aysncpro 5.0 for Delphi XE. It is weird. When my Delphi XE IDE is open or running, my program is communicating flawlessly. When I shutdown or close my Delphi XE IDE while my program is running, the same program doesn't communicate very well or it times out.

Chime in if you have any idea why it is happening.
Any help will be appreciated.

Thank you,

Answer:

Sounds like a timer resolution problem to me. I had the same problem trying to write to a USB FTDI driver using an event based timer with timeSetEvent()... When Delphi loads, it changes the timer resolution to less than 20ms, which made my app work fine. When the IDE wasn't running I couldn't get things to work below 20ms +/- 5ms (the default Windows resolution I believe).

To fix the problem, I call timeBeginPeriod(1) in the thread to set the minimum system wide timer resolution.

I believe this affects the resolution of other time based events, because I get better than +/-5ms accuracy on other (non-multimedia timer) wait events in my app when I use timeBeginPeriod().
So, what I'm suggesting is that somewhere in the AsyncPro code it's using some time based event or call back... That would be affected by Delphi's change to the timer resolution when it is loaded. Try calling timeBeginPeriod(1) somewhere in your app when it starts and see if there is a change.
Oh, and don't forget to call timeEndPeriod(1) when your app shuts down.
N@

VCL TTimer stops when a window dragged or pull down menu clicked

I have TTimer enabled and is supposed to run non-stop forever until the user stop it. However, it doesn't work that way. Within OnTimerevent, it processes window messages over and over again in milliseconds.
For instance, here is a snippet of my code.


procedure TDXCommDlg.Timer2Timer(Sender: TObject);
begin
  inherited
;
 
if Scanning then
 
begin
   
Timer1.Enabled := false;
   
Timer2.Enabled := false;
     
while not PostMessage(Handle,WM_USER + 10,1234,5678) do;
     
Timer1.Enabled := true;
 
end;
end;
 
 
What happens is this. While the TTimer is enabled and running, you drag any windows of the application or click on pull down menu the TTimer event completely stops working, even though I have taken precautionary steps in other part of the code to prevent this from happening. However, it doesn't seem to be helping.
The only way to restart the OnTimer event is to stop and restart the Timer by the user through TButton event.
The same code or program works fine under Windows XP compiled with Delphi 7. Currently, I am using Windows 7 and Delphi 2010 to rebuild my system.
I will try to give you more information. What I am working on is a copyrighted software.
There is a user defined procedure called HandleMsg. It actually processes the serial port messages. HandleMsg is set to the Application event onMessage;
Application.onMessage:=HandleMsg();
PostMessage is associated with onMessage event of the application.
Whenever PostMessage is called, it fires onMessage event which is set to HandleMsg().


Here more of my code:
 procedure TDXCommDlg.HandleMsg(var
 
Msg: TMsg; var Handled: Boolean);
 
begin
     
Handled := false;
     
case Msg.message of
      WM_USER
+ 10:
             
begin
                   
if (Msg.wParam = 1111) and (Msg.lParam = 2222) then
                   
begin
                     
SendLanMessage;
                     
Handled := true;
                   
end
                   
else if (Msg.wParam = 1234) and (Msg.lParam = 5678) then
                   
begin
                       
SendMessage;
                       
Handled := true;
                   
end
                   
else
                   
begin
                     
if (Msg.wParam = 4321) then
                     
begin
                       
MainFrm.CloseWindow(TViewFrm(Msg.lParam).WinCap);
                     
end;
                   
end;
             
end;
     
end; { case } end;
HandleMsg() responds to PostMessage. Correct me if I am wrong.


 Answer:

In both cases (starting to size/move window or opening a menu) the last message dispatched from TApplication.ProcessMessage is WM_NCLBUTTONDOWN (or a WM_NCRBUTTONDOWN if caption and system menu exists and clicked on the caption.. or a WM_RBUTTONUP if opening a context menu, etc..). Common to all is they are starting a modal message loop.
For instance the below is from WM_ENTERSIZEMOVE documentation:
The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving or sizing modal loop. [....] The operation is complete when DefWindowProc returns.
After a modal message loop is started the HandleMessage call in TApplication.Run will not return until DefWindowProc for the relevant window returns (in the WM_NCLBUTTONDOWN case for instance, the dispatched message will cause a WM_SYSCOMMAND to be sent to the window which will start the modal message loop and will not return until moving/sizing is complete). So you won't be able to use an OnMessage handler of the application in this period which is called in TApplication.ProcessMessage.
Your solution is simple. Instead of using the OnMessage handler, handle the message with a message handler of your form:
const
  WMUSER_10
= WM_USER + 10;

type
 
TForm1 = class(TForm)
   
Timer1: TTimer;
    procedure
Timer1Timer(Sender: TObject);
 
private
    procedure
WmUser10(var Msg: TMsg); message WMUSER_10;
 
public
 
end;

procedure
TForm1.Timer1Timer(Sender: TObject);
begin
 
PostMessage(Handle, WMUSER_10, 1234, 5678);
end;

procedure
TForm1.WmUser10(var Msg: TMsg);
begin
 
//
end;

Or, put your code in the OnTimer event, since WM_TIMER is itself posted.

Delphi 2010 Control Flickering

I have been upgrading or migrating our software from XP OS to be able to compile and run under Windows 7. Our software is starting to show issues that we didn't notice under Windows XP. Currently, I am dealing with a user defined control flickering on a TForm. It seems to flicker every now and then not always, but when it flickers it is very noticeable. I have set DoubleBuffered for the TForm and TTrendChart Class, but it is not helping.

This a user-defined control of TCustomPanel. It is supposed to display a Live Trendchart on a TForm.

TTrendChart = class(TCustomPanel)
private
  fCount
:integer;
  fColors
:array[0..7] of TColor;
  fNames
:array[0..7] of string;
  fMinText
:string16;
  fMaxText
:string16;
  fShowNames
:Boolean;
  fMaxTextWidth
:integer;
  data
:TList;
 
Indexer:integer;
  chartRect
:TRect;
  fWidth
:integer;
  fHeight
:integer;
  firstTime
:Boolean;
 
function GetColors(Index:integer):TColor;
  procedure
SetColors(Index:integer; const value :TColor);
 
function GetNames(Index:integer):string;
  procedure
SetNames(Index:integer; const value: string);
  procedure
SetCount(const value : integer);
  procedure rShowNames
(const value : Boolean);
  procedure
SetMaxText(const value:string16);
  procedure
SetMinText(const value:string16);
  procedure
RecalcChartRect;
protected
  procedure
Resize; override;
  procedure
Paint; override;
public
  constructor
Create(AOwner : TComponent); override;
  destructor
Destroy; override;
  procedure
PlotPoints(p1,p2,p3,p4,p5,p6,p7,p8:real);
  procedure
ClearChart;
  procedure
Print;
  property
TrendColors[Index:integer]: TColor read GetColors write SetColors;
  property
TrendNames[index:integer]: string read GetNames write SetNames;
published
  property
TrendCount: Integer read fCount write SetCount default 8;
  property
ShowNames: Boolean read fShowNames write rShowNames default true;
  property
MaxText:string16 read fMaxText write SetMaxText;
  property
MinText:string16 read fMinText write SetMinText;
  property
Align;
  property
Alignment;
  property
BevelInner;
  property
BevelOuter;
  property
BevelWidth;
  property
DragCursor;
  property
DragMode;
  property
Enabled;
  property
Caption;
  property
Color;
  property
Ctl3D;
  property
Font;
  property
Locked;
  property
ParentColor;
  property
ParentCtl3D;
  property
ParentFont;
  property
ParentShowHint;
  property
PopupMenu;
  property
ShowHint;
  property
TabOrder;
  property
TabStop;
  property
Visible;

  property
OnClick;
  property
OnDblClick;
  property
OnDragDrop;
  property
OnDragOver;
  property
OnEndDrag;
  property
OnEnter;
  property
OnExit;
  property
OnMouseDown;
  property
OnMouseUp;
  property
OnMouseMove;
  property
OnResize;
end;
 
 
Here how it created:
    constructor TTrendChart.Create(AOwner:TComponent);
var
  i
:integer;
  tp
:TTrendPoints;
begin
  inherited
Create(AOwner);
 
Parent := TWinControl(AOwner);
  fCount
:= 8;
  fShowNames
:= true;
 
Caption := '';
  fMaxText
:= '100';
  fMinText
:= '0';
  fMaxTextWidth
:= Canvas.TextWidth('Bar 0');
  firstTime
:= true;
 
BevelInner := bvLowered;
  data
:= TList.Create;
 
Indexer := 0;
 
RecalcChartRect;
 
DoubleBuffered:=true;
 
for i := 0 to 10 do
 
begin
    tp
:= TTrendPoints.Create(0.0 + 0.1 * fWidth,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
    data
.Add(tp);
 
end;
 
for i := 0 to 7 do
 
begin
   
case i of
   
0: fColors[i] := clMaroon;
   
1: fColors[i] := clGreen;
   
2: fColors[i] := clOlive;
   
3: fColors[i] := clNavy;
   
4: fColors[i] := clPurple;
   
5: fColors[i] := clFuchsia;
   
6: fColors[i] := clLime;
   
7: fColors[i] := clBlue;
   
end;
    fNames
[i] := Format('Line %d',[i]);
 
end;
end;
Here is how it is painted on the Form:
    procedure TTrendChart.Paint;
var
  oldColor
:TColor;
  dataPt
:TTrendPoints;
  i
,j:integer;
  curx
:integer;
  count
,step:integer;
  r
:TRect;
begin
   inherited
Paint;

  oldcolor
:= Canvas.Pen.Color;

 
Canvas.Brush.Color:=clWhite;
  r
.Left:=chartRect.Left-25;
  r
.Right:=chartRect.Right+11;
  r
.Top:=chartRect.Top-11;
  r
.Bottom:=chartRect.Bottom+22;
 
Canvas.FillRect(r);

 
if FirstTime then
 
begin
    count
:= Indexer - 1;
 
end
 
else
    count
:= data.Count - 2;

   
{ Draw minute lines }
   
Canvas.Pen.Color := clBtnShadow;
    i
:= chartRect.left + 60;
   
while i < chartRect.Right do
   
begin
         
Canvas.Moveto(i, chartRect.top);
         
Canvas.LineTo(i, chartRect.bottom);
         i
:= i + 60;
   
end;

   
{ Draw value lines }

    step
:= (chartRect.bottom - chartRect.top) div 5;

   
if step > 0 then
   
begin
         i
:= chartRect.bottom - step;
         
while i > (chartRect.top + step - 1) do
         
begin
             
Canvas.Moveto(chartRect.left,i);
             
Canvas.LineTo(chartRect.right,i);
              i
:= i - step;
         
end;
   
end;

 
{ Draw Pens }
 
for j := 0 to fCount - 1 do
 
begin
   
Canvas.Pen.Color := fColors[j];
    dataPt
:= TTrendPoints(data.Items[0]);
   
Canvas.MoveTo(chartRect.left,PinValue(round(chartRect.bottom - (fHeight * dataPt.pnts[j] / 100.0)),
                                                                 chartRect
.top,chartRect.bottom));

   
for i := 1 to count do
   
begin
      dataPt
:= TTrendPoints(data.Items[i]);
     
if i <> Indexer then
     
begin
           
Canvas.LineTo(chartRect.left+i,PinValue(round(chartRect.bottom - (fHeight * dataPt.pnts[j] / 100.0)),
                                                               chartRect
.top,chartRect.bottom));
     
end
     
else
     
begin
           
Canvas.MoveTo(chartRect.left+i,PinValue(round(chartRect.bottom - (fHeight * dataPt.pnts[j] / 100.0)),
                                                                 chartRect
.top,chartRect.bottom));
     
end;
   
end;
 
end;

    r
:= chartRect;
   
InflateRect(r,1,1);
   
Canvas.Pen.Color := clBtnShadow;
   
Canvas.moveto(r.left,r.top);
   
Canvas.lineto(r.right,r.top);
   
Canvas.lineto(r.right,r.bottom);
   
Canvas.lineto(r.left,r.bottom);
   
Canvas.lineto(r.left,r.top);

   
{ draw index line }
//    Canvas.Pen.Color := clWhite;
   
Canvas.Pen.Color := clBlack;  
   
Canvas.MoveTo(chartRect.Left + Indexer,chartRect.top);
   
Canvas.LineTo(chartRect.left + Indexer, chartRect.bottom+1);
   
Canvas.Pen.Color := oldcolor;

   
Canvas.Font.COlor := clBlack;
   
Canvas.TextOut(chartRect.left-Canvas.TextWidth(string(fMinText))-2,chartRect.Bottom-8,string(fMinText));
   
Canvas.TextOut(chartRect.left-Canvas.TextWIdth(string(fMaxText))-2,chartRect.top-8,string(fMaxText));

   
if fShowNames then
   
begin
      curx
:= 32;
     
for i := 0 to fCount - 1 do
     
begin
       
Canvas.Font.Color := fColors[i];
       
Canvas.TextOut(curx,chartRect.bottom+4,fNames[i]);
        curx
:= curx +  fMaxTextWidth + 16;
     
end;
   
end;
end;
Here is how one would use it:
  TrendChart := TTrendChart.Create(form);
 

Any help will be appreciated. Thank you.


Answer:

ControlStyle := ControlStyle + [csOpaque];