Delphi local net app - delphi

Trying to learn how to make server-client apps and stuff like. I trying to draw circles(on mouse click) in all clients so this is how i trying to do that. But it's not working - no errors but form is empty. What i need to fix?
Client code
unit Client;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, {Figure, Ball,} IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, ScktComp;
type
TForm1 = class(TForm)
Timer1: TTimer;
Button1: TButton;
ClientSocket: TClientSocket;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
public
{ Public declarations }
end;
var
Form1: TForm1;
f:boolean;
p:MyPoint;
s:MyPoint;
z:TCanvas;
obj: MyFigure;
pX, pY:Integer;
myBuf: array[1..32] of Integer;
dataBuf: array[1..32] of Integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled:=false;
Timer1.Interval:=5;
z:=Form1.Canvas;//TCanvas.Create;
Button1.Caption:='Пуск';
f:=false;
ClientSocket.Port:=1234;
ClientSocket.Active:= False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if not f then
begin
Timer1.Enabled:=true;
Button1.Caption:='Стоп';
f:=not f;
end
else
begin
Timer1.Enabled:=false;
Button1.Caption:='Пуск';
ClientSocket.Active:= True;
f:=not f;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
//z.Lock;
//z.Brush.Color:=ClWhite;
//z.FillRect(Canvas.ClipRect);
//obj.Draw(z);
if ClientSocket.Active then
ClientSocket.Socket.ReceiveBuf(dataBuf, 32);
z.Brush.Color:=ClRed;
z.Ellipse(dataBuf[1] + 10, dataBuf[2] + 10,dataBuf[1] - 10, dataBuf[2] - 10);
//z.Unlock;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ClientSocket.Active := false;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
myBuf[1]:=X;
myBuf[2]:=Y;
if ClientSocket.Active then
ClientSocket.Socket.SendBuf(myBuf, 32);
end;
end.
Server
unit ServerProject;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;
type
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocket1ClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
sBufer : array [1..32] of Integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
ServerSocket1.Port:=1234;
ServerSocket1.Active := True;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ServerSocket1.Active := false;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do
begin
with ServerSocket1.Socket.Connections[i] do
begin
ReceiveBuf(sBufer, 32);
end;
end;
end;
procedure TForm1.ServerSocket1ClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do
begin
with ServerSocket1.Socket.Connections[i] do
begin
SendBuf(sBufer, 32);
end;
end;
end;
end.

Your painting code is in the wrong place and is painting to the wrong thing. In Windows programs you are meant to paint in response to a WM_PAINT message. You are not doing so. What's more, you have to paint on a device context that is provided by a call to BeginPaint.
The VCL wraps all those details up for you, but you still need to follow the rules. In your case I recommend that you add a TPaintBox component to your form. Then implement an OnPaint event handler for the paint box. Finally, whenever you wish to repaint the paint box, for example on a timer, call the Invalidate method of the paint box.
I suspect that you want your each new ellipse to be drawn in addition to the earlier drawn ellipses. In which case you are probably best served by drawing them to an off-screen bitmap first and then, when you come to paint to the paint box, draw that bitmap on the paint box. The point is that a window needs to be able to re-paint itself in its entirety. When you paint to a screen device, what you painted is lost the next time that window needs to be painted. So it's the responsibility of the application to be able to paint its entire self at any point, if it is asked.
More generally I urge you to stop using global variables. They will cause you no end of trouble. Prefer local variables wherever possible. If you need state to persist between different method calls, use member variables. The guiding principle is to use the narrowest scope possible.
Your current design uses a timer to poll for new data. That's a very poor approach. The most efficient and effective approach is to use synchronous blocking communication. Indy takes that approach. Windows sockets components instead tend to be used in an asynchronous mode. Irrespective of the relative merits of these two approaches, you should not be polling on a timer. If you do use asynchronous communication, then respond to new data by handling an event rather than polling.
Your program is currently trying to mix together GDI painting, and network communication. I suggest that you attempt to get on top of these concepts one at a time. Learn how to paint without the distraction of communication. Then when you have cracked painting, try to bring in the communication aspect.

