2010. május 31., hétfő
Display a sort order indicator in the column header of a TListView
Problem/Question/Abstract:
How can I display a sort order arrow on the tiltle row in TListView control (on the right side of the column caption)?
Answer:
The easiest way would be to add the arrow picture to the imagelist, assign it to the listview's smallimages property and specify the image index to the column (ImageIndex property of the TListColumn). Now you'll see the picture on the left side of the column header.
Another approach would be to draw the header by yourself. In case you're working with the standard (not overridden)TListView control, you can set the new window proc to the header in the form's OnCreate event. In the new header's window procedure you can check up if the WM_PAINT message is coming and perform custom drawing for the header or its part. See the example below for details:
{ ... }
type
TForm1 = class(TForm)
ListView1: TListView;
{ ... }
protected
FHeader: longint;
FOldWndProc: pointer;
procedure HeaderWndProc(var Message: TMessage);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FHeader := ListView_GetHeader(ListView1.Handle);
FOldWndProc := Pointer(GetWindowLong(FHeader, GWL_WNDPROC));
SetWindowLong(FHeader, GWL_WNDPROC,
integer(Classes.MakeObjectInstance(HeaderWndProc)));
end;
procedure TForm1.HeaderWndProc(var Message: TMessage);
var
XCanvas: TCanvas;
XDC: HDC;
XSizeRect: TRect;
begin
if Assigned(FOldWndProc) then
Message.Result := CallWindowProc(FOldWndProc, FHeader, Message.Msg,
Message.WParam, Message.LParam);
case Message.Msg of
WM_PAINT:
begin
XCanvas := TCanvas.Create;
XDC := GetWindowDC(FHeader);
try
XCanvas.Handle := XDC;
Windows.GetClientRect(FHeader, XSizeRect);
XCanvas.Brush.Color := clRed;
XCanvas.FillRect(XSizeRect);
{draw the new header's content here...}
finally
ReleaseDC(FHeader, XDC);
XCanvas.Free;
end;
end;
end;
end;
2010. május 30., vasárnap
TScreen, TApplication used in a DLL
Problem/Question/Abstract:
TScreen, TApplication used in a DLL
Answer:
Each DLL in Delphi maintains its own instance of Application & Screen, your DLL-calling application should send the its own Application and Screen values to the DLL. The DLL should save and restore its original values.
You should put this code somewhere in your DLL and call the Init() function from your application:
const
SavedApplication: TApplication = nil;
SavedScreen: TScreen = nil;
// export this procedure and call it after loading the DLL
procedure Init(anApplicationHandle, aScreenHandle: LongWord);
begin
if not Assigned(SavedApplication) then
begin
SavedApplication := Application;
Application := TApplication(anApplicationHandle);
end;
if not Assigned(SavedScreen) then
begin
// ....same...
end;
end;
initialization
finalization
if Assigned(SavedApplication) then
begin
Application := SavedApplication;
end;
if Assigned(SavedScreen) then
begin
// ....same.....
end;
end.
2010. május 29., szombat
How to determine the absolute location of a control
Problem/Question/Abstract:
Is there a built-in method for getting the absolute location of a control or do I need to step through the hierarchy? E.g.: Form1...Group1...Button1 means that the absolute left of Button1 is Form1.Left+Group1.Left+Button1.Left
Answer:
Solve 1:
You need to use the ClientToScreen and ScreenToClient methods, like this:
procedure TForm1.Button1Click(Sender: TObject);
var
P: TPoint;
begin
P := Point(Button1.Left, Button1.Top);
{Button1's coordinates are expressed relative to it's parent. Using Parent.ClientToScreen converts these client coordinates to screen coordinates, which are absolute, not relative.}
P := Button1.Parent.ClientToScreen(P);
{Using ScreenToClient here is the same as Self.ScreenToClient. Since Self is the current instance of TForm1, this statement converts the absolute screen coordinates back to coordinates relative to Self.}
P := ScreenToClient(P);
ShowMessage(Format('x: %d, y: %d', [P.X, P.Y]));
end;
Because this code uses the absolute screen coordinates in the conversion process, it will work regardless of how deeply nested the Button is. It could be on the form, on a group on the form, on a panel in a group on the form... it doesn't matter. The code will always return the same results, the coordinates expressed in the form's client system.
Solve 2:
I don't know if there is a simpler method, but this one works:
function GetScreenCoordinates(AControl: TControl): TPoint;
begin
if AControl.Parent <> nil then
begin
Result := AControl.Parent.ClientToScreen(Point(AContol.Left, AControl.Top));
end
else
begin
Result := Point(AContol.Left, AControl.Top);
end;
end;
The trick is: If a control has no parent, (Left, Top) should be the screen coordinates already (TForm). If it has a parent, the ClientToScreen function of the parent can be used to get it.
Solve 3:
Use TComponent.DesignInfo, which holds the Left and Top of the component. You can do this:
X := LongRec(MyComponent.DesignInfo).Lo;
Y := LongRec(MyComponent.DesignInfo).Hi;
2010. május 28., péntek
How to synchronize the scrolling of three TScrollBoxes when only one shows a scrollbar
Problem/Question/Abstract:
I need to synchronize three scrollboxes, only one of which will show the scrollbars. The documentation for TControlScrollBar reads: "If Visible is set to False, the scroll bar is never visible. This is useful, for example, for programmatically controlling the scroll position of a form without allowing the user to control the scroll position." I have been unable to make the scrollbox scroll when the scrollbar is visible. In fact, the moment you set the scrollbar to invisible, the position jumps back to 0.
Answer:
I looked at the VCL source for TScrollbox and TControlScrollbar and found the source of the problem: the TControlscrollbar class has an internal field named FCalcRange. If you try to set the scrollbar position the passed position is clipped to the range 0..FCalcRange. The only problem is that FCalcRange is set to 0 when the scrollbar is set to invisible, so Position will always be set to 0, regardless of what you try to set it to. I see no way around that, so you need to use a different strategy: instead of using the invisible scrollbars for the two slave scrollboxes scroll them directly, using ScrollBy.
The following example uses three scrollboxes of same size and scroll ranges. AutoScroll and Autosize are false for all, the first two have invisible scrollbars, the last has visible scrollbars and controls the other two. Each scrollbox has an edit in it so there is something visible to scroll around.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ScrollBox1: TScrollBox;
Edit1: TEdit;
ScrollBox2: TScrollBox;
Edit2: TEdit;
ScrollBox3: TScrollBox;
Edit3: TEdit;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FOldProc: TWndMethod;
procedure NewProc(var msg: TMessage);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
FoldProc := Scrollbox3.WindowProc;
Scrollbox3.WindowProc := NewProc;
end;
procedure TForm1.NewProc(var msg: TMessage);
var
oldpos, newpos: Integer;
begin
case msg.Msg of
WM_VSCROLL:
begin
oldpos := scrollbox3.VertScrollBar.Position;
FoldProc(msg);
newpos := scrollbox3.VertScrollBar.Position;
if oldpos <> newpos then
begin
scrollbox1.ScrollBy(0, oldpos - newpos);
scrollbox2.ScrollBy(0, oldpos - newpos);
end;
end;
WM_HSCROLL:
begin
oldpos := scrollbox3.HorzScrollBar.Position;
FoldProc(msg);
newpos := scrollbox3.HorzScrollBar.Position;
if oldpos <> newpos then
begin
scrollbox1.ScrollBy(oldpos - newpos, 0);
scrollbox2.ScrollBy(oldpos - newpos, 0);
end;
end
else
FoldProc(msg);
end;
end;
end.
2010. május 27., csütörtök
Add proxy authorization support to the TNMHTTP component
Problem/Question/Abstract:
The NMHTTP component is a nice and simple way to retrieve URLs within your application using http. Although the component supports a proxy, it does not support proxy authentication (proxy username/password). This method solves this drawback.
Answer:
{
To add proxy authentication support you must:
1. Have a proxy username and password (strings)
2. Merge these strings with a ':' between as:
totalString := UserName + ':' + PassWord
3. Base-64 encode totalString
4. On the OnAboutToSend event of the NMHTTP, add
'Proxy-authorization: ' + totalString
to the http header
The routine below encodes the Proxy username/password
to a string accepted by the proxy
}
uses Forms, Classes, NMUUE; // Don't forget these !
function EncodeAuth(username, password: string): string;
var
uu: TNMUUProcessor;
si, so: TStringStream;
decoded: string;
encoded: string;
begin
decoded := username + ':' + password; // Username:Password
SetLength(encoded, 20 * length(decoded)); // Estimate len
uu := TNMUUProcessor.Create(Application); // UU Processor
si := TStringStream.Create(decoded); // Input
so := TStringStream.Create(encoded); // Output
uu.InputStream := si;
uu.OutputStream := so;
uu.Method := uuMime;
uu.Encode; // Decode
result := so.ReadString(255); // Read Result
result := copy(result, 1, pos(#13, result) - 1); // No CRLF
si.free; // Free objects
so.free;
uu.free;
end;
{
The OnAboutToSend event on the NMHTTP should look like:
}
procedure TForm1.NMHTTP1AboutToSend(Sender: TObject);
begin
if username <> '' then
NMHTTP1.SendHeader.Insert(2, 'Proxy-authorization: ' +
EncodeAuth(username, password));
end;
{
We are inserting the Proxy-authorization token
to the 3rd position as it is a valid position to
place it
}
2010. május 26., szerda
DLLs: Import dynamic or static
Problem/Question/Abstract:
DLLs: Import dynamic or static
Answer:
Both techniques have their advantages. Static importing means you define functions like this:
function f: integer; external 'mydll.dll';
The advantage is that it is easy - it does not require ugly code. The application will only start up if all DLLs are present and can be loaded. All functions across all modules are bound during startup time.
Dynamic importing has its advantages as well.
it basically gives you full control over the usage of the DLL. If your DLL is only needed rarely for a seldomly used function, you may not want to load it at startup of your application. Your application will start faster then.
if the application will work without that DLL to a usable extent, you may allow the user to do so.
if you have different DLLs for different environments, for example one for Windows NT, another one for Windows 95 etc, then you MUST bind dynamic - you determine the operating system and load the DLL that you want.
dynamic binding allows you to release the DLL if you do not need it anymore
The following Article shows you how to do it: Dynamic loading and binding of DLLs
2010. május 25., kedd
How to limit the number of characters per line and the number of lines in a TMemo
Problem/Question/Abstract:
Is there any way to control the amount of characters per line in a TMemo component, e.g. that I can only store 7 lines of 50 chars each. The MaxLength property does not help in this case as it controls the total number of characters in the control.
Answer:
Limiting a memo to 6 lines of input:
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
var
line: Integer;
begin
if key = #13 then
begin
with Sender as TMemo do
begin
if lines.count >= 6 then
begin
key := #0;
line := Perform(EM_LINEFROMCHAR, SelStart, 0);
if line < 5 then
SelStart := Perform(EM_LINEINDEX, line + 1, 0);
end;
end;
end;
end;
Limiting a memo to 5 lines of input of max. 25 characters each:
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
var
line, col: Integer;
begin
with Sender as TMemo do
begin
line := Perform(EM_LINEFROMCHAR, SelStart, 0);
col := SelStart - Perform(EM_LINEINDEX, line, 0);
if key = #8 then
begin
{ Do not allow backspace if caret is on first column and deleting the
linebreak of the line in front would result in a line of more than 25
characters. Inconvenient for the user but specs are specs... }
if (col = 0) and (line > 0) then
begin
if (Length(lines[line]) + Length(lines[line - 1])) > 25 then
Key := #0;
end;
end
else if key in [#13, #10] then
begin
{ Handle hard linebreaks via Enter or Ctrl-Enter }
if lines.count >= 5 then
begin
{ Max number of lines reached or exceeded, set caret to start of next
line or this line, if on the last }
key := #0;
if line = 4 then
SelStart := Perform(EM_LINEINDEX, line, 0)
else
SelStart := Perform(EM_LINEINDEX, line + 1, 0);
end;
end
else if Key >= ' ' then
begin
{ Do swallow key if current line has reached limit. }
if Length(lines[line]) >= 25 then
Key := #0;
end;
end;
if Key = #0 then
Beep;
end;
procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
line, col: Integer;
begin
if Key = VK_DELETE then
with Sender as TMemo do
begin
line := Perform(EM_LINEFROMCHAR, SelStart, 0);
col := SelStart - Perform(EM_LINEINDEX, line, 0);
if col = Length(lines[line]) then
if (line < 4) and ((Length(lines[line]) + Length(lines[line + 1])) > 25) then
begin
key := 0;
Beep
end;
end;
end;
2010. május 24., hétfő
Create a TTable at runtime
Problem/Question/Abstract:
How to create a TTable at runtime
Answer:
Solve 1:
Delphi allows rapid addition and configuration of database elements to a Delphi project within the design environment, but there are situations where information needed to create and configure objects is not known at design time. For instance, you may want to add the ability to add columns of calculated values (using formulas of the users own creation) to an application at runtime. So without the benefit of the design environment, Object Inspector, and TFields editor, how do you create and configure TFields and other data related components programmatically?
The following example demonstrates dynamically creating a TTable, a database table based off the TTable, TFieldDefs, TFields, calculated fields, and attaches an event handler to the OnCalc event.
To begin, select New Application from the File menu. The entire project will be built on a blank form, with all other components created on-the-fly.
In the interface section of your forms unit, add an OnCalcFields\ event handler, and a TaxAmount field to the form declaration, as shown below. Later we will create a TTable and hook this handler to the TTable's OnCalcFields event so that each record read fires the OnCalcFields event and in turn executes our TaxAmountCalc procedure.
type
TForm1 = class(TForm)
procedure TaxAmountCalc(DataSet: TDataset);
private
TaxAmount: TFloatField;
end;
in the implementation section add the OnCalc event handler as shown below.
procedure TForm1.TaxAmountCalc(DataSet: TDataset);
begin
Dataset['TaxAmount'] := Dataset['ItemsTotal'] * (Dataset['TaxRate'] / 100);
end;
Create a OnCreate event handler for the form as shown below(for more information on working with event handlers see the Delphi Users Guide, Chapter 4 "Working with Code").
procedure TForm1.FormCreate(Sender: TObject);
var
MyTable: TTable;
MyDataSource: TDataSource;
MyGrid: TDBGrid;
begin
{Create the TTable component - the underlying database table is created later}
MyTable := TTable.Create(Self);
with MyTable do
begin
{Specify an underlying database and table. Note: Test.DB doesn't exist yet}
DatabaseName := 'DBDemos';
TableName := 'Test.DB';
{Assign TaxAmountCalc as the event handler to use when the OnCalcFields
event fires for MyTable}
OnCalcFields := TaxAmountCalc;
{Create and add field definitions to the TTable's FieldDefs array, then create
a TField using the field definition information}
with FieldDefs do
begin
Add('ItemsTotal', ftCurrency, 0, false);
FieldDefs[0].CreateField(MyTable);
Add('TaxRate', ftFloat, 0, false);
FieldDefs[1].CreateField(MyTable);
TFloatField(Fields[1]).DisplayFormat := '##.0%';
{Create a calculated TField, assign properties, and add to MyTable's
field definitions array}
TaxAmount := TFloatField.Create(MyTable);
with TaxAmount do
begin
FieldName := 'TaxAmount';
Calculated := True;
Currency := True;
DataSet := MyTable;
Name := MyTable.Name + FieldName;
MyTable.FieldDefs.Add(Name, ftFloat, 0, false);
end;
end;
{Create the new database table using MyTable as a basis}
MyTable.CreateTable;
end;
{Create a TDataSource component and assign to MyTable}
MyDataSource := TDataSource.Create(Self);
MyDataSource.DataSet := MyTable;
{Create a data aware grid, display on the form, and assign MyDataSource to
access MyTable's data}
MyGrid := TDBGrid.Create(Self);
with MyGrid do
begin
Parent := Self;
Align := alClient;
DataSource := MyDataSource;
end;
{Start your engines!}
MyTable.Active := True;
Caption := 'New table ' + MyTable.TableName;
end;
The following is the full source for the project.
unit gridcalc;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, ExtCtrls, DBCtrls, DB, DBTables, StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure TaxAmountCalc(DataSet: TDataset);
private
TaxAmount: TFloatField;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.TaxAmountCalc(DataSet: TDataset);
begin
Dataset['TaxAmount'] := Dataset['ItemsTotal'] * (Dataset['TaxRate'] / 100);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
MyTable: TTable;
MyDataSource: TDataSource;
MyGrid: TDBGrid;
begin
MyTable := TTable.Create(Self);
with MyTable do
begin
DatabaseName := 'DBDemos';
TableName := 'Test.DB';
OnCalcFields := TaxAmountCalc;
with FieldDefs do
begin
Add('ItemsTotal', ftCurrency, 0, false);
FieldDefs[0].CreateField(MyTable);
Add('TaxRate', ftFloat, 0, false);
FieldDefs[1].CreateField(MyTable);
TFloatField(Fields[1]).DisplayFormat := '##.0%';
TaxAmount := TFloatField.Create(MyTable);
with TaxAmount do
begin
FieldName := 'TaxAmount';
Calculated := True;
Currency := True;
DataSet := MyTable;
Name := MyTable.Name + FieldName;
MyTable.FieldDefs.Add(Name, ftFloat, 0, false);
end;
end;
MyTable.CreateTable;
end;
MyDataSource := TDataSource.Create(Self);
MyDataSource.DataSet := MyTable;
MyGrid := TDBGrid.Create(Self);
with MyGrid do
begin
Parent := Self;
Align := alClient;
DataSource := MyDataSource;
end;
MyTable.Active := True;
Caption := 'New table ' + MyTable.TableName;
end;
end.
Solve 2:
procedure TForm1.FormCreate(Sender: TObject);
begin
MyTable := TTable.Create(Self);
with MyTable do
begin
Active := False;
DatabaseName := 'c:\temp';
TableName := 'Test.DB';
if not FileExists(DatabaseName + '\' + TableName) then
begin
with FieldDefs do
begin
Clear;
Add('InputNr', ftAutoInc, 0, false);
Add('SName', ftString, 35, false);
Add('name', ftString, 35, false);
end;
with IndexDefs do
begin
Clear;
Add('InputNr', 'InputNr', [ixPrimary]);
Add('SName', 'SName', []);
end;
CreateTable;
end;
end;
DataSource1.DataSet := MyTable;
MyTable.Open;
MyTable.FieldByName('SName').visible := false;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
with OKBottomDlg do
begin
Edit1.text := '';
ShowModal;
if ModalResult = mrOK then
begin
MyTable.Append;
MyTable.SetFields([nil, AnsiUppercase(Edit1.text), Edit1.text]);
MyTable.Post;
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
MyTable.IndexFieldNames := 'sname';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
MyTable.IndexFieldNames := 'InputNr';
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
with OKBottomDlg do
begin
Edit1.text := MyTable.FieldValues['name'];
ShowModal;
if ModalResult = mrOK then
begin
MyTable.Edit;
MyTable.SetFields([nil, AnsiUppercase(Edit1.text), Edit1.text]);
MyTable.Post;
end;
end;
end;
Solve 3:
It depends on the type of database you want to build. However, I can show you how to do it with a Paradox table. Conceivably, it stands to reason that since the TTable is database-independent and if you've got the right settings in the BDE, you should be able to create a table with the TTable component in any database. This is not necessarily true. SQL tables are normally created using the SQL call CREATE TABLE. And each server has its own conventions for creating tables and defining fields. So it's important to note this if you're working with a SQL database. The problem is that SQL databases support different data types that aren't necessarily available in the standard BDE set. For instance, MS SQL server's NUMERIC data format is not necessarily a FLOAT as it's defined in the BDE. So your best bet would probably be to create SQL tables using SQL calls.
What you have to do is declare a TTable variable, create an instance, then with the TTable's FieldDefs property, add field definitions. Finally, you'll make a call to CreateTable, and your table will be created. Here's some example code:
{ "Add" is the operative function here.
Add(const Name: string; DataType: TFieldType; Size: Word; Required: Boolean);
}
procedure CreateATable(DBName, //Alias or path
TblName: string); //Table Name to Create
var
tbl: TTable;
begin
tbl := TTable.Create(Application);
with tbl do
begin
Active := False;
DatabaseName := DBName;
TableName := TblName;
TableType := ttParadox;
with FieldDefs do
begin
Clear;
Add('LastName', ftString, 30, False);
Add('FirstName', ftString, 30, False);
Add('Address1', ftString, 40, False);
Add('Address2', ftString, 40, False);
Add('City', ftString, 30, False);
Add('ST', ftString, 2, False);
Add('Zip', ftString, 10, False);
end;
{Add a Primary Key to the table}
with IndexDefs do
begin
Clear;
Add('Field1Index', 'LastName;FirstName', [ixPrimary, ixUnique]);
end;
CreateTable; {Make the table}
end;
end;
The procedure above makes a simple contact table, first by defining the fields to be included in the table, then creating a primary key. As you can see, it's a pretty straightforward procedure. One thing you can do is to change the TableType property setting to a variable that's passed as a parameter to the procedure so you can create DBase or even ASCII tables. Here's snippet of how you'd accomplish that:
procedure CreateATable(DBName, //Alias or path
TblName: string); //Table Name to Create
TblType: TTableType); //ttDefault, ttParadox, ttDBase, ttASCII
var
tbl: TTable;
begin
tbl := TTable.Create(Application);
with tbl do
begin
Active := False;
DatabaseName := DBName;
TableName := TblName;
TableType := TblType;
with FieldDefs do
begin
Clear;
Add('LastName', ftString, 30, False);
Add('FirstName', ftString, 30, False);
Add('Address1', ftString, 40, False);
Add('Address2', ftString, 40, False);
Add('City', ftString, 30, False);
Add('ST', ftString, 2, False);
Add('Zip', ftString, 10, False);
end;
{Add a Primary Key to the table}
with IndexDefs do
begin
Clear;
Add('Field1Index', 'LastName;FirstName', [ixPrimary, ixUnique]);
end;
CreateTable; {Make the table}
end;
end;
Pretty simple, right? One thing you should note is that the TableType property is only used for desktop databases. It doesn't apply to SQL tables.
Oh well, that's it in a nutshell. Have fun!
2010. május 23., vasárnap
How to assign the system time to a TDateField
Problem/Question/Abstract:
How can I assign the System time (returned by date function) to a TDateField in a TTable?
Answer:
Table1.Edit;
Table1.FieldByName('aDate').AsDateTime := Date;
Table1.Post;
2010. május 22., szombat
How to scroll a TMemo through code
Problem/Question/Abstract:
How to scroll a TMemo through code
Answer:
procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_F8 then
SendMessage(Memo1.Handle, WM_VSCROLL, SB_PAGEDOWN, 0)
else if Key = VK_F7 then
SendMessage(Memo1.Handle, WM_VSCROLL, SB_PAGEUP, 0);
end;
2010. május 21., péntek
Change a form's caption font and alignment (2)
Problem/Question/Abstract:
Does anyone know how to write text with the TEXTOUT command in the title bar of a form in D5?
Answer:
You have to handle the WM_NCPAINT message.
{ ... }
type
TForm1 = class(TForm)
private
procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.WMNCPaint(var Msg: TWMNCPaint);
var
ACanvas: TCanvas;
begin
inherited;
ACanvas := TCanvas.Create;
try
ACanvas.Handle := GetWindowDC(Form1.Handle);
with ACanvas do
begin
Brush.Color := clActiveCaption;
Font.Name := 'Tahoma';
Font.Size := 8;
Font.Color := clred;
Font.Style := [fsItalic, fsBold];
TextOut(GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CXBORDER),
Round((GetSystemMetrics(SM_CYCAPTION) - Abs(Font.Height)) / 2) + 1,
' Some Text');
end;
finally
ReleaseDC(Form1.Handle, ACanvas.Handle);
ACanvas.Free;
end;
end;
2010. május 20., csütörtök
Moving rows and columns of a StringGrid by code
Problem/Question/Abstract:
The user can move rows and columns of a StringGrid with the mouse. Can it also be done by code? In the help for TCustomGrid you can see the methods MoveColumn and MoveRow, but they are hidden in TStringGrid
Answer:
The user can move rows and columns of a StringGrid with the mouse. Can it also be done by code? In the help for TCustomGrid you can see the methods MoveColumn and MoveRow, but they are hidden in TStringGrid. We can make them accessible again by subclassing TStringGrid and declaring these methods as public:
type
TStringGridX = class(TStringGrid)
public
procedure MoveColumn(FromIndex, ToIndex: Longint);
procedure MoveRow(FromIndex, ToIndex: Longint);
end;
The implementation of these methods simply consists of invoking the corresponding method of the ancestor:
procedure TStringGridX.MoveColumn(FromIndex, ToIndex: Integer);
begin
inherited;
end;
procedure TStringGridX.MoveRow(FromIndex, ToIndex: Integer);
begin
inherited;
end;
You don't have to register this component in the Components Palette. Use a TStringGrid or any TCustomGrid descendant, and when you need to call these methods simply cast the object to the new class. For example:
procedure TForm1.Button1Click(Sender: TObject);
begin
TStringGridX(StringGrid1).MoveColumn(1, 3);
end;
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2010. május 19., szerda
Connect to server databases (InterBase) without the login dialog
Problem/Question/Abstract:
Connect to server databases (InterBase) without the login dialog
Answer:
To bypass the login dialog when connecting to a server database, use the property LoginPrompt.You will have to provide the username & password at runtime, but you also can set that up at design time in the object inspector, property Params.
This short source code shows how to do it:
Database1.LoginPrompt := false;
with Database1.Params do
begin
Clear;
// the parameters SYSDBA & masterkey should be
// retrieved somewhat different :-)
Add('USER NAME=SYSDBA');
Add('PASSWORD=masterkey);
end;
Database1.Connected := tr
2010. május 18., kedd
TDataSet => Excel (No OLE or EXCEL required)
Problem/Question/Abstract:
TDataSet => Excel (No OLE or EXCEL required)
Answer:
This class will produce an Excel Spreadsheet from a TDataSet. No OLE is required or Excel Installation needed to create the file. The one problem with Excel OLE is that is tends to be rather Sloooow. The class uses tandard Delphi I/O functions and is considerably faster than the OLE calls.
Example.
var
XL: TDataSetToExcel;
begin
XL := TDataSetToExcel.Create(MyQuery, 'c:\temp\test.xls');
XL.WriteFile;
XL.Free;
end;
The columns are neatly sized, Numerics are formatted in "Courier" and obey "###,###,##0.00" for floats and "0" for integers. Dates are formatted "dd-MMM-yyyy hh:nn:ss". Column headers are in Bold and are boxed and shaded.
unit MahExcel;
interface
uses Windows, SysUtils, DB, Math;
// =============================================================================
// TDataSet to Excel without OLE or Excel required
// Mike Heydon Dec 2002
// =============================================================================
type
// TDataSetToExcel
TDataSetToExcel = class(TObject)
protected
procedure WriteToken(AToken: word; ALength: word);
procedure WriteFont(const AFontName: string; AFontHeight,
AAttribute: word);
procedure WriteFormat(const AFormatStr: string);
private
FRow: word;
FDataFile: file;
FFileName: string;
FDataSet: TDataSet;
public
constructor Create(ADataSet: TDataSet; const AFileName: string);
function WriteFile: boolean;
end;
// -----------------------------------------------------------------------------
implementation
const
// XL Tokens
XL_DIM = $00;
XL_BOF = $09;
XL_EOF = $0A;
XL_DOCUMENT = $10;
XL_FORMAT = $1E;
XL_COLWIDTH = $24;
XL_FONT = $31;
// XL Cell Types
XL_INTEGER = $02;
XL_DOUBLE = $03;
XL_STRING = $04;
// XL Cell Formats
XL_INTFORMAT = $81;
XL_DBLFORMAT = $82;
XL_XDTFORMAT = $83;
XL_DTEFORMAT = $84;
XL_TMEFORMAT = $85;
XL_HEADBOLD = $40;
XL_HEADSHADE = $F8;
// ========================
// Create the class
// ========================
constructor TDataSetToExcel.Create(ADataSet: TDataSet;
const AFileName: string);
begin
FDataSet := ADataSet;
FFileName := ChangeFileExt(AFilename, '.xls');
end;
// ====================================
// Write a Token Descripton Header
// ====================================
procedure TDataSetToExcel.WriteToken(AToken: word; ALength: word);
var
aTOKBuffer: array[0..1] of word;
begin
aTOKBuffer[0] := AToken;
aTOKBuffer[1] := ALength;
Blockwrite(FDataFile, aTOKBuffer, SizeOf(aTOKBuffer));
end;
// ====================================
// Write the font information
// ====================================
procedure TDataSetToExcel.WriteFont(const AFontName: string;
AFontHeight, AAttribute: word);
var
iLen: byte;
begin
AFontHeight := AFontHeight * 20;
WriteToken(XL_FONT, 5 + length(AFontName));
BlockWrite(FDataFile, AFontHeight, 2);
BlockWrite(FDataFile, AAttribute, 2);
iLen := length(AFontName);
BlockWrite(FDataFile, iLen, 1);
BlockWrite(FDataFile, AFontName[1], iLen);
end;
// ====================================
// Write the format information
// ====================================
procedure TDataSetToExcel.WriteFormat(const AFormatStr: string);
var
iLen: byte;
begin
WriteToken(XL_FORMAT, 1 + length(AFormatStr));
iLen := length(AFormatStr);
BlockWrite(FDataFile, iLen, 1);
BlockWrite(FDataFile, AFormatStr[1], iLen);
end;
// ====================================
// Write the XL file from data set
// ====================================
function TDataSetToExcel.WriteFile: boolean;
var
bRetvar: boolean;
aDOCBuffer: array[0..1] of word;
aDIMBuffer: array[0..3] of word;
aAttributes: array[0..2] of byte;
i: integer;
iColNum,
iDataLen: byte;
sStrData: string;
fDblData: double;
wWidth: word;
begin
bRetvar := true;
FRow := 0;
FillChar(aAttributes, SizeOf(aAttributes), 0);
AssignFile(FDataFile, FFileName);
try
Rewrite(FDataFile, 1);
// Beginning of File
WriteToken(XL_BOF, 4);
aDOCBuffer[0] := 0;
aDOCBuffer[1] := XL_DOCUMENT;
Blockwrite(FDataFile, aDOCBuffer, SizeOf(aDOCBuffer));
// Font Table
WriteFont('Arial', 10, 0);
WriteFont('Arial', 10, 1);
WriteFont('Courier New', 11, 0);
// Column widths
for i := 0 to FDataSet.FieldCount - 1 do
begin
wWidth := (FDataSet.Fields[i].DisplayWidth + 1) * 256;
if FDataSet.FieldDefs[i].DataType = ftDateTime then
inc(wWidth, 2000);
if FDataSet.FieldDefs[i].DataType = ftDate then
inc(wWidth, 1050);
if FDataSet.FieldDefs[i].DataType = ftTime then
inc(wWidth, 100);
WriteToken(XL_COLWIDTH, 4);
iColNum := i;
BlockWrite(FDataFile, iColNum, 1);
BlockWrite(FDataFile, iColNum, 1);
BlockWrite(FDataFile, wWidth, 2);
end;
// Column Formats
WriteFormat('General');
WriteFormat('0');
WriteFormat('###,###,##0.00');
WriteFormat('dd-mmm-yyyy hh:mm:ss');
WriteFormat('dd-mmm-yyyy');
WriteFormat('hh:mm:ss');
// Dimensions
WriteToken(XL_DIM, 8);
aDIMBuffer[0] := 0;
aDIMBuffer[1] := Min(FDataSet.RecordCount, $FFFF);
aDIMBuffer[2] := 0;
aDIMBuffer[3] := Min(FDataSet.FieldCount - 1, $FFFF);
Blockwrite(FDataFile, aDIMBuffer, SizeOf(aDIMBuffer));
// Column Headers
for i := 0 to FDataSet.FieldCount - 1 do
begin
sStrData := FDataSet.Fields[i].DisplayName;
iDataLen := length(sStrData);
WriteToken(XL_STRING, iDataLen + 8);
WriteToken(FRow, i);
aAttributes[1] := XL_HEADBOLD;
aAttributes[2] := XL_HEADSHADE;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, iDataLen, SizeOf(iDataLen));
if iDataLen > 0 then
BlockWrite(FDataFile, sStrData[1], iDataLen);
aAttributes[2] := 0;
end;
// Data Rows
while not FDataSet.Eof do
begin
inc(FRow);
for i := 0 to FDataSet.FieldCount - 1 do
begin
case FDataSet.FieldDefs[i].DataType of
ftBoolean,
ftWideString,
ftFixedChar,
ftString:
begin
sStrData := FDataSet.Fields[i].AsString;
iDataLen := length(sStrData);
WriteToken(XL_STRING, iDataLen + 8);
WriteToken(FRow, i);
aAttributes[1] := 0;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, iDataLen, SizeOf(iDataLen));
if iDataLen > 0 then
BlockWrite(FDataFile, sStrData[1], iDataLen);
end;
ftAutoInc,
ftSmallInt,
ftInteger,
ftWord,
ftLargeInt:
begin
fDblData := FDataSet.Fields[i].AsFloat;
iDataLen := SizeOf(fDblData);
WriteToken(XL_DOUBLE, 15);
WriteToken(FRow, i);
aAttributes[1] := XL_INTFORMAT;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, fDblData, iDatalen);
end;
ftFloat,
ftCurrency,
ftBcd:
begin
fDblData := FDataSet.Fields[i].AsFloat;
iDataLen := SizeOf(fDblData);
WriteToken(XL_DOUBLE, 15);
WriteToken(FRow, i);
aAttributes[1] := XL_DBLFORMAT;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, fDblData, iDatalen);
end;
ftDateTime:
begin
fDblData := FDataSet.Fields[i].AsFloat;
iDataLen := SizeOf(fDblData);
WriteToken(XL_DOUBLE, 15);
WriteToken(FRow, i);
aAttributes[1] := XL_XDTFORMAT;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, fDblData, iDatalen);
end;
ftDate:
begin
fDblData := FDataSet.Fields[i].AsFloat;
iDataLen := SizeOf(fDblData);
WriteToken(XL_DOUBLE, 15);
WriteToken(FRow, i);
aAttributes[1] := XL_DTEFORMAT;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, fDblData, iDatalen);
end;
ftTime:
begin
fDblData := FDataSet.Fields[i].AsFloat;
iDataLen := SizeOf(fDblData);
WriteToken(XL_DOUBLE, 15);
WriteToken(FRow, i);
aAttributes[1] := XL_TMEFORMAT;
BlockWrite(FDataFile, aAttributes, SizeOf(aAttributes));
BlockWrite(FDataFile, fDblData, iDatalen);
end;
end;
end;
FDataSet.Next;
end;
// End of File
WriteToken(XL_EOF, 0);
CloseFile(FDataFile);
except
bRetvar := false;
end;
Result := bRetvar;
end;
end.
2010. május 17., hétfő
Open a TOpenDialog in detail view
Problem/Question/Abstract:
I use the standard OpenDialog to select a file to open. To see the creation date of a file, I have to change the view to Details (in fact my preferred view) every time. I was looking for an attribute (in the Options) to configure, that always detail view is selected when the OpenDialog is activated. However I didn't find anything. Does somebody have a hint for this problem?
Answer:
Add this code to the OnFolderChange event of the dialog:
procedure TForm1.OpenDialog1FolderChange(Sender: TObject);
var
H, H2: THandle;
begin
H := FindWindowEx(GetParent(OpenDialog1.Handle), 0, PChar('SHELLDLL_DefView'), nil);
H2 := FindWindowEx(H, 0, PChar('SysListView32'), nil);
if (H <> 0) and (H2 <> 0) then
begin
SendMessage(H, WM_COMMAND, $702C, 0);
Windows.SetFocus(H2);
PostMessage(H2, WM_KEYDOWN, VK_SPACE, 0);
end;
end;
2010. május 16., vasárnap
Checking for Numlock and Capslock and displaying on Statusbar
Problem/Question/Abstract:
How do I check for a Numlock enabled and Capslock enable?
Answer:
Here are two procedures that you add to the main form of your application:
procedure TfrmMain.CheckCapslock;
begin
if Odd(Getkeystate(VK_CAPITAL)) then
Statusline.Panels[1].text := 'CAPS'
else
Statusline.Panels[1].text := '';
end;
procedure TfrmMain.CheckNumlock;
begin
if Odd(Getkeystate(VK_NUMLOCK)) then
Statusline.Panels[2].text := 'NUM'
else
Statusline.Panels[2].text := '';
end;
Add a application component to your project and simply call both these procedures in the Application.onmessage event i.e.:
procedure TfrmMain.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
CheckCapslock;
CheckNumlock;
end;
2010. május 15., szombat
Creating a Delphi-Expert (Part I)
Problem/Question/Abstract:
Sometimes you want to define some routines to make your life easier while using Delphi. A simple way to do this, is creating an Expert. This first article shows you the basics.
Answer:
This article introduces you to the world of Delphi Experts. Delphi Experts are DLLs, that will be loaded during the startup sequence of Delphi. This article first appeared on Delphi-PRAXiS in German.
NOTE: The techniques shown in this article are valid starting with Delphi 3 or 4 and since Delphi 7 they are deprecated, however, still fully suported by the Delphi IDE.
Installation of a Delphi-IDE-Expert
Every Delphi-Expert has to be registered in the Windows-Registry. For each Delphi-Version installed on a machine, as well as for each user using the machine, the Delphi-Expert has to be registered separately.
In the Registry the Delphi-Expert has to be registered under the folowing key:
HKCU\Software\Borland\Delphi\X.0\Experts
, where the X has to be replaced by the appropriate Delphi-Version supported. It may happen that the Experts key is not installed, in such case you are required to create it.
Underneath the Experts key you have to create a string value for the Delphi-Expert. The name must be unique. The value must point to the Delphi-Expert DLL, including both complete path and file name of the Delphi-Expert. Next time Delphi starts, the Expert will be loaded automatically.
The interface of the Delphi-Expert
In order for the Delphi Expert to interact with the Delphi-IDE ist has to export a function with the name ExpertEntryPoint, using the following parameters:
function InitExpert(ToolServices: TIToolServices; RegisterProc:
TExpertRegisterProc; var Terminate: TExpertTerminateProc): Boolean; export;
stdcall;
The first parameter ToolServices offers all "documented" interfaces to the Delphi-IDE. The second parameter RegisterProc is used to load the expert into the Delphi-IDE. The last parameter Teminate is used to notify the Expert-DLL when it is about to be unloaded by the Delphi-IDE.
The InitExpert method returns True, if the Expert has loaded successfully, otherwise it can eiter return False or raises an exception to unload the DLL from the Delphi-IDE (see code sample for solution).
The PlugIn class TIExpert
Any Delphi-Expert must be derived from the class TIExpert, which is declared in the unit ExptIntf. This class defines some abstract methods, which must be implemented by each PlugIn: GetName, GetAuthor, GetComment, GetPage, GetGlyph (different for Windows and Linux), GetStyle, GetState, GetIDString, GetMenuText and Execute. The purpose of each method is explained in the source code below.
The simplest Delphi-Expert
This Delphi-Expert want do much good, however, it shows you the basic way of getting the job done. It will show an entry in the Help menu (default behavior). Once the user clicks the menu item the method Execute from the Expert will be called. The following points must be respected in order to get the expert working:
The method GetState must return [esEnabled]
The method GetStyle must return esStandard
The method GetMenuText returns the text to be shown in the Help menu
The method Execute defines the expert action upon activation
The Library Source Code (DelphiPlugI.dpr)
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* App. Name : DelphiPlug
* Autor : Daniel Wischnewski
* Copyright : Copyright © 2000-2003 by gate(n)etwork GmbH. All Right Reserved.
* Urheber : Daniel Wischnewski
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
library DelphiPlug;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
ShareMem,
ExptIntf,
uPlugIn in 'uPlugIn.pas';
{$R *.res}
exports
InitExpert name ExpertEntryPoint;
begin
end.
The Unit Source Code (uPlugIn.pas)
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Unit Name : uPlugIn
* Autor : Daniel Wischnewski
* Copyright : Copyright © 2000-2003 by gate(n)etwork GmbH. All Right Reserved.
* Urheber : Daniel Wischnewski
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
unit uPlugIn;
interface
uses
ToolIntf, EditIntf, ExptIntf, VirtIntf, Windows, Messages;
const
MIdx_Main = $0001;
MIdx_ShowItems = $0002;
MIdx_RunCommand = $0003;
type
TDelphiPlug = class(TIExpert)
private
protected
public
// abstract methods to be overriden
{ Expert UI strings }
function GetName: string; override; stdcall;
function GetAuthor: string; override; stdcall;
function GetComment: string; override; stdcall;
function GetPage: string; override; stdcall;
{$IFDEF MSWINDOWS}
function GetGlyph: HICON; override; stdcall;
{$ENDIF}
{$IFDEF LINUX}
function GetGlyph: Cardinal; override; stdcall;
{$ENDIF}
function GetStyle: TExpertStyle; override; stdcall;
function GetState: TExpertState; override; stdcall;
function GetIDString: string; override; stdcall;
function GetMenuText: string; override; stdcall;
{ Launch the Expert }
procedure Execute; override; stdcall;
end;
function InitExpert(ToolServices: TIToolServices; RegisterProc:
TExpertRegisterProc; var Terminate: TExpertTerminateProc): Boolean; export;
stdcall;
implementation
uses
SysUtils, ShellAPI;
function InitExpert(ToolServices: TIToolServices; RegisterProc:
TExpertRegisterProc; var Terminate: TExpertTerminateProc): Boolean; export;
stdcall;
var
DelphiPlug: TDelphiPlug;
begin
Result := True;
try
// assign tools services
ExptIntf.ToolServices := ToolServices;
// create the Delphi-Plug
DelphiPlug := TDelphiPlug.Create;
// register with Delphi
RegisterProc(DelphiPlug);
except
// kill assistant
ToolServices.RaiseException(ReleaseException);
end;
end;
{ TDelphiPlug }
procedure TDelphiPlug.Execute;
begin
// en:
// Execute will be called, whenever the user clicks on the menu entry in the
// help menu
// de:
// Execute wird aufgerufen, wenn der User auf den Eintrag im Hilfe-Men�
// klickt
MessageBox(ToolServices.GetParentHandle, 'How may I help you?', 'Hmm',
MB_ICONQUESTION + MB_OK);
end;
function TDelphiPlug.GetAuthor: string;
begin
// en:
// returns the name of the author of the plugin
// de:
// liefert den Namen des Autoren des PlugIns zur�ck (wof�r auch immer)
Result := 'sakura (Daniel Wischnewski)';
end;
function TDelphiPlug.GetComment: string;
begin
// en:
// I got no idea where this comment will be displayed, ever.
// de:
// Auch hier wei� ich nicht, wo das jemals angezeigt wird, aber bitte...
Result := 'A simple Delphi-PlugIn example.';
end;
{$IFDEF MSWINDOWS}
function TDelphiPlug.GetGlyph: HICON;
begin
// en:
// an icon handle for the entry in the help menu
// de:
// Ein Icon-Handle f�rs Men�
Result := NOERROR;
end;
{$ENDIF}
{$IFDEF LINUX}
function TDelphiPlug.GetGlyph: Cardinal;
begin
// en:
// an icon handle for the entry in the help menu
// de:
// Ein Icon-Handle f�rs Men�
Result := NOERROR;
end;
{$ENDIF}
function TDelphiPlug.GetIDString: string;
begin
// en:
// id of the expert
// de:
// ID des Experten
Result := 'DelphiPlugSampleI';
end;
function TDelphiPlug.GetMenuText: string;
begin
// en:
// this text will be schon in the help menu. each time the menu drops down,
// this method will be called.
// NOTE:
// the method GetState must return esStandard, otherwise the help menu
// entry will not be generated and shown
//
// de:
// Text der im Hilfe Men� angezeigt wird. Diese Funktion wird jedesmal
// aufgerufen, wenn das Hilfemen� angezeigt wird.
// HINWEIS:
// die Methode GetState mu� esStandard zur�ckliefern, damit dieser Eintrag
// im Hilfemen� automatisch generiert wird
Result := 'You''l find me in the help menu';
end;
function TDelphiPlug.GetName: string;
begin
// en:
// this name must be unique
// de:
// dieser Name muss!!! einmalig sein
Result := 'sakura_DelphiPlugSample';
end;
function TDelphiPlug.GetPage: string;
begin
// en:
// interesting to experts expanding the default dialogs of the Delphi-IDE
// de:
// Ist f�r Experte interessant, welche Standard-Dialoge erweitern sollen
Result := '';
end;
function TDelphiPlug.GetState: TExpertState;
begin
// en:
// returns a set of states
// possible values: esEnabled, esChecked
// de:
// liefert ein Set von Stati zur�ck
// m�gliche Werte: esEnabled, esChecked
Result := [esEnabled];
end;
function TDelphiPlug.GetStyle: TExpertStyle;
begin
// en:
// returns the type of expert
// de:
// liefert die Art des Experten zur�ck
// m�gliche Werte: esStandard, esForm, esProject, esAddIn
Result := esStandard;
end;
end.
2010. május 14., péntek
Create smaller EXEs
Problem/Question/Abstract:
Create smaller EXEs
Answer:
Delphi 3 can create reasonable smaller executables (*.EXE files) than Delphi 1/ 2.
Go to menu "Project | Options", select tab "Packages" and check the option "Build with runtime packages".
The packages listed in the text - by default:
vclx30;VCL30;vcldb30;vcldbx30;VclSmp30;inetdb30;inet30;Qrpt30;
teeui30;teedb30;tee30;dss30;IBEVNT30
will not be compiled into the EXE file. Instead you will have to ship a *.DCP file.
This makes (only) sense if you have several applications using the same controls = the same *.DCP file.
2010. május 13., csütörtök
Add a submenu to the system menu of a program
Problem/Question/Abstract:
I'm trying to insert a submenu into my application's system menu. Anyone have a code example of adding a submenu and menu items underneath that new submenu?
Answer:
Here's some sample code to play with:
{ ... }
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
{ ... }
const
SC_ITEM = $FF00; {Should be a multiple of 16}
procedure TForm1.WMSysCommand(var Msg: TWMSysCommand);
begin
{See if this is a command we added}
if (Msg.CmdType and $FFF0) = SC_ITEM then
begin
ShowMessage('Item command received');
Msg.Result := 0;
end
else
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
MenuItemInfo: TMenuItemInfo;
PopupMenu: HMENU;
Result: Boolean;
SysMenu: HMenu;
begin
{Create the popup menu}
PopupMenu := CreatePopupMenu;
Assert(PopupMenu <> 0);
{Insert an item into it}
FillChar(MenuItemInfo, SizeOf(MenuItemInfo), 0);
with MenuItemInfo do
begin
cbSize := SizeOf(MenuItemInfo);
fMask := MIIM_TYPE or MIIM_ID;
fType := MFT_STRING;
wID := SC_ITEM;
dwTypeData := PChar('Item');
cch := 4; {'Item' is 4 chars}
end;
Result := InsertMenuItem(PopupMenu, 0, True, MenuItemInfo);
Assert(Result, 'InsertMenuItem failed');
{Insert the popup into the system menu}
FillChar(MenuItemInfo, SizeOf(MenuItemInfo), 0);
with MenuItemInfo do
begin
cbSize := SizeOf(MenuItemInfo);
fMask := MIIM_SUBMENU or MIIM_TYPE;
fType := MFT_STRING;
hSubMenu := PopupMenu;
dwTypeData := PChar('SubMenu');
cch := 7; {'SubMenu' is 7 chars}
end;
SysMenu := GetSystemMenu(Handle, False);
Assert(SysMenu <> 0);
Result := InsertMenuItem(SysMenu, GetMenuItemCount(SysMenu), True, MenuItemInfo);
Assert(Result, 'InsertMenuItem failed');
end;
2010. május 12., szerda
Using Console in non-console applications
Problem/Question/Abstract:
How to implement console input/output for non-console applications?
Answer:
For implementing console input/output for non-console applications you should use the AllocConsole and FreeConsole functions.
Example below demonstrates using these functions:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
AllocConsole;
try
Write('Type here your words and press ENTER: ');
Readln(s);
ShowMessage(Format('You typed: "%s"', [s]));
finally
FreeConsole;
end;
end;
2010. május 11., kedd
Resize a *.jpg image and save the result to a file
Problem/Question/Abstract:
How do I resize a *.jpg or *.gif image from say 640 x 480 to 50 x 50 and then save the image as a new one?
Answer:
procedure TForm1.Button1Click(Sender: TObject);
var
bmp: TBitmap;
jpg: TJpegImage;
scale: Double;
begin
if opendialog1.execute then
begin
jpg := TJpegImage.Create;
try
jpg.Loadfromfile(opendialog1.filename);
if jpg.Height > jpg.Width then
scale := 50 / jpg.Height
else
scale := 50 / jpg.Width;
bmp := TBitmap.Create;
try
{Create thumbnail bitmap, keep pictures aspect ratio}
bmp.Width := Round(jpg.Width * scale);
bmp.Height := Round(jpg.Height * scale);
bmp.Canvas.StretchDraw(bmp.Canvas.Cliprect, jpg);
{Draw thumbnail as control}
Self.Canvas.Draw(100, 10, bmp);
{Convert back to JPEG and save to file}
jpg.Assign(bmp);
jpg.SaveToFile(ChangeFileext(opendialog1.filename, '_thumb.JPG'));
finally
bmp.free;
end;
finally
jpg.free;
end;
end;
end;
2010. május 10., hétfő
What is DelphiX?
Problem/Question/Abstract:
What is DelphiX?
Answer:
DelphiX is a very good DirectX implementation with 12 visual components for the Delphi versions 3, 4 and 5. The componets were programmed by Hiroyuki Hori (his homepage: http://www.yks.ne.jp/~hori/index-e.html). DelphiX supports all DirectX technologies (DirectDraw, Direct3D, DirectPlay, DirectInput...) to program high performance graphic applications with Delphi.
This components are:
TDXDraw - DirectDraw surface for graphic output
TDXDIB - DIB-Image
TDXImageList - Imagelist of DIB-Images
TDX3D - Direct3D support for TDXDraw surface
TDXSound - DirectSound support
TDXWave - Soundfile-component for DirectSound
TDXWaveList - Soundfile-list
TDXInput - DirectInput, controller support
TDXPlay - DirectPlay, for multiplayer network games
TDXSpriteEngine - Spriteengine for DirectDraw-surface
TDXTimer - High-performance Timer
TDXPaintBox - Like TPaintbox but faster
Related links:
Microsoft's DirectX
- http://www.microsoft.com/directx
Hori's homepage
- http://www.yks.ne.jp/~hori/index-e.html
DelphiX download
- http://www.yks.ne.jp/~hori/DelphiX-e.html
Game FinalFighter, programmed using DelphiX
- http://www.finalfighter.com
Good page about DelphiX
- http://turbo.gamedev.net/delphix.asp
Component Download: http://www.yks.ne.jp/~hori/DelphiX-e.html
2010. május 9., vasárnap
Read Adobe Acrobat PDF files from my application
Problem/Question/Abstract:
Adobe Acrobat PDF is a well known format that some users love, so how can I open PDF files from a Delphi Application?
Answer:
Ok, you must have installed the Acrobat Reader program in your machine, if you don�t have it you can download it from Adobe�s site: www.adobe.com
After that you have to install the type library for Acrobat (Project -> Import Type Library from Delphi�s menu) select "Acrobat Control for ActiveX (version x)". Where x stands for the current version of the type library. Click the install button to install it into the IDE.
Now, Start a new Application, drop from whatever page of the component palette you have installed a TPDF component in a form, next add an OpenDialog, and finally a Button, in the Onclick event of the Button use:
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
pdf1.src := OpenDialog1.FileName;
end;
in PdfLib_TLB Unit you can find the interface of the TPdf class in order to know the behaviour of that class so here it is:
TPdf = class(TOleControl)
private
FIntf: _DPdf;
function GetControlInterface: _DPdf;
protected
procedure CreateControl;
procedure InitControlData; override;
public
function LoadFile(const fileName: WideString): WordBool;
procedure setShowToolbar(On_: WordBool);
procedure gotoFirstPage;
procedure gotoLastPage;
procedure gotoNextPage;
procedure gotoPreviousPage;
procedure setCurrentPage(n: Integer);
procedure goForwardStack;
procedure goBackwardStack;
procedure setPageMode(const pageMode: WideString);
procedure setLayoutMode(const layoutMode: WideString);
procedure setNamedDest(const namedDest: WideString);
procedure Print;
procedure printWithDialog;
procedure setZoom(percent: Single);
procedure setZoomScroll(percent: Single; left: Single; top:
Single);
procedure setView(const viewMode: WideString);
procedure setViewScroll(const viewMode: WideString; offset:
Single);
procedure setViewRect(left: Single; top: Single; width: Single;
height: Single);
procedure printPages(from: Integer; to_: Integer);
procedure printPagesFit(from: Integer; to_: Integer; shrinkToFit:
WordBool);
procedure printAll;
procedure printAllFit(shrinkToFit: WordBool);
procedure setShowScrollbars(On_: WordBool);
procedure AboutBox;
property ControlInterface: _DPdf read GetControlInterface;
property DefaultInterface: _DPdf read GetControlInterface;
published
property TabStop;
property Align;
property DragCursor;
property DragMode;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property Visible;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnStartDrag;
property src: WideString index 1 read GetWideStringProp write
SetWideStringProp stored False;
end;
finally here�s an advice:
You can�t be sure your users will have Acrobat Reader installed so please fisrt check that situation before you take any actions with the TPdf component. And second if your PDF file have links for an AVI file for example, they don�t work from Delphi.
2010. május 8., szombat
Use RTTI to determine if a property is a TDateTime
Problem/Question/Abstract:
How to use RTTI to determine if a property is a TDateTime
Answer:
When it comes to RTTI, TDateTime and Double are not the same. That's what the extra "type" keyword in TDateTime's declaration is for: to give it its own RTTI, distinct from that of Double. Here is an example:
program Test;
uses
TypInfo;
{$APPTYPE CONSOLE}
{$M+}
type
TTest = class
private
FDateTime: TDateTime;
published
property D: TDateTime read FDateTime write FDateTime;
end;
var
T: TTest;
DateInfo: Pointer;
TestInfo: PPropInfo;
begin
T := TTest.Create;
DateInfo := TypeInfo(TDateTime);
TestInfo := GetPropInfo(T, 'D');
writeln(DateInfo = TestInfo^.PropType^);
readln;
end.
It should print TRUE on the console.
2010. május 7., péntek
Remove the popup menu from Flash's ActiveX
Problem/Question/Abstract:
I wanted to insert an Macromedia Flash intro into my program using the provided ActiveX, but I also wanted to remove the ugly Flash's popup menu. That's the way.
And if it's not enought, you can replace it with your own popup menu!
Answer:
In your Form, where the Flash ActiveX is, place an "Application Events" component.
Into the "OnMessage" Event put this code:
procedure TfrmMain.AppEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if (Msg.Message = WM_RBUTTONDOWN) then
Handled := True;
end;
It's not enought? Do you want to put your own PopupMenu? There's the solution:
procedure TfrmMain.AppEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if (Msg.Message = WM_RBUTTONDOWN) then
begin
popupmnuFlash.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y);
Handled := True;
end;
end;
2010. május 5., szerda
How to start or stop Interbase service
Problem/Question/Abstract:
How to start or stop Interbase service
Answer:
Do you need to shutdown the Interbase db service e.g. for an installation program and afterwards restart it?
You could do this with a lot of Delphi code involving unit WinSvc and function calls to
OpenSCManager()
EnumServicesStatus()
OpenService()
StartService() or ControlService().
But luckily there is a much easier solution that uses the NET.EXE program which has been part of Windows since Windows for Workgroups (Wfw 3.11). Just create the two batch files
IBSTOP.BAT
IBSTART.BAT
and call them from your code. You may want to call them and wait for their termination.
IBSTOP.BAT
=============
@echo off
net stop "InterBase Guardian" >NULL
net stop "InterBase Server" >NULL
IBSTART.BAT
=============
@echo off
net start "Interbase Guardian" >NULL
2010. május 4., kedd
How to search and replace strings in a TMemo
Problem/Question/Abstract:
How to search and replace strings in a TMemo
Answer:
Doing search and replace on strings has been made trivial because of these 3 functions: Pos(), Delete(), and Insert(). Pos() takes two parameters, a pattern search string, and a string to find the pattern in - it returns the location of the string, or 0 if it does not exist. Delete() takes three parameters, the string to delete from, location of where to start deleting, and how much to delete. Similarly, Insert() takes three parameters too. The string that will be inserted, the string to insert into, the location to insert.
Many class properties use strings to store values, so one can use this method on any of them. For instance, the searching and replacing of an entire TMemo component might look like this:
procedure TForm1.Button2Click(Sender: TObject);
var
i: integer;
s1: string;
SearchStr: string;
NewStr: string;
place: integer;
begin
SearchStr := 'line';
NewStr := 'OneEye';
for i := 0 to Memo1.Lines.Count - 1 do
begin
s1 := Memo1.Lines[i];
repeat
Place := pos(SearchStr, s1);
if place > 0 then
begin
Delete(s1, Place, Length(SearchStr));
Insert(NewStr, s1, Place);
Memo1.Lines[i] := s1;
end;
until
place = 0;
end;
end;
2010. május 3., hétfő
Create a borderless TComboBox
Problem/Question/Abstract:
Is it possible to create a flat or borderless combo box? If so, how would I go about it.
Answer:
{ ... }
TNoBorderComboBox = class(TComboBox)
protected
procedure WMPaint(var Msg: TMessage); message WM_PAINT;
end;
procedure TNoBorderComboBox.WMPaint(var Msg: TMessage);
var
C: TControlCanvas;
R: TRect;
begin
inherited;
C := TControlCanvas.Create;
try
C.Control := Self;
with C do
begin
Brush.Color := clBtnFace;
R := ClientRect;
FrameRect(R);
InflateRect(R, -1, -1);
FrameRect(R);
end;
finally
C.Free;
end;
end;
2010. május 2., vasárnap
Various image XOR effects
Problem/Question/Abstract:
Various image XOR effects
Answer:
Solve 1:
Create a new application, add a button to the form, and add the following code for the button's OnClick event:
{ ... }
var
bih: TBitmapInfo;
i, j: Byte;
ptrBits, ptrTemp: Pointer;
begin
{Initialise BITMAPINFO structure}
ZeroMemory(@bih, SizeOf(bih));
with bih.bmiHeader do
begin
biSize := SizeOf(TBitmapInfoHeader);
biWidth := 256;
biHeight := 256;
biPlanes := 1;
biBitCount := 24;
biSizeImage := 256 * 256 * 3;
end;
{Allocate memory for pixel data}
ptrBits := GlobalAllocPtr(GMEM_FIXED or GMEM_ZEROINIT, 256 * 256 * 3);
try
ptrTemp := ptrBits;
{Manipulate pixels using XOR operator}
for j := 0 to 255 do
begin
for i := 0 to 255 do
begin
PByte(ptrTemp)^ := i xor j; {Blue component}
Inc(PByte(ptrTemp));
PByte(ptrTemp)^ := i xor j; {Green component}
Inc(PByte(ptrTemp));
PByte(ptrTemp)^ := i xor j; {Red component}
Inc(PByte(ptrTemp));
end;
end;
{Draw to screen}
StretchDIBits(Canvas.Handle, 0, 255, 256, -256, 0, 0, 256, 256,
ptrBits, bih, DIB_RGB_COLORS, SRCCOPY);
finally
GlobalFreePtr(ptrBits);
end;
end;
Solve 2:
Mark, this was a very interesting effect. I first tried your code in a FormCreate but saw nothing. Your code works fine from a ButtonClick method, but will need to be moved to an OnPaint for persistence.
Code using Scanline in my opinion is easier to understand - and like your code will also work in D3 - D6:
procedure TFormXOReffect.ButtonScanlineMethodClick(Sender: TObject);
type
TRGBTripleArray = array[Word] of TRGBTriple;
pRGBTripleArray = ^TRGBTripleArray;
var
Bitmap: TBitmap;
i: Byte;
j: Byte;
row: pRGBTripleArray;
begin
Bitmap := TBitmap.Create;
try
Bitmap.Width := 256;
Bitmap.Height := 256;
Bitmap.PixelFormat := pf24bit;
for j := 0 to 255 do
begin
row := Bitmap.Scanline[j];
for i := 0 to 255 do
begin
row[i].rgbtBlue := i xor j;
row[i].rgbtGreen := i xor j;
row[i].rgbtRed := i xor j
end;
end;
{Display in 256-by-256 TImage}
Image1.Picture.Graphic := Bitmap
finally
Bitmap.Free
end;
end;
Solve 3:
I played around with it for a few minutes and came up with a very subtle gradient effect:
{ ... }
{Shade}
Bmp.Canvas.Brush.Color := clBlack;
Bmp.Canvas.FillRect(Rect(0, 0, Bmp.Width - 1, Bmp.Height - 1));
for j := 0 to Bmp.Height - 1 do
begin
row := Bmp.Scanline[j];
for i := 0 to Bmp.Width - 1 do
begin
row[i].rgbtBlue := row[i].rgbtBlue xor j;
row[i].rgbtGreen := row[i].rgbtGreen xor j;
row[i].rgbtRed := row[i].rgbtRed xor j
end;
end;
{ ... }
if you change 1 or 2 of the xor j's to XOR i, then it does another nice gradient effect.:
begin
row[i].rgbtBlue := row[i].rgbtBlue xor i;
row[i].rgbtGreen := row[i].rgbtGreen xor i;
row[i].rgbtRed := row[i].rgbtRed xor j
end;
Solve 4:
I like that one, too. And if you add ...
{ ... }
{now gray scale it}
row[i].rgbtRed := (row[i].rgbtRed + Row[i].rgbtGreen + row[i].rgbtBlue) div 3;
row[i].rgbtGreen := row[i].rgbtRed;
row[i].rgbtBlue := row[i].rgbtRed;
... you get a nice metalic look.
2010. május 1., szombat
Search and replace text in a Word document
Problem/Question/Abstract:
How to search and replace text in a Word document
Answer:
Solve 1:
You should use a variant because the Find.Execute method is a bit buggy. Something like this, for example:
{ ... }
var
Rnge: OleVariant;
{ ... }
Rnge := Doc.Content;
Rnge.Find.Execute('old', Wrap := wdFindContinue, ReplaceWith := 'new', Replace :=
wdReplaceAll);
{ ... }
Solve 2:
{ ... }
{ Create the OLE Object }
WordApp := CreateOLEObject('Word.Application');
WordApp.Documents.Open(yourDocFile);
WordApp.Selection.Find.ClearFormatting;
WordApp.Selection.Find.Text := yourOldStr;
WordApp.Selection.Find.Replacement.Text := yourNewStr;
WordApp.Selection.Find.Forward := True;
WordApp.Selection.Find.Wrap := 1; {wdFindContinue}
WordApp.Selection.Find.Format := False;
WordApp.Selection.Find.MatchCase := False;
WordApp.Selection.Find.MatchWholeWord := False;
WordApp.Selection.Find.MatchWildcards := True;
WordApp.Selection.Find.MatchSoundsLike := False;
WordApp.Selection.Find.MatchAllWordForms := False;
WordApp.Selection.Find.Execute(Replace := 2); {wdReplaceAll}
{Or as alternative: WordApp.Selection.Find.Execute(Replace := 1); for one replace}
WordApp.ActiveDocument.SaveAs(yourNewDocFile);
WordApp.Quit;
WordApp := Unassigned;
{ ... }
Feliratkozás:
Bejegyzések (Atom)