This
Mini FAQ is provided to supplement the user-supported
cppbuilder.vcl.components.using
newsgroup. For convenience, it is
divided
into separate sections according to the following categories:
Part
1/5: Application, SDI, and MDI
Part
2/5: Common Controls
Part
3/5: Standard Controls
Part
4/5: VCL Specific Controls
Part
5/5: Resources
This
text can be downloaded in its entirety from the following URL:
http://homepages.go.com/~damonchandler/vcl_faq.zip
(12 KB)
//////////////////////////////////////////////////////////////////////
Part
1: Application, SDI, and MDI
==================================
---
(Q1.1) How do I create a hidden main form?
(A1.1)
The TApplication class has the ShowMainForm property, which
can be used to control whether or not the main form is
initially displayed. The default value is true.
WINAPI
WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->ShowMainForm = false;
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
---
(Q1.2) How do I hide the application's taskbar button?
(A1.2)
To hide a window's taskbar button, use the ShowWindow()
API function, specifying the SW_HIDE flag. Since the
taskbar button that is visible belongs to the Application
window, pass its Handle property as the hWnd parameter
of the function. The taskbar buttton will appear when the
Application is minimized via an explicit call to the
TApplication::Minimize() member function, or the by
minimizing the main form. Combat this behavior by providing
a handler for the TApplication::OnMinimize event.
//
in header...
void __fastcall AppMinimizeHandler(TObject *Sender);
//
in source...
__fastcall
TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Application->OnMinimize = AppMinimizeHandler;
}
void
__fastcall TForm1::AppMinimizeHandler(TObject *Sender)
{
ShowWindow(Application->Handle, SW_HIDE);
}
void
__fastcall TForm1::FormShow(TObject *Sender)
{
ShowWindow(Application->Handle, SW_HIDE);
}
---
(Q1.3) How do I create a menu that lists all the MDI child forms?
(A1.3)
The VCL has a wrapper around the standard MDI Window menu,
the TForm::WindowMenu property.
---
(Q1.4) How do I hide an MDI child form?
(A1.4)
The ShowWindow() API function can be used to hide MDI child
forms. Specify the handle of the target child form and the
SW_HIDE flag. To show the form again, use the same function
with the SW_SHOW flag. For example:
ShowWindow(ActiveMDIChild->Handle,
SW_HIDE);
---
(Q1.5) How do I access the active MDI child form?
(A1.5)
The TForm::ActiveMDIChild property can be used to access the
active MDI child. This is the same as MDIChildren[0]. If
you're trying to access a specific control on this child form,
be sure to cast the returned TForm pointer appropriately.
---
(Q1.6) How do I access a specific MDI child form?
(A1.6)
You can use the MDIChildren array. Be aware however, contrary
to the help files, this array contains the most active MDI
child in the zeroth position. The child form active before
the MDIChildren[0] (ActiveMDIChild) form is held in the first
position, MDIChildren[1], and so on. If you're trying to
access a specific control on this form, be sure to cast the
returned TForm pointer appropriately.
---
(Q1.7) How do I paint a background image on an MDI form?
(A1.7)
To do this, you'll need to draw directly to the client window.
More specifically, subclass the client window (access its handle
via the ClientHandle property) and render the image in response
to the WM_ERASEBKGND message.
//
in header...
FARPROC NewClientWP;
FARPROC OldClientWP;
void __fastcall MDIClientWndProc(TMessage &Msg);
//
in source...
__fastcall
TMainForm::TMainForm(TComponent *Owner)
: TForm(Owner)
{
NewClientWP = reinterpret_cast<FARPROC>
(MakeObjectInstance(MDIClientWndProc));
OldClientWP = reinterpret_cast<FARPROC>
(SetWindowLong(ClientHandle, GWL_WNDPROC,
reinterpret_cast<LONG>(NewClientWP)));
}
void
__fastcall TMainForm::MDIClientWndProc(TMessage &Msg)
{
switch (Msg.Msg)
{
// draw the image to the device context of
the
// client window
case WM_ERASEBKGND:
{
HDC Hdc = reinterpret_cast<HDC>(Msg.WParam);
SelectPalette(Hdc, Image1->Picture->Bitmap->Palette, true);
RealizePalette(Hdc);
StretchBlt(Hdc, 0, 0,
Image1->Width, Image1->Height,
Image1->Canvas->Handle,
0, 0,
Image1->Picture->Bitmap->Width,
Image1->Picture->Bitmap->Height,
SRCCOPY);
Msg.Result = 0;
return;
}
// handle the palette changes
case WM_QUERYNEWPALETTE:
{
HDC Hdc = GetDC(ClientHandle);
SelectPalette(Hdc, Image1->Picture->Bitmap->Palette, true);
RealizePalette(Hdc);
InvalidateRect(ClientHandle, NULL, true);
ReleaseDC(ClientHandle, Hdc);
Msg.Result = 0;
return;
}
case WM_PALETTECHANGED:
{
if (reinterpret_cast<HWND>(Msg.WParam) != ClientHandle)
{
HDC Hdc = GetDC(ClientHandle);
SelectPalette(Hdc,
Image1->Picture->Bitmap->Palette,
true);
RealizePalette(Hdc);
UpdateColors(Hdc);
ReleaseDC(ClientHandle, Hdc);
}
Msg.Result = 0;
return;
}
// refresh the image upon scrolling
case WM_HSCROLL:
case WM_VSCROLL:
{
InvalidateRect(ClientHandle, NULL, true);
break;
}
// un-subclass the client window
case WM_DESTROY:
{
SetWindowLong(ClientHandle, GWL_WNDPROC,
reinterpret_cast<LONG>(OldClientWP));
FreeObjectInstance(NewClientWP);
}
}
// call the default window procedure
Msg.Result = CallWindowProc(OldClientWP, ClientHandle, Msg.Msg,
Msg.WParam, Msg.LParam);
}
---
(Q1.8) How
do I display a dialog, such as a password dialog,
before the main form becomes visible?
(A1.8) Create,
display, and destroy the dialog before creating
the main form. This can be done in the source file for
the project (Project | View Source).
#include
<memory>
#include
"PWDialogUnit.h"
WINAPI WinMain(HINSTANCE,
HINSTANCE, LPSTR, int)
{
try
{
// give PWDialog local scope
{
AnsiString error_msg("Sorry, acess denied.");
std::auto_ptr<TPWDialog> PWDialog(new TPWDialog(NULL));
if (PWDialog->ShowModal() == mrOk)
{
if (PWDialog->PasswordEdit->Text != "password")
throw EAbort(error_msg);
}
else throw EAbort(error_msg);
}
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->CreateForm(__classid(TPWDialog), &PWDialog);
Application->Run();
}
catch
(Exception &exception)
{
Application->ShowException(&exception);
}
return
0;
}
Part
2: Common Controls
========================
---
(Q2.1) How can I retrieve all selected items in a TListView?
(A2.1)
Use the TListView::GetNextItem() member function. This will
return a pointer to a TListItem which can be successively passed
into the method again.
TItemStates
selected = TItemStates() << isSelected;
TListItem
*Item = ListView1->Selected;
while
(Item)
{
// code...
Item = ListView1->GetNextItem(Item, sdAll, selected);
}
---
(Q2.2) How do I speed up adding items to my ListView?
(A2.2a)
Be sure to suspend screen updates via the BeginUpdate() and
EndUpdate() methods (or the WM_SETREDRAW message). Also,
disable any sorting routines if present, and switch out of
vsReport ViewStyle before adding any items.
(A2.2b)
Abandon the TListItems/TListItem classes, and use the API
directly via the LVITEM structure in conjunction with the
LPSTR_TEXTCALLBACK and I_IMAGECALLBACK flags for the pszText
and iImage members, respectively.
(A2.2c)
Use a virtual-mode ListView instead. This can be done in
BCB4 via OwnerData or in previous versions by flagging the
LVS_OWNERDATA style. Although some of the functionality is
lacking in a virtual-mode ListView, this is the fastest
approach.
---
(Q2.3)
My TToolBar images are not showing up on some machines, how
do I resolve this?
(A2.3)
This is likely due to an outdated comctl32.dll version. See
Jeff Overcash's various posts on the subject and the following
MSDN Knowledge Base article:
http://www.deja.com/threadmsg_ct.xp?AN=519822766
http://www.deja.com/threadmsg_ct.xp?AN=556738732
http://support.microsoft.com/support/kb/articles/Q186/1/76.asp
---
(Q2.4)
How do I change the font/color of an individual item in
a TTreeView or TListView?
(A2.4)
Use the Custom Draw (IE3+) service. This can be done in BCB4
via the OnCustomDrawItem event or in earlier versions via a
WM_NOTIFY and NM_CUSTOMDRAW message handler. See the following
reference for the latter implementation:
http://bcbcaq.freeservers.com/CustomDraw_TV.html
---
(Q2.5) How do I place a TProgressBar inside a TStatusBar?
(A2.5a)
Recall, the TControl::Parent property determines which surface
a particular control is displayed on. To have a ProgressBar
inside a StatusBar, assign the latter as the Parent of the
former.
ProgressBar1->Parent = StatusBar1;
Note: The Top and Left properties of the ProgressBar are now
relative to the client area of the StatusBar.
(Q2.5b)
Create a new TStatusBar descendant that includes
csAcceptsControls in its ControlStyle property. Doing this
allows controls to be parented to the StatusBar at design-time.
__fastcall
TMyStatusBar::TMyStatusBar(TComponent *AOwner)
: TStatusBar(AOwner)
{
ControlStyle = ControlStyle << csAcceptsControls;
}
---
(Q2.6) How do I enable multiple selection of nodes in my TTreeView?
(A2.6a)
The underlying WC_TREEVIEW common control does not support
such functionality. Consequently, neither does the TTreeView
class. The easiest approach is to search for a third-party
component that implements this (http://www.torry.ru).
(A2.6b)
The following URL links to a primitive implementation of adding
multi-select functionality to the TTreeView class:
http://www.deja.com/threadmsg_ct.xp?AN=555168670.1
Part
3: Standard Controls
=========================
---
(Q3.1)
How do I supress the default system sound when the Enter key is
pressed in a TEdit?
(A3.1)
You can manipulate the Key parameter in the edit control's
OnKeyPress event handler. This is event is a wrapper around
the WM_CHAR message. Specifically, set the Key parameter to
zero.
void
__fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (Key == VK_RETURN) Key = 0;
}
For a more in-depth discussion, examine the following post:
http://www.deja.com/threadmsg_ct.xp?AN=498742760
---
(Q3.2)
I've changed the Color property of my Button's Font, but it
doesn't seem to work. What's wrong?
(A3.2)
The TButton class is a wrapper for the Windows BUTTON standard
control. Since the standard control itself does not support
any other font color except clBtnText, changing the
TButton::Font::Color property will have no effect. Use a
TBitBtn instead (TBitBtn is owner-drawn implementation of the
BUTTON control).
---
(Q3.3)
How do I change a TButton's Color? I've tried setting the
Brush::Color property, but this does not work.
(A3.3)
The TButton class is a wrapper for the Windows BUTTON standard
control. The only way to change the color of this control is
to make the button owner-drawn via the BS_OWNERDRAW style.
Doing this, however, will require an implementation for drawing
every aspect of the button. Since this is not a trivial task,
it is recommended to search for a third-party component
(http://www.torry.ru). If one is
interested in implementing
this functionality, either start with a TBitBtn or create a
custom "button" control (TCustomControl is a good place to
start). The following posts describe this task:
http://www.deja.com/threadmsg_ct.xp?AN=483185496.1
http://www.deja.com/threadmsg_ct.xp?AN=498626460
http://www.deja.com/threadmsg_ct.xp?AN=513500673
--
(Q3.4)
When I load a large text file in a TRichEdit, I am no longer
able to input any additional text. What's wrong?
(A3.4)
Send the RichEdit the EM_EXLIMITTEXT message before loading the
file, to set a new upper limit (in characters). See Robert
Dunn's FAQ section for more information:
http://home.att.net/~robertdunn/Yacs.html
---
(Q3.5) How do I select an item in a TComboBox or TListBox via code?
(A3.5)
Set the ItemIndex property to the zero-based index of the item
that you want selected. For multiple-selection ListBoxes, use
the Selected property.
---
(Q3.6) I
have used the Selected property to set the selected item in
my TListBox, but it raises an EListError exception. What's the
problem?
(A3.6) The
Selected property only applies to ListBoxes with the
MultiSelect property set to true. For single selection
ListBoxes, use the ItemIndex property as described in (A3.5).
---
(Q3.7) How
can I determine which items are selected in a
multi-select TListBox?
(A3.7) Use
the TListBox::Selected property. This an array of booleans
indicating the selected state of each item. In an iterative
implementation, this is best used in conjunction with
the SelCount property. Note: the Selected property only applies
to multiple-selection ListBoxes (see also A3.6).
---
(Q3.8)
How do I determine the position of a TRichEdit's caret in a
(row, col) format?
(A3.8)
Use the EM_EXLINEFROMCHAR and EM_LINEINDEX messages to retrieve
this information.
int
col = RichEdit1->SelStart;
int
row = SNDMSG(RichEdit1->Handle, EM_EXLINEFROMCHAR,
0, static_cast<LPARAM>(col));
col
= col - SNDMSG(RichEdit1->Handle, EM_LINEINDEX, row, 0);
---
(Q3.9)
I have provided a handler for the OnClick event of my CheckBox,
however, I've noticed that this handler is fired when I
manipulate the TCheckBox::Checked property. Is this a bug?
(A3.9)
This is by design (see the TCustomCheckBox::SetState method
in the stdcltrs.pas file). There are, however, a few ways
around this behavior as described in the following posts:
http://www.deja.com/threadmsg_ct.xp?AN=569313536
http://www.deja.com/threadmsg_ct.xp?AN=569335452
---
(Q4.1)
I've created a control dynamically using the following code,
but it fails to appear. What's wrong?
TShape
*Shape = new TShape(Panel1);
Shape->Top
= 20;
Shape->Left
= 30;
Shape->Visible
= true;
(A4.1)
The above code is missing the Parent property assignment.
Among other things, the Parent property is used to determine
which windowed control's surface to draw a particular child
control on. Note: The Top and Left properties of the child
control are relative to its Parent.
Shape->Parent
= Panel1;
---
(Q4.2) How do I assign an event handler at run-time?
(A4.2)
Examine the signature of the target event, create a member
function of the same signature, then assign this handler to the
desired event in the same way a property value is specified.
//
in header...
TSpeedButton *SpeedButton1;
void __fastcall SpeedButtonClick(TObject *Sender);
//
in source...
__fastcall
TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
SpeedButton1 = new TSpeedButton(this);
SpeedButton1->Parent = this;
SpeedButton1->OnClick = SpeedButtonClick;
}
void
__fastcall TForm1::SpeedButtonClick(TObject *Sender)
{
ShowMessage("you clicked it!");
}
---
(Q4.3)
How do I change the font/background color of a particular cell
in a TStringGrid?
(A4.3)
Set the DefaultDrawing property to false, then implement a
handler for the OnDrawCell event.
void
__fastcall TForm1::StringGrid1DrawCell(TObject *Sender,
int Col, int Row, TRect &Rect, TGridDrawState State)
{
// if it's a fixed row (headers)
if (State.Contains(gdFixed))
{
StringGrid1->Canvas->Brush->Color = clBtnFace;
StringGrid1->Canvas->Font->Color = clWindowText;
StringGrid1->Canvas->FillRect(Rect);
Frame3D(StringGrid1->Canvas, Rect,
clBtnHighlight, clBtnShadow, 1);
}
// if the cell is selected
else if (State.Contains(gdSelected))
{
StringGrid1->Canvas->Brush->Color = clHighlight;
StringGrid1->Canvas->Font->Color = clHighlightText;
StringGrid1->Canvas->FillRect(Rect);
}
// color cell (2, 2) only
else if (Col == 2 && Row == 2)
{
StringGrid1->Canvas->Brush->Color = clBlue;
StringGrid1->Canvas->Font->Color = clRed;
StringGrid1->Canvas->FillRect(Rect);
}
// if normal
else
{
StringGrid1->Canvas->Brush->Color = StringGrid1->Color;
StringGrid1->Canvas->Font->Color = StringGrid1->Font->Color;
StringGrid1->Canvas->FillRect(Rect);
}
AnsiString text = StringGrid1->Cells[Col][Row];
StringGrid1->Canvas->TextRect(Rect, Rect.Left, Rect.Top, text);
}
---
(Q4.4) How do I insert/remove a row or column in a TStringGrid?
(A4.4)
Implement this functionality manually by manipulating the
RowCount or ColCount property, then shifting the contents of
the surrounding cells. The following function can be used to
insert a row. Inserting a column, or deleting a row or column
can be accomplished via a similar technique.
void
__fastcall InsertRow(TStringGrid *StringGrid, long AfterIndex)
{
SNDMSG(StringGrid->Handle, WM_SETREDRAW, false, 0);
try
{
int row_count = StringGrid->RowCount;
StringGrid->RowCount = row_count + 1;
for (int row = row_count; row > AfterIndex + 1; row--)
StringGrid->Rows[row] = StringGrid->Rows[row - 1];
StringGrid->Rows[AfterIndex + 1]->Clear();
}
catch (...)
{
SNDMSG(StringGrid->Handle, WM_SETREDRAW, true, 0);
}
SNDMSG(StringGrid->Handle, WM_SETREDRAW, true, 0);
RECT R = StringGrid->CellRect(0, AfterIndex);
InflateRect(&R, StringGrid->Width, StringGrid->Height);
InvalidateRect(StringGrid->Handle, &R, false);
}
---
(Q4.5)
I've assigned an OnExit event handler for a particular control,
however, it does not fire when I click a TSpeedButton. What's
the problem?
(A4.5)
TSpeedButton is a TGraphicControl descendant. Since
TGraphicControls can never truly receive keyboard focus, and
the OnExit event of a windowed control is fired when that
control loses keyboard focus, clicking the TSpeedButton will
not fire the OnExit event. This also helps explain why
TGraphicControl descendants do not exhibit OnEnter and OnExit
events. The easiest solution in this case is to use a TBitBtn
instead of a TSpeedButton.
---
(Q4.6)
I've changed the Color property of a TPaintBox, but it seems
to have no effect. What could be wrong?
(A4.6)
The TPaintBox component is little more than a fancy visual
TControlCanvas. The TControlCanvas in this case simply
encaptulates the process of grabbing the device context of the
PaintBox's Parent, then selecting the appropriate clipping
region, defined by the bounds of the PaintBox, into this
device context. As the name suggests, anything that is to be
displayed in the PaintBox must be "painted". To fill a
PaintBox, use the FillRect() method of the PaintBox's Canvas
property.
PaintBox1->Color
= clBlue;
PaintBox1->Canvas->FillRect(PaintBox1->Canvas->ClipRect);
---
(Q4.7)
How can I detect when the mouse cursor enters or leaves a
control?
(A4.7)
Handle the CM_MOUSEENTER and CM_MOUSELEAVE VCL messages to
determine when the mouse cursor enters or leaves the bounds
of a VCL control. This can be done via a subclass procedure
or by creating a descendant of the target control and
augmenting the WndProc() method (or via a message map).
//
in header...
Controls::TWndMethod OldLabelWP;
void __fastcall NewLabelWP(TMessage &Msg);
//
in source...
__fastcall
TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
OldLabelWP = Label1->WindowProc;
Label1->WindowProc = NewLabelWP;
}
void
__fastcall TForm1::NewLabelWP(TMessage &Msg)
{
switch (Msg.Msg)
{
case CM_MOUSEENTER:
{
// mouse cursor has entered control
break;
}
case CM_MOUSELEAVE:
{
// mouse cursor has left control
break;
}
case WM_DESTROY:
{
Label1->WindowProc = OldLabelWP;
break;
}
}
OldLabelWP(Msg);
}
---
(Q4.8)
When I switch my StringGrid into EditorMode, the caret does not
appear in the cell, although I can still input text. What's
wrong here?
(A4.8)
[From Jonathan Arnold]: If the font height is too tall for the
cell, the input caret will not appear. Adjust either the size
of the font, or the StringGrid's DefaultRowHeight property.
---
(1) Components Sites:
o
Code Corner:
<http://www.codecorner.com/>
o
Torry's Delphi Pages:
<http://www.torry.ru/>
o
Delphi Super Page:
<http://sunsite.icm.edu.pl/delphi/>
o
Richey's Delphi Box:
<http://Inner-Smile.com>
o
The Delphi Deli:
<http://www.delphix.com/>
o Joe C. Hecht's
Printing Components:
<http://home1.gte.net/joehecht/index.htm>
---
(2) C++ Builder Reference:
o
Borland Community Site:
<http://community.borland.com>
o
BCBDEV.COM (components and FAQs):
<http://www.bcbdev.com/>
o
Too many others to list -- see the following webrings for
a comprehensive list:
The
C++ Builder Programmer's Ring maintained by Mark Cashman:
<http://www.webring.org/cgi-bin/webring?ring=cbuilder&list>
Borland
C++ Builder Webring maintained by Emilio Alberto:
<http://www.webring.org/cgi-bin/webring?ring=bcbwebring;list>
o
Borland C++Builder 4 Unleashed (K. Reisdorph, C. Calvert):
ISBN: 0672315106
o
Teach Yourself Borland C++ Builder 3 in 21 Days (K. Reisdorph):
ISBN: 0672312662
o
C++ Builder How-To (J. Miano, T. Cabanski, H. Howe)
ISBN: 157169109X
---
(3) Windows API Reference:
o
Microsoft Developers Network:
<http://msdn.microsoft.com>
o
Windows API Guide (VB targeted):
<http://skyscraper.fortunecity.com/transmission/45/api/>
o
CodeGuru (MFC targeted):
<http://www.codeguru.com>
o
Programming Windows 95 (C. Petzold):
ISBN: 1556156766
o
Programming Windows (C. Petzold):
ISBN: 157231995X
o
Advanced Windows (J. Richter):
ISBN: 1572315482
o
Win32 Programming (Rector & Newcomer)
ISBN: 0201634929
---
(4) C++ References:
o
C++ FAQ Lite (M. Cline):
<http://www.cerfnet.com/~mpcline/c++-faq-lite/>
o
Thinking in C++ (B. Eckel, online book):
<http://www.bruceeckel.com/ThinkingInCPP2e.html>
o
C++ How to Program (H. Deitel, P. Deitel):
ISBN: 0135289106
o
C++ Primer (S. Lippman, J. Lajoie):
ISBN: 0201824701
o
The C++ Programming Language (B. Stroustrup):
ISBN: 0201889544
---
Send suggestions, corrections, and/or comments to: dmc27@ee.cornell.edu
///////////////////////////////////////////////////////////////////////
[EOF:All -- Last Modified: 04/17/2000]