Related

Delphi 5 a Simple Example for Tserversocket and Tclientsocket

provide an example in DELPHI 5 if is possible
lets say we have the follow code.A simple tclientsocket communicates with a tserver socket.
Everything works fine if requests from tclientsocket are coming after the process of data on event tserversocket1onReadClient.
But How i will bypass the problem when on the middle of process i am getting a new Request from socketclient1 and i havent finish yet my process?
do i have to Implement it in ServerType: stThreadBlocking
Do i have to Create a thread to Do the process?
is it a better way do this simple.
My tclientsocket string messages will not be larger than 255 chars.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ScktComp;
type
TForm1 = class(TForm)
ClientSocket1: TClientSocket;
Button1: TButton;
ServerSocket1: TServerSocket;
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure ServerSocket1ClientError(Sender: TObject;Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;var ErrorCode: Integer);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
with Serversocket1 do
begin
Active:=false;
ServerType:=stNonBlocking;
port:=5052;
Active:=true;
end;
with ClientSocket1 do
begin
active:=false;
port:=5052;
Address:='127.0.0.1';
host:='127.0.0.1';
active:=true;
end;
end;
procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
errorcode:=0;
end;
procedure TForm1.ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
errorcode:=0;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);
var mydata:String;
begin
mydata:=socket.ReceiveText;
///Proceccing my data now
//Line 1
//Line 2
//Line 3
//Line 4 <---- ie. when i am proccessing line 4 a new Request from clientsocket1 arrives
//Line 5
end;
end.
The scenario you describe cannot happen. When the ServerSocket1ClientRead event handler is executing, it will not be called in a re-entrant fashion. Well, unless you were to call Application.ProcessMessages. So, don't do that!

How to replace a TDBNavigator with a TSpeedButton?

I did:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
DataTable.qOrders.Next;
end;
It works, but the problem is when I click the button to reach the last record, the button is not disabled, like in a TDBNavigator.
How did I make the TSpeedButton disable and enable automatically like the TDBNavigator does?
Drop a TActionList onto your form and add the standard dataset actions to it. Connect these actions to your dataset and your speedbuttons to the appropriate actions. These standard actions will handle the enable state according to the current dataset state.
Here is a simple solution, that works perfectly for me.
I have a form (frmMain), dataset (dsWork), datasource (srcWork), grid and two speedbuttons (btnNext and btnPrior). The important part is in "OnDataChange" event of TDataSource. Here is the source code:
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, DBTables, StdCtrls, ExtCtrls;
type
TfrmMain = class(TForm)
btnNext: TButton;
srcWork: TDataSource;
dsWork: TTable;
btnPrior: TButton;
grdWork: TDBGrid;
procedure btnNextClick(Sender: TObject);
procedure btnPriorClick(Sender: TObject);
procedure srcWorkDataChange(Sender: TObject; Field: TField);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnNextClick(Sender: TObject);
begin
if not dsWork.Eof then dsWork.Next;
end;
procedure TfrmMain.btnPriorClick(Sender: TObject);
begin
if not dsWork.Bof then dsWork.Prior;
end;
procedure TfrmMain.srcWorkDataChange(Sender: TObject; Field: TField);
begin
btnNext.Enabled := not dsWork.Eof;
btnPrior.Enabled := not dsWork.Bof;
end;
end.

How create a "shadow effect" similar to " shutting down Windows"? [duplicate]

I am looking to create an effect similar to the lightbox effect seen on many website where the background of the screen fades out and the content you want to emphasize does not. What would be the best way to go about creating such an effect in delphi ?
The content I want to emphasize in this case is a movable panel located on my form and basically all I want to do is to fade out any area of the screen that is not directly under that panel.
Thanks.
Oscar
Create a new form and add this code to the FormCreate method. You could also change the properties using the properties inspector, but I'm choosing to show you the relevant properties using code:
unit Unit1;
// This is a full screen partially transparent black form.
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
Self.WindowState := wsMaximized;
AlphaBlend := true;
Alphablendvalue := 127;
Color := clBlack;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Form2.Show;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
Close;
end;
end.
Here's a second form which has no border, which I am showing over top. It does not have alpha blending turned on, and the form style should be fsStayOnTop, or else you should use the ParentWindow property (on versions of Delphi that support that).
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Label1: TLabel;
procedure FormDeactivate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FAutoDeactivate: Boolean;
FCounter: Integer;
procedure WMUser1(var Message:TMessage); message WM_USER+1;
public
property AutoDeactivate:Boolean read FAutoDeactivate write FAutoDeactivate;
end;
var
Form2: TForm2;
implementation
uses Unit1;
{$R *.dfm}
procedure TForm2.FormDeactivate(Sender: TObject);
begin
if Self.Visible and FAutoDeactivate then
begin
FAutoDeactivate := false;
Form1.Close;
end;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Form1.Close;
end;
procedure TForm2.FormActivate(Sender: TObject);
begin
PostMessage(Self.Handle, WM_USER+1, 0, 0);
end;
procedure TForm2.WMUser1(var Message: TMessage);
begin
FAutoDeactivate := true;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
Color := clWhite;
FormStyle := fsStayOnTop; // or set parent
end;
end.
That addresses how to make the whole screen "go dim", and then show something on top of that "dimmed area", but what you describe as "showing a panel in your main form" would require you to move that content out of your main form, or else clip a region out of form1, or use a combination of alpha blend plus transparency, but I don't have any code for those to show you.
If I was doing it, I would just float the thing I want not to be dimmed, above the full screen borderless 50% alpha form, as shown below.
But as you see, the screen isn't dimmed (screen brightness is not reduced), it's merely that we've done a 50% transparent layer of black which has blended in and darkened the overall screen appearance.
I have the same need as Oscar. After some search on the net, I found what is shown here.
It has helped me to do this, since it works. You can move what is emphasized in a Form instead of a Panel.
I use two forms. The first is use as "fader" and the second as dialogbox.
First
unit uFormFaded;
interface
uses
...
type
TFormFaded = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
FormFaded: TFormFaded;
implementation
{$R *.dfm}
procedure TFormFaded.FormCreate(Sender: TObject);
begin
Align := alClient;
AlphaBlend := true;
AlphaBlendValue := 100;
BorderStyle := bsNone;
Color := clBlack;
Enabled := false;
FormStyle := fsStayOnTop;
end;
end.
Second
unit UFormDlgBox;
interface
uses
...
type
TFormDlgBox = class(TForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
FormDlgBox: TFormDlgBox;
implementation
{$R *.dfm}
uses uFormFaded;
procedure TFormDlgBox.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FormFaded.Close;
end;
procedure TFormDlgBox.FormShow(Sender: TObject);
begin
FormFaded.Show;
end;
end.
The use
FormDlgBox.ShowModal;
I tried to reproduce this schema creating the forms in run-time an make the TFormDlgBox Owns and create the TFormFaded but it doesn't work. It seems it works only with forms created in design-time.

how to calculate the Framerate and Timecode during screen capturing?

Delphi 6 project
I have searched google pretty thorough but am not finding the answers to my delima. basically i want to have the timecode and videos framerate of a current screen capture session showing in my app, in the statusbar or label. i also need this with respect to syncing the captures to the framerate of the sofware player playing the video, otherwise i get a lot of duplicate or missed frames. the videos are 29.970 and 23.976 fps. So i need to be able to configure for both, somehow.
Currently, I can screen capture from tv cards and software video players like, vlc, ffplay, mplayer, virtualdub, etc.
i'm not sure how to implement the necessary routines into mine, let alone where. i've been reading a lot about the following items below but they are all over my head though i did make many attempts at it:
timer1 control -- setting interval to 34 is not exact, it duplicates or misses frames during screen capture
gettimetick and timegettime
timeBeginPeriod and timeEndPeriod
QueryPerformanceTimer and QueryPerformanceCounter
To help simiplfy the process, i snipped a lot of code of the original project to only feature the screen capturing. Here is the complete routine (along with some remarked-out experimental code) for this:
(thanks in advanced for any help)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, mmsystem,
ExtCtrls, clipbrd, DXClass;
type
TForm1 = class(TForm)
Timer1: TTimer;
Panel1: TPanel;
m1: TMemo;
btnCapOnOff: TButton;
txtHandle: TEdit;
Edit2: TEdit;
stDataRate: TStaticText;
btnCopy: TButton;
btnSetHDC: TButton;
dxt1: TDXTimer;
sb1: TScrollBox;
Splitter1: TSplitter;
im1: TImage;
procedure btnCapOnOffClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure capturewindow;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnCopyClick(Sender: TObject);
procedure btnSetHDCClick(Sender: TObject);
procedure dxt1Timer(Sender: TObject; LagCount: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
start,
finish : cardinal; //int64;
i : integer;
s : string;
bm: tbitmap;
dc: hdc=0;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
form1.DoubleBuffered:=true;
sb1.DoubleBuffered:=true; // this is a scrollbox control
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
im1.Picture.Bitmap.PixelFormat:=pf24bit;
im1.Width:=352;
im1.Height:=240;
end;
procedure TForm1.btnSetHDCClick(Sender: TObject);
begin
if dc=0 then dc := getdc(strToint(txtHandle.text));
end;
procedure TForm1.capturewindow;
begin
//timeBeginPeriod(1);
start := timegettime;
//sleep(1);
bitblt(bm.canvas.Handle, 0,0, 352,240, dc, 0,0, srccopy);
finish := timegettime-start;
//m1.lines.Add(intTostr(finish)); // debugging: to spill out timing values, etc.
im1.Picture.Bitmap := bm;
stDataRate.Caption := 'Date Rate: '+intTostr(finish) + ' fps or ms';
end;
procedure TForm1.dxt1Timer(Sender: TObject; LagCount: Integer);
begin
capturewindow;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
// capturewindow; // timer1 is too slow or unpredictable
end;
// button: a cheeters way to turn On or Off capturing
procedure TForm1.btnCapOnOffClick(Sender: TObject);
begin
if btnCapOnOff.caption='Cap is Off' then begin
btnCapOnOff.caption:='Cap is On';
//timer1.Enabled:=true; // capture the window // too slow
dxt1.Enabled:=true; // capture the window // a better timer control component (delphiX)
end else begin
btnCapOnOff.Caption:='Cap is Off';
//timer1.Enabled:=false; // too slow
dxt1.Enabled:=false; // stop capturing the window // a better timer control component (delphiX)
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
bm.free;
releaseDC(dc,dc);
//timeEndPeriod(1);
end;
procedure TForm1.btnCopyClick(Sender: TObject);
begin
clipboard.assign(im1.picture.bitmap); // to take quick pics
end;
initialization
bm := tbitmap.Create;
bm.PixelFormat:=pf24bit;
bm.Width:=352;
bm.Height:=240; beep;
end.
Actually hooking the software that's playing the video, and synchronizing to it, I'm not sure how to do that. But working on timing might help out. Assuming that the software playing the video is also well timed, you should be able to get a smooth capture.
This tutorial is useful: http://www.codeproject.com/Articles/1236/Timers-Tutorial
The "Multimedia Timers" offer good resolution (down to 1ms on most machines), and I've found them to be reliable.
What I would try is using the Performance Timer (queryperformancetimer, as you've already mentioned) to time your "CaptureWindow" procedure. Then, when you call "timesetevent" in a multimedia timer, subtract the amount of time the capture took from the overall time of a single frame, and use that as your "uDelay" value.
HowLongTimerShouldWait := LengthOfASingleFrame - TimeSpentCapturingPreviousFrame
The nice thing about the Multimedia Timers is that they let you use it as a 'one shot', where each interval can have a different delay period. I've generally set the timer to call a single procedure recursively, until it's flagged to stop.
This way, with a bit of fine tuning, you should be able to get capture rates are are within a +/-1ms tolerance of the actual video FPS.
As promised, here is the code i came up with based on some google searches and working them out in delphi. the following links did help me out some though (but due to c/c++/c# i could not translate as easily to delphi) so most of the final answer were based on lots of trial and error:
http://www.andrewduncan.ws/Timecodes/Timecodes.html
http://puredata.hurleur.com/sujet-990-framenumber-timecode-conversion
To my knowledge the routine works flawless. but just so you know, i like my numbers formated for spacing purposes, so i padded to 2-digits, this way there is not shriking back and forth as numbers progress past 59.
Heres how it works:
It computes the timecode based on the frame rate of your video source (ie 29.970 interlace or progressive, and 23.976 for 24p film) .. so just feed it a frame number and the function will return the timecode in string format.
Example Preporation/Useage:
put two Tedit's and one Tbutton control on your form1
in button1 onClick event, enter this: edit2.text := frameNo2timecode(strToint(edit1.text), 29.970);
now, run the program and enter your frame number in the first edit1.text
then, press the button1 control, it will compute the timecode in edit2.text
source code to calculate timecode:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, math;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function FrameNo2Timecode(fn: longint; rate: real): string;
var
hours,mins,secs,milli: extended;
hoursStr, minsStr, secsStr, milliStr: string;
function padzero(N: longint; Len: Integer): string;
begin
FmtStr(Result, '%d', [N]);
while Length(Result) < Len do
Result := '0' + Result;
end;
begin
hours := floor( (fn/rate)/3600) mod 60;
hoursStr := padzero(floor(hours),2);
mins := floor( (fn/rate)/60.0) mod 60;
minsstr := padzero(floor(mins),2);
secs := floor( (fn/rate)) mod 60;
secsstr := padzero(floor(secs),2);
milli := floor( (1000*fn/rate)) mod 6000 mod 1000;
millistr := padzero(floor(milli),3);
result := hoursStr +':'+ minsStr +':'+ secsStr +'.'+ milliStr;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
edit2.text := frameNo2timecode(strToint(edit1.text), 29.970);
end;
end.

DBGrid scroll page instead of row

Well I have an issue with DBGrid vertical scrolling. When I scroll it vertically with mousewheel or vertical scrollbar it moves selected row up and down. I want to make it scroll not selected row but entire grid. Just like it works in Microsoft Excel for example (just to let you know what I mean). Any suggestions?
Well, almost what I'd like to see. Found the post of hanuleye on swissdelhicenter.ch. This code let's you freely scroll DBGrid with mouse wheel.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, DBTables;
type
TForm1 = class(TForm)
DataSource1: TDataSource;
Table1: TTable;
DBGrid1: TDBGrid;
procedure FormCreate(Sender: TObject);
procedure DBGridMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
TWheelDBGrid = class(TDBGrid)
public
property OnMouseWheel;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TWheelDBGrid(DBGrid1).OnMouseWheel := DBGridMouseWheel;
end;
function GetNumScrollLines: Integer;
begin
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, #Result, 0);
end;
procedure TForm1.DBGridMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
Direction: Shortint;
begin
Direction := 1;
if WheelDelta = 0 then
Exit
else if WheelDelta > 0 then
Direction := -1;
with TDBGrid(Sender) do
begin
if Assigned(DataSource) and Assigned(DataSource.DataSet) then
DataSource.DataSet.MoveBy(Direction * GetNumScrollLines);
Invalidate;
end;
end;
end.
I don't think that's possible, since to me it seems that scrollbar on DBGrids are more like a progress indicator rather than a scroll. It behaves differently from the scrolls in ListViews where you scroll "pages", in the db controls even if you move up or down a single row the scrollbar changes to reflect the "current row"/"total rows" fraction

Resources