1. Trang chủ
  2. » Công Nghệ Thông Tin

MASTERING DELPHI 6 phần 3 ppsx

108 236 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 108
Dung lượng 671,88 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The power of this control lies in the fact you can customize the editing options for eachposition of the grid or for each key value, using the run-time-only ItemPropsarray property.For e

Trang 1

When you set a font, either by entering values for the attributes of the property in theObject Inspector or by using the standard font selection dialog box, you can choose one ofthe fonts installed in the system The fact that Delphi allows you to use all the fonts installed

on your system has both advantages and drawbacks The main advantage is that if you have anumber of nice fonts installed, your program can use any of them The drawback is that ifyou distribute your application, these fonts might not be available on your users’ computers

If your program uses a font that your user doesn’t have, Windows will select some otherfont to use in its place A program’s carefully formatted output can be ruined by the font sub-stitution For this reason, you should probably rely only on standard Windows fonts (such as

MS Sans Serif, System, Arial, Times New Roman, and so on)

Colors

There are various ways to set the value of a color The type of this property is TColor Forproperties of this type, you can choose a value from a series of predefined name constants orenter a value directly The constants for colors include clBlue, clSilver, clWhite, clGreen,clRed, and many others

TIP Delphi 6 adds four new standard colors: clMoneyGreen, clSkyBlue, clCream, and clMedGray.

As a better alternative, you can use one of the colors used by the system to denote the tus of given elements These sets of colors are different in VCL and CLX VCL includes pre-defined Windows colors such as the background of a window (clWindow), the color of the text

sta-of a highlighted menu (clHightlightText), the active caption (clActiveCaption), and the tous button face color (clBtnFace)

ubiqui-CLX includes a different and incompatible set of system colors, including clBackground,which is the standard color of a form; clBase, used by edit boxes and other visual controls;clActiveForeground, the foreground color for active controls; and clDisabledBase, the back-ground color for disabled text controls All the color constants mentioned here are listed inVCL and CLX Help files under the “TColor type” topic

Another option is to specify a TColoras a number (a 4-byte hexadecimal value) instead ofusing a predefined value If you use this approach, you should know that the low three bytes

of this number represent RGB color intensities for blue, green, and red, respectively Forexample, the value $00FF0000 corresponds to a pure blue color, the value $0000FF00 togreen, the value $000000FF to red, the value $00000000 to black, and the value $00FFFFFF

to white By specifying intermediate values, you can obtain any of 16 million possible colors.Instead of specifying these hexadecimal values directly, you should use the Windows RGBfunction, which has three parameters, all ranging from 0 to 255 The first indicates theamount of red, the second the amount of green, and the last the amount of blue Using theRGBfunction makes programs generally more readable than using a single hexadecimal

TControl and Derived Classes

Trang 2

constant Actually, RGBis almost a Windows API function It is defined by the

Windows-related units and not by Delphi units, but a similar function does not exist in the WindowsAPI In C, there is a macro that has the same name and effect, so this is a welcome addition

to the Pascal interface to Windows RGBis not available on CLX, so I’ve written my own sion as:

ver-function RGB (red, green, blue: Byte): Cardinal;

In Windows, most elements of the user interface are windows From a user standpoint, awindow is a portion of the screen surrounded by a border, having a caption and usually a sys-tem menu But technically speaking, a window is an entry in an internal system table, oftencorresponding to an element visible on the screen that has some associated code Most ofthese windows have the role of controls; others are temporarily created by the system (forexample, to show a pull-down menu) Still other windows are created by the application butremain hidden from the user and are used only as a way to receive a message (for example,nonblocking sockets use windows to communicate with the system)

The common denominator of all windows is that they are known by the Windows systemand refer to a function for their behavior; each time something happens in the system, a noti-fication message is sent to the proper window, which responds by executing some code Each

window of the system, in fact, has an associated function (generally called its window

procedure), which handles the various messages the window is interested in.

In Delphi, any TWinControlclass can override the WndProcmethod or define a new valuefor the WindowProcproperty Interesting Windows messages, however, can be better tracked

by providing specific message handlers Even better, VCL converts these lower-level sages into events In short, Delphi allows us to work at a high level, making applicationdevelopment easier, but still allows us to go low-level when this is required

Trang 3

Notice also that creating a WinControl doesn’t automatically create its correspondingWindow handle Delphi, in fact, uses a lazy initialization technique, so that the low control isonly created when this is required, generally as soon as a method accesses the Handleprop-erty The get method for this property the first time calls HandleNeeded, which eventuallycalls CreateHandle… and so on reaching CreateWnd, CreateParams, and CreateWindowHandle(the sequence is rather complex, and I don’t think it is necessary to know it in detail) At theopposite end, you can keep an existing (perhaps invisible) control in memory but destroy itswindow handle, to save system resources

In CLX, every TWidgetControlhas an internal Qt object, referenced using the Handleerty This property has the same name as the corresponding Windows property, but it istotally different behind the scenes

prop-The Qt object is generally owned by the TWidgetControl, which automatically frees theobject when it is destroyed The class also uses delayed construction, as you can see in theInitWidgetmethod, similar to CreateWindow However it is also possible to create a widgetaround an existing Qt object: in this case, the widget won’t own the Qt object and won’tdestroy it The behavior is indicated by the OwnHandleproperty

Actually each VisualCLX component has two associated C++ objects, the Qt Handleandthe Qt Hook, which is the object receiving the system events With the current Qt design, thishas to be a C++ object, which acts as an intermediary to the event handlers of the Object Pas-cal control The HookEventsmethod associates the hook object to the CLX control

Differently from Windows, Qt defines two different types of events:

Events are the translation of input or system events (such as key press, mouse move, and

paint)

Signals are internal component events (corresponding to VCL internal or abstract

operations, such as OnClickand OnChange)

NOTE In CLX there is a seldom-used EventHandler method, which corresponds more or less to the

WndProc method of VCL.

Opening the Component Tool Box

So you want to write a Delphi application You open a new Delphi project and find yourselffaced with a large number of components The problem is that for every operation, there aremultiple alternatives For example, you can show a list of values using a list box, a combo box,

Opening the Component Tool Box

Trang 4

a radio group, a string grid, a list view, or even a tree view if there is a hierarchical order.Which should you use? That’s difficult to say There are many considerations, depending onwhat you want your application to do For this reason, I’ve provided a highly condensedsummary of alternative options for a few common tasks.

NOTE For some of the controls described in the following sections, Delphi also includes a data-aware

version, usually indicated by the DB prefix As you’ll see in Chapter 13, “Delphi’s Database

Architecture,” the DB version of a control typically serves a role similar to that of its dard” equivalent; but the properties and the ways you use it are often quite different For example, in an Edit control you use the Text property, while in a DBEdit component you access the Value of the related field object.

“stan-The Text Input Components

Although a form or component can handle keyboard input directly, using the OnKeyPressevent, this isn’t a common operation Windows provides ready-to-use controls you can use toget string input and even build a simple text editor Delphi has several slightly different com-ponents in this area

The Edit Component

The Edit component allows the user to enter a single line of text You can also display a singleline of text with a Label or a StaticText control, but these components are generally usedonly for fixed text or program-generated output, not for input In CLX, there is also a nativeLCD digit control you can use to display numbers

The Edit component uses the Textproperty, whereas many other controls use the Captionproperty to refer to the text they display The only condition you can impose on user input isthe number of characters to accept If you want to accept only specific characters, you canhandle the OnKeyPressevent of the edit box For example, we can write a method that testswhether the character is a number or the Backspace key (which has a numerical value of 8)

If it’s not, we change the value of the key to the null character (#0), so that it won’t be

processed by the edit control and will produce a warning beep:

procedure TForm1.Edit1KeyPress(

Sender: TObject; var Key: Char);

begin

// check if the key is a number or backspace

if not (Key in [‘0’ ’9’, #8]) then

Trang 5

NOTE A minor difference of CLX is that the Edit control has no Undo mechanism built in Another is

that the PasswordChar property is replaced by the EchoMode property You don’t determine

the character to display, but whether to echo the entered text or display an asterisk instead.

The New LabeledEdit Control

Delphi 6 adds a very nice control, called LabeledEdit, which is an Edit control with a labelattached to it The Label appears as a property of the compound control, which inherits fromTCustomEdit

I have to say this component is very handy, because it allows you to reduce the number ofcomponents on your forms, move them around more easily, and have a more standard layoutfor labels, particularly when they are placed above the edit box The EditLabelproperty isconnected with the subcomponent, which has the usual properties and events Two moreproperties, LabelPositionand LabelSpacing, allow you to configure the relative positions ofthe two controls

NOTE This component has been added to the ExtCtrls unit to demonstrate the use of

subcompo-nents in the Object Inspector, which is a new feature of Delphi 6 I’ll discuss the development

of these components in Chapter 11, “Creating Components.” Notice also that this nent, along with all of the other new Delphi 6 components, is not (yet) available on CLX and

compo-on the first release of Kylix However, we can expect all ncompo-on–Windows-specific additicompo-ons to VCL, including subcomponents in general and the LabeledEdit control in particular, to be avail- able in the next release of Kylix.

The MaskEdit Component

To customize the input of an edit box further, you can use the MaskEdit component, whichhas an EditMaskproperty This is a string indicating for each character whether it should beuppercase, lowercase, or a number, and other similar conditions You can see the editor of theEditMaskproperty in Figure 6.3

F I G U R E 6 3 :

The MaskEdit component’s

EditMask property editor

Opening the Component Tool Box

Trang 6

TIP You can display any property’s editor by selecting the property in the Object Inspector and

clicking the ellipsis (…) button.

The Input Mask editor allows you to enter a mask, but it also asks you to indicate a

charac-ter to be used as a placeholder for the input and to decide whether to save the licharac-terals present

in the mask, together with the final string For example, you can choose to display the theses around the area code of a phone number only as an input hint or to save them with thestring holding the resulting number These two entries in the Input Mask editor correspond

paren-to the last two fields of the mask (separated by semicolons)

TIP Clicking the Masks button of the Mask Editor lets you choose predefined input masks for

dif-ferent countries.

The Memo and RichEdit Components

Both of the controls discussed so far allow a single line of input The Memo component, bycontrast, can host several lines of text but (on the Win95/98 platforms) still retains the 16-bitWindows text limit (32 KB) and allows only a single font for the entire text You can work onthe text of the memo line by line (using the Linesstring list) or access the entire text at once(using the Textproperty)

If you want to host a large amount of text or change fonts and paragraph alignments, in VCLyou should use the RichEdit control, a Win32 common control based on the RTF documentformat You can find an example of a complete editor based on the RichEdit component amongthe sample programs that ship with Delphi (The example is named RichEdit, too.)

The RichEdit component has a DefAttributesproperty indicating the default styles and aSelAttributesproperty indicating the style of the current selection These two propertiesare not of the TFonttype, but they are compatible with fonts, so we can use the Assignmethod to copy the value, as in the following code fragment:

procedure TForm1.Button1Click(Sender: TObject);

Trang 7

The TextViewer CLX Control

Among all of the common controls, CLX and Qt lack a RichEdit control However, theyprovide a full-blown HTML viewer, which is very powerful for displaying formatted text butnot for typing it This HTML viewer is embedded in two different controls, the single-pageTextViewer control or the TextBrowser control with active links

As a simple demo, I’ve added a memo and a text viewer to a CLX form and connectedthem so that everything you type on the memo is immediately displayed in the viewer I’vecalled the example HtmlEdit not because this is a real HTML editor, but because this is thesimplest way I know of to build an HTML preview inside a program The form of the pro-gram is visible at run time in Figure 6.4, while typing some text inside a cell of the table

TIP I originally built this example with Kylix on Linux To port it to Windows and Delphi 6, all I had

to do was to copy the files and recompile.

Selecting Options

There are two standard Windows controls that allow the user to choose different options, aswell as controls for grouping sets of options

F I G U R E 6 4 :

The HtmlEdit example at

run time: when you add

new HTML text to the

memo, you get an

immediate preview.

Opening the Component Tool Box

Trang 8

The CheckBox and RadioButton Components

The first standard option-selecting control is the check box, which corresponds to an option

that can be selected regardless of the status of other check boxes Setting the AllowGrayedproperty of the check box allows you to display three different states (selected, not selected,and grayed), which alternate as a user clicks the check box

The second type of control is the radio button, which corresponds to an exclusive selection.

Two radio buttons on the same form or inside the same radio group container cannot beselected at the same time, and one of them should always be selected (as programmer, youare responsible for selecting one of the radio buttons at design time)

The GroupBox Components

To host several groups of radio buttons, you can use a GroupBox control to hold themtogether, both functionally and visually To build a group box with radio buttons, simplyplace the GroupBox component on a form and then add the radio buttons to the group box.You can handle the radio buttons individually, but it’s easier to navigate through the array

of controls owned by the group box, as discussed in the previous chapter Here is a smallcode excerpt used to get the text of the selected radio button of a group:

if (GroupBox1.Controls[I] as TRadioButton).Checked then

Text := (GroupBox1.Controls[I] as TRadioButton).Caption;

The RadioGroup Component

Delphi has a similar component that can be used specifically for radio buttons: the RadioGroup

component A RadioGroup is a group box with some radio button clones painted inside it The term clone in this context refers to the fact that the RadioGroup component is a single

control, a single window, with elements similar to radio buttons painted on its surface.Using the radio group is generally easier than using the group box, since the various itemsare part of a list, as in a list box This is how you can get the text of the selected item:

Text := RadioGroup1.Items [RadioGroup1.ItemIndex];

Technically, a RadioGroup uses fewer resources and less memory, and it should be faster tocreate and paint Also, the RadioGroup component can automatically align its radio buttons

in one or more columns (as indicated by the Columnsproperty), and you can easily add newchoices at run time, by adding strings to the Itemsstring list By contrast, adding new radiobuttons to a group box would be quite complex

Trang 9

Lists

When you have many selections, radio buttons are not appropriate The usual number ofradio buttons is no more than five or six, to avoid cluttering the user interface; when youhave more choices, you can use a list box or one of the other controls that display lists ofitems and allow the selection of one of them

The ListBox Component

The selection of an item in a list box uses the Itemsand ItemIndexproperties as in the codeshown above for the RadioGroup control If you need access to the text of selected list boxitems often, you can write a small wrapper function like this:

function SelText (List: TListBox): string;

list boxes: multiple selection and extended selection In the first case, a user selects multiple items

simply by clicking them, while in the second case the user can use the Shift and Ctrl keys toselect multiple consecutive or nonconsecutive items, respectively This second choice isdetermined by the ExtendedSelectproperty

For a multiple-selection list box, a program can retrieve information about the number ofselected items by using the SelCountproperty, and it can determine which items are selected

by examining the Selectedarray This array of Boolean values has the same number of entries

as the list box For example, to concatenate all the selected items into a string, you can scanthe Selectedarray as follows:

for nItem := 0 to ListBox1.Items.Count - 1 do

if ListBox1.Selected [nItem] then

SelItems := SelItems + ListBox1.Items[nItem] + ‘ ‘;

Opening the Component Tool Box

Trang 10

In CLX the ListBox can be configured to use a fixed number of columns and rows, usingthe Columns, Row, ColumnLayoutand RowLayoutproperties Of these, the VCL ListBox hasonly the Columnsproperty.

The ComboBox Component

List boxes take up a lot of screen space, and they offer a fixed selection—that is, a user canchoose only among the items in the list box and cannot enter any choice that the program-mer did not specifically foresee

You can solve both problems by using a ComboBox control, which combines an edit boxand a drop-down list The behavior of a ComboBox component changes a lot depending onthe value of its Styleproperty:

• The csDropDown style defines a typical combo box, which allows direct editing anddisplays a list box on request

• The csDropDownList style defines a combo box that does not allow editing (but usesthe keystrokes to select an item)

• The csSimple style defines a combo box that always displays the list box below it.Note also that accessing the text of the selected value of a ComboBox is easier than doingthe same operation for a list box, since you can simply use the Textproperty A useful andcommon trick for combo boxes is to add a new element to the list when a user enters sometext and presses the Enter key The following method first tests whether the user has pressedthat key, by looking for the character with the numeric (ASCII) value of 13 It then tests tomake sure the text of the combo box is not empty and is not already in the list—if its position

in the list is less than zero Here is the code:

NOTE In CLX, the combo box can automatically add the text typed into the edit to the drop-down

list, when the user presses the Enter key Also, some events fire at different times than in VCL.

Trang 11

Delphi 6 includes two new events for the combo box The OnCloseUpevent corresponds tothe closing of the drop-down list and complements the preexisting OnDropDownevent TheOnSelectevent fires only when the user selects something in the drop-down list, as opposed

to typing in the edit portion

Another very nice addition is the AutoCompleteproperty When it is set, the ComboBoxcomponent (and the ListBox, as well) automatically locates the string nearest to the one theuser is entering, suggesting the final part of the text The core of this feature, available also inCLX, is implemented in the TCustomListBox.KeyPressmethod

The CheckListBox Component

Another extension of the list box control is represented by the CheckListBox component, alist box with each item preceded by a check box (as you can see in Figure 6.5) A user canselect a single item of the list, but can also click the check boxes to toggle their status Thismakes the CheckListBox a very good component for multiple selections or for highlightingthe status of a series of independent items (as in a series of check boxes)

To check the current status of each item, you can use the Checkedand the Statearrayproperties (use the latter if the check boxes can be grayed) Delphi 5 introduced the Item- Enabledarray property, which you can use to enable or disable each item of the list We’ll usethe CheckListBox in the DragList example, later in this chapter

Trang 12

TIP Most of the list-based controls share a common and important feature Each item of the list

has an associated 32-bit value, usually indicated by the TObject type This value can be used

as a tag for each list item, and it’s very useful for storing additional information along with each item This approach is connected to a specific feature of the native Windows list box con- trol, which offers four bytes of extra storage for each list box item We’ll use this feature in the ODList example later on in this chapter.

New Combo Boxes: ComboBoxEx and ColorBox

The ComboBoxEx (where ex stands for extended) is the wrapper of a new Win32 common

controls, which extends the traditional combo box by allowing images to appear next to theitems in the list You attach an image list to the combo, and then select an image index foreach item to display The effect of this change is that the simple Itemsstring list is replaced

by a more complex collection, the ItemsExproperty

The ColorBox control is a new version of the combo box specifically aimed at selecting ors You can use its Styleproperty for choosing which groups of colors you want to see inthe list (standard color, extended colors, system colors, and so on)

col-The ListView and TreeView Components

If you want an even more sophisticated list, you can use the ListView common control, whichwill make the user interface of your application look very modern This component is slightlymore complex to use, as described at the beginning of the next chapter, “Advanced VCL Con-trols.” Other alternatives for listing values are the TreeView common control, which showsitems in a hierarchical output, and the StringGrid control, which shows multiple elements foreach line The string grid control is described in the “Graphics in Delphi” bonus chapter,available on the companion CD

If you use the common controls in your application, users will already know how to interactwith them, and they will regard the user interface of your program as up to date TreeViewand ListView are the two key components of Windows Explorer, and you can assume thatmany users will be familiar with them, even more than with the traditional Windows controls.CLX adds also an IconView control, which parallels part of the features of the VCL ListView

The New ValueListEditor Component

Delphi applications often use the name/value structure natively offered by string lists, which Idiscussed in the last chapter Delphi 6 introduces a version of the StringGrid component specif-ically geared towards this type of string lists The ValueListEditor has two columns where youcan display and let the user edit the contents of a string list with name/value pairs, as you cansee in Figure 6.6 This string list is indicated in the Stringsproperty of the control

Trang 13

The power of this control lies in the fact you can customize the editing options for eachposition of the grid or for each key value, using the run-time-only ItemPropsarray property.For each item, you can indicate:

• Whether it is read-only

• The maximum number of characters of the string

• An edit mask (eventually requested in the OnGetEditMaskevent)

• The items of a drop-down pick list (eventually requested in the OnGetPickListevent)

• The display of a button for showing an editing dialog (in the OnEditButtonClickevent)Needless to say, this behavior resembles what is available generally for string grids and theDBGrid control, but also the behavior of the Object Inspector

The ItemPropsproperty has to be set up at run time, by creating an object of the TItemPropclass and assigning it to an index or a key of the string list To have a default editor for eachline, you can assign the same item property object multiple times In the example, this sharededitor sets an edit mask for up to three numbers:

procedure TForm1.FormCreate(Sender: TObject);

var

I: Integer;

F I G U R E 6 6 :

The NameValues example

has the new ValueListEditor

component, which shows

the name/value or key/

value pairs of a string list,

visible also in a plain memo.

Opening the Component Tool Box

Trang 14

if not Assigned (ValueListEditor1.ItemProps [I]) then

ValueListEditor1.ItemProps [I] := SharedItemProp;

end;

NOTE Apparently reassigning the same editor twice causes some trouble, so I’ve assigned the editor

only to the lines not having already one.

Another property, KeyOptions, allows you to let the user also edit the keys (the names), addnew entries, delete existing ones, and allow for duplicated names in the first portion of the string.Oddly enough, you cannot add new keys unless you also activate the edit options, which makes ithard to let the user add extra entries while preserving the names of the basic ones

Ranges

Finally, there are a few components you can use to select values in a range Ranges can beused for numeric input and for selecting an element in a list

The ScrollBar Component

The stand-alone ScrollBar control is the original component of this group, but it is seldomused by itself Scroll bars are usually associated with other components, such as list boxes andmemo fields, or are associated directly with forms In all these cases, the scroll bar can beconsidered part of the surface of the other components For example, a form with a scroll bar

is actually a form that has an area resembling a scroll bar painted on its border, a feature

gov-erned by a specific Windows style of the form window By resembling, I mean that it is not

technically a separate window of the ScrollBar component type These “fake” scroll bars areusually controlled in Delphi using specific properties of the form and the other componentshosting them

Trang 15

The TrackBar and ProgressBar Components

Direct use of the ScrollBar component is quite rare, especially with the TrackBar componentintroduced with Windows 95, which is used to let a user select a value in a range AmongWin32 common controls is the companion ProgressBar control, which allows the program

to output a value in a range, showing the progress of a lengthy operation

The UpDown Component

Another related control is the UpDown component, which is usually connected to an edit box

so that the user can either type a number in it or increase and decrease the number using thetwo small arrow buttons To connect the two controls, you set the Associateproperty of theUpDown component Nothing prevents you from using the UpDown component as a stand-alone control, displaying the current value in a label or in some other way

NOTE In CLX there is no UpDown control, but a SpinEdit that bundles an Edit with the UpDown in a

single control.

The PageScroller Component

The Win32 PageScroller control is a container allowing you to scroll the internal control Forexample, if you place a toolbar in the page scroller and the toolbar is larger than the availablearea, the PageScroller will display two small arrows on the side Clicking these arrows willscroll the internal area This component can be used as a scrollbar, but it also partially replacesthe ScrollBox control

The ScrollBox Component

The ScrollBox control represents a region of a form that can scroll independently from therest of the surface For this reason, the ScrollBox has two scrollbars used to move the embed-ded components You can easily place other components inside a ScrollBox, as you do with apanel In fact, a ScrollBox is basically a panel with scroll bars to move its internal surface, aninterface element used in many Windows applications When you have a form with manycontrols and a toolbar or status bar, you might use a ScrollBox to cover the central area of theform, leaving its toolbars and status bars outside of the scrolling region By relying on thescrollbars of the form, in fact, you might allow the user to move the toolbar or status bar out

of view, a very odd situation

Handling the Input Focus

Using the TabStopand TabOrderproperties available in most controls, you can specify theorder in which controls will receive the input focus when the user presses the Tab key

Opening the Component Tool Box

Trang 16

Instead of setting the tab order property of each component of a form manually, you can usethe shortcut menu of the Form Designer to activate the Edit Tab Order dialog box, as shown

in Figure 6.7

Besides these basics settings, it is important to know that each time a component receives

or loses the input focus, it receives a corresponding OnEnteror OnExitevent This allows you

to fine-tune and customize the order of the user operations Some of these techniques aredemonstrated by the InFocus example, which creates a fairly typical password-login window.Its form has three edit boxes with labels indicating their meaning, as shown in Figure 6.8 Atthe bottom of the window is a status area with prompts guiding the user Each item needs to

be entered in sequence

For the output of the status information, I’ve used the StatusBar component, with a singleoutput area (obtained by setting its SimplePanelproperty to True) Here is a summary of theproperties for this example Notice the &character in the labels, indicating a shortcut key,

Trang 17

object Label1: TLabel

Caption = ‘&First name’

object Label2: TLabel

Caption = ‘&Last name’

procedure TFocusForm.GlobalEnter(Sender: TObject);

var

I: Integer;

begin

for I := 0 to ControlCount - 1 do

// if the control is a label

Opening the Component Tool Box

Trang 18

if (Controls [I] is TLabel) and

// and the label is connected to the current edit box

(TLabel(Controls[I]).FocusControl = Sender) then

// copy the text, leaving off the initial & character

procedure TFocusForm.EditFirstNameExit(Sender: TObject);

TIP The CLX version of this example has exactly the same code and is available as the QInFocus

program The same happens for most of the other examples of this chapter Notice that some

of the examples are quite complex, but I rarely had to touch the code at all.

Working with Menus

Working with menus and menu items is generally quite simple This section offers only somevery brief notes and a few more advanced examples The first thing to keep in mind aboutmenu items is that they can serve different purposes:

Commands are menu items used to execute an action

State-setters are menu items used to toggle an option on and off, to change the state of aparticular element These commands usually have a check mark on the left to indicate they

Trang 19

Dialog menu items cause a dialog box to appear and are usually indicated by an ellipsis(three dots) after the text.

As you enter new elements in the Menu Designer, Delphi creates a new component for eachmenu item and lists it in the Object Inspector (although nothing is added to the form) To

name each component, Delphi uses the caption you enter and appends a number (so that Open

becomes Open1) Because Delphi removes spaces and other special characters in the captionwhen it creates the name, and the menu item separators are set up using a hyphen as caption,

these items would have an empty name For this reason Delphi adds the letter N to the name,

appending the number and generating items called N1, N2, and so on

WARNING Do not use the Break property, which is used to lay out a pull-down menu on multiple

columns The mbMenuBarBreak value indicates that this item will be displayed in a second or subsequent line; the mbMenuBreak value that this item will be added to a second or subse- quent column of the pull-down.

Accelerator Keys

Since Delphi 5, you don’t need to enter the &character in the Captionof a menu item; it vides an automatic accelerator key if you omit one Delphi’s automatic accelerator-key systemcan also figure out if you have entered conflicting accelerator keys and fix them on-the-fly.This doesn’t mean you should stop adding custom accelerator keys with the &character,because the automatic system simply uses the first available letter, and it doesn’t follow thedefault standards You might also find better mnemonic keys than those chosen by the auto-matic system

pro-This feature is controlled by the AutoHotkeysproperty, which is available in the mainmenu component and in each of the pull-down menus and menu items In the main menu,this property defaults to maAutomatic, while in the pull-downs and menu items it defaults tomaParent, so that the value you set for the main menu component will be used automatically

by all the subitems, unless they have a specific value of maAutomatic or maManual

The engine behind this system is the RethinkHotkeysmethod of the TMenuItemclass, andthe companion InternalRethinkHotkeys There is also a RethinkLinesmethod, which

Working with Menus

Trang 20

checks whether a pull-down has two consecutive separators or begins or ends with a tor In all these cases, the separator is automatically removed.

separa-One of the reasons Delphi includes this feature is the Integrated Translation Environment(ITE) When you need to translate the menu of an application, it is convenient if you don’thave to deal with the accelerator keys, or at least if you don’t have to worry about whethertwo items on the same menu conflict Having a system that can automatically resolve similarproblems is definitely an advantage Another motivation was Delphi’s IDE itself With all thedynamically loaded packages that install menu items in the IDE main menu or in pop-up menus,and with different packages loaded in different versions of the product, it’s next to impossible toget nonconflicting accelerator-key selections in each menu That is why this mechanism isn’t awizard that does static analysis of your menus at design time; it was created to deal with the realproblem of managing menus created dynamically at run time

WARNING This feature is certainly very handy, but because it is active by default, it can break existing code.

I had to modify two of this chapter’s program examples, between the Delphi 4 and Delphi 5 tion of the book, just to avoid run-time errors caused by this change The problem is that I use

edi-the caption in edi-the code, and edi-the extra & broke my code The change was quite simple, though:

All I had to do was to set the AutoHotkeys property of the main menu component to maManual.

Pop-Up Menus and the OnContextPopup Event

Besides the MainMenu component, you can use the similar PopupMenu component This istypically displayed when the user right-clicks a component that uses the given pop-up menu

as the value for its PopupMenuproperty

However, besides connecting the pop-up menu to a component with the correspondingproperty, you can call its Popupmethod, which requires the position of the pop-up in screencoordinates The proper values can be obtained by converting a local point to a screen pointwith the ClientToScreenmethod of the local component, in this code fragment a label:

procedure TForm1.Label3MouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

var

ScreenPoint: TPoint;

begin

// if some condition applies

if Button = mbRight then

begin

ScreenPoint := Label3.ClientToScreen (Point (X, Y));

PopupMenu1.Popup (ScreenPoint.X, ScreenPoint.Y)

end;

end;

Trang 21

An alternative approach is the use of the OnContextMenuevent This event, introduced inDelphi 5, fires when a user right-clicks a component—exactly what we’ve traced above withthe test if Button = mbRight The advantage is that the same event is also fired in response

to a Shift+F10 key combination, as well as by any other user-input methods defined by dows Accessibility options or hardware (including the shortcut-menu key of some Windows-compatible keyboards) We can use this event to fire a pop-up menu with little code:

Win-procedure TFormPopup.Label1ContextPopup(Sender: TObject;

MousePos: TPoint; var Handled: Boolean);

ScreenPoint := ClientToScreen (MousePos);

PopupMenu2.Popup (ScreenPoint.X, ScreenPoint.Y);

The Handledparameter is preinitialized to False, so that if you do nothing in the event handler,the normal pop-up menu processing will occur If you do something in your event handler toreplace the normal pop-up menu processing (such as popping up a dialog or a customized menu,

as in this case), you should set Handledto True and the system will stop processing the message.Setting Handledto True should be fairly rare, as you’ll generally handle the OnContextPopuptodynamically create or customize the pop-up menu, but then you can let the default handleractually show the menu

The handler of an OnContextPopupevent isn’t limited to displaying a pop-up menu It can

do any other operation, such as directly display a dialog box Here is an example of a click operation used to change the color of the control:

right-procedure TFormPopup.Label2ContextPopup(Sender: TObject;

MousePos: TPoint; var Handled: Boolean);

begin

Working with Menus

Trang 22

Creating Menu Items Dynamically

Besides defining the structure of a menu with the Menu Designer and modifying the status ofthe items using the Checked, Visible, and Captionproperties, you can create an entire menu

or portions of one at run time This makes sense, for example, when you have many repetitiveitems, or when the menu items depend on some system configuration or user permissions.The basic idea is that each object of the TMenuItemclass—which Delphi uses for bothmenu items and pull-down menus—contains a list of menu items Each of these items has thesame structure, in a kind of recursive way A pull-down menu has a list of submenus, and eachsubmenu has a list of submenus, each with its own list of submenus, and so on The proper-ties you can use to explore the structure of an existing menu are Items, which contains theactual list of menu items, and Count, which contains the number of subitems Adding newmenu items or entire pull-down menus to an existing menu is fairly easy, particularly if youcan write a single event handler for all of them

This is demonstrated by the DynaMenu example (and its QDynaMenu counterpart),which also illustrates the use of menu check marks, radio items, and many other features ofmenus that aren’t described in detail in the text As soon as you start this program, it creates anew pull-down with menu items used to change the font size of a big label hosted by theform Instead of creating a bunch of menu items with captions indicating sizes ranging from

8 to 48, you can let the program do this repetitive work for you

The new pull-down menu should be inserted in the Itemsproperty of the MainMenu1ponent You can calculate the position by asking the MainMenu component for the previouspull-down menu:

com-procedure TFormColorText.FormCreate(Sender: TObject);

var

PullDown, Item: TMenuItem;

Position, I: Integer;

begin

// create the new pull-down menu

PullDown := TMenuItem.Create (Self);

Trang 23

Position := MainMenu1.Items.IndexOf (Options1);

MainMenu1.Items.Insert (Position + 1, PullDown);

// create menu items for various sizes

I := 8;

while I <= 48 do

begin

// create the new item

Item := TMenuItem.Create (Self);

Item.Caption := IntToStr (I);

// make it a radio item

// add extra item at the end

Item := TMenuItem.Create (Self);

F I G U R E 6 9 :

The Size pull-down menu of

the DynaMenu example is

created at run time, along

with all of its menu items.

Working with Menus

Trang 24

WARNING Because the program uses the Caption of the new items dynamically, we should either

dis-able the AutoHotkeys property of the main menu component, or disdis-able this feature for the pull-down menu we are going to add (and thus automatically disable it for the menu items) This is what I’ve done in the code above by setting the AutoHotkeys property of the dynami- cally created pull-down component to maManual An alternative approach is to let the menu display the automatic captions and then call the new StripHotkeys function before convert-

ing the caption to a number There is also a new GetHotkey function, which returns the active

character of the caption.

The handler for the OnClickevent of these dynamically created menu items uses the tion of the Sendermenu item to set the size of the font:

cap-procedure TFormColorText.SizeItemClick(Sender: TObject);

begin

with Sender as TMenuItem do

Label1.Font.Size := StrToInt (Caption);

end;

This code doesn’t set the proper radio-item mark next to the selected item, because theuser can select a new size also by changing the font The proper radio item is checked in theOnClickevent handler of the entire pull-down menu, which is connected just after the pull-down is created and activated just before showing the pull-down The code scans the items ofthe pull-down menu (the Senderobject) and checks whether the caption matches the currentSizeof the font If no match is found, the program checks the last menu item, to indicatethat a different size is active:

procedure TFormColorText.SizeClick (Sender: TObject);

if not Found then

Items [Count - 1].Checked := True;

end;

end;

Trang 25

When you want to create a menu or a menu item dynamically, you can use the ing components, as I’ve done in the DynaMenu and QDynaMenu examples As an alternative,you can also use some global functions available in the Menus unit: NewMenu, NewPopupMenu,NewSubMenu, NewItem, and NewLine

correspond-Using Menu Images

In Delphi it is very easy to improve a program’s user interface by adding images to menuitems This is becoming common in Windows applications, and it’s very nice that Borlandhas added all the required support, making the development of graphical menu items trivial.All you have to do is add an image list control to the form, add a series of bitmaps to theimage list, connect the image list to the menu using its Imagesproperty, and set the properImageIndexproperty for the menu items You can see the effect of these simple operations inFigure 6.10 (You can also associate a bitmap with the menu item directly, using the Bitmapproperty.)

TIP The definition of images for menus is quite flexible, as it allows you to associate an image list

with any specific pull-down menu (and even a specific menu item) using the SubMenuImages property Having a specific and smaller image list for each pull-down menu, instead of one single huge image list for the entire menu, allows for more run-time customization of an application.

To create the image list, double-click the component, activating the corresponding editor(shown in Figure 6.11), then import existing bitmap or icon files You can actually prepare asingle large bitmap and let the image editor divide it according to the Heightand Widthproperties of the ImageList component, which refer to the size of the individual bitmaps inthe list

F I G U R E 6 1 0 :

The simple graphical menu

of the MenuImg example

Working with Menus

Trang 26

TIP As an alternative, you can use the series of images that ship with Delphi and are stored by

default in the \Program Files\Common Files\Borland Shared\Images\Buttons tory Each bitmap contains both an “enabled” and a “disabled” image As you import them, the Image List editor will ask you whether to split them in two, a suggestion you should accept This operation adds to the image list a normal image and a disabled one, which is not generally used (as it can be built automatically when needed) For this reason I generally delete the disabled part of the bitmap from the image list.

direc-The program’s code is very simple direc-The only element I want to emphasize is that if you setthe Checkedproperty of a menu item with an image instead of displaying a check mark, theitem paints its image as “sunken” or “recessed.” You can see this in the Large Font menu ofthe MenuImg example in Figure 6.10 Here is the code for that menu item selection:

procedure TForm1.LargeFont1Click(Sender: TObject);

// changes the image style near the item

LargeFont1.Checked := not LargeFont1.Checked;

end;

WARNING To make the CLX version of the program, QMenuImg, display the bitmaps properly, I had to

reimport them Simply converting the Image List component data didn’t work.

F I G U R E 6 1 1 :

The Image List editor, with

the bitmaps of the

MenuImg example

Trang 27

Customizing the System Menu

In some circumstances, it is interesting to add menu commands to the system menu itself,instead of (or besides) having a menu bar This might be useful for secondary windows, tool-boxes, windows requiring a large area on the screen, and “quick-and-dirty” applications.Adding a single menu item to the system menu is straightforward:

AppendMenu (GetSystemMenu (Handle, FALSE), MF_SEPARATOR, 0, ‘’);

AppendMenu (GetSystemMenu (Handle, FALSE), MF_STRING, idSysAbout, ‘&About ’);

This code fragment (extracted from the OnCreateevent handler of the SysMenu example)adds a separator and a new item to the system menu item The GetSystemMenuAPI function,which requires as a parameter the handle of the form, returns a handle to the system menu.The AppendMenuAPI function is a general-purpose function you can use to add menu items

or complete pull-down menus to any menu (the menu bar, the system menu, or an existingpull-down menu) When adding a menu item, you have to specify its text and a numericidentifier In the example I’ve defined this identifier as:

const idSysAbout = 100;

Adding a menu item to the system menu is easy, but how can we handle its selection?Selecting a normal menu generates the wm_CommandWindows message This is handled inter-nally by Delphi, which activates the OnClickevent of the corresponding menu item compo-nent The selection of system menu commands, instead, generates a wm_SysCommandmessage,which is passed by Delphi to the default handler Windows usually needs to do something inresponse to a system menu command

We can intercept this command and check to see whether the command identifier (passed

in the CmdTypefield of the TWmSysCommandparameter) of the menu item is idSysAbout Sincethere isn’t a corresponding event in Delphi, we have to define a new message-responsemethod for the form class:

if Msg.CmdType = idSysAbout then

ShowMessage (‘Mastering Delphi: SysMenu example’);

inherited;

end;

To build a more complex system menu, instead of adding and handling each menu item as

we have just done, we can follow a different approach Just add a MainMenu component to

Working with Menus

Trang 28

the form, create its structure (any structure will do), and write the proper event handlers.Then reset the value of the Menuproperty of the form, removing the menu bar.

Now we can add some code to the SysMenu example to add each of the items from thehidden menu to the system menu This operation takes place when the button of the form isclicked The corresponding handler uses generic code that doesn’t depend on the structure ofthe menu we are appending to the system menu:

procedure TForm1.Button1Click(Sender: TObject);

var

I: Integer;

begin

// add a separator

AppendMenu (GetSystemMenu (Handle, FALSE), MF_SEPARATOR, 0, ‘’);

// add the main menu to the system menu

with MainMenu1 do

for I := 0 to Items.Count - 1 do AppendMenu (GetSystemMenu (Self.Handle, FALSE),

mf_Popup, Items[I].Handle, PChar (Items[I].Caption));

// disable the button

Button1.Enabled := False;

end;

TIP This code uses the expression Self.Handle to access the handle of the form This is required

because we are currently working on the MainMenu1 component, as specified by the with statement.

The menu flag used in this case, mf_Popup, indicates that we are adding a pull-down menu

In this function call, the fourth parameter is interpreted as the handle of the pull-downmenu we are adding (in the previous example, we passed the identifier of the menu, instead).Since we are adding to the system menu items with submenus, the final structure of the sys-tem menu will have two levels, as you can see in Figure 6.12

F I G U R E 6 1 2 :

The second-level system

menu items of the SysMenu

example are the result of

copying a complete main

menu to the system menu.

Trang 29

WARNING The Windows API uses the terms pop-up menu and pull-down menu interchangeably This is

really odd, because most of us use the terms to mean different things Pop-up menus are shortcut menus, and pull-down menus are the secondary menus of the menu bar Apparently, Microsoft uses the terms in this way because the two elements are implemented with the same kind of internal windows; the fact that they are two distinct user-interface elements is probably something that was later conceptually built over a single basic internal structure.

Once you have added the menu items to the system menu, you need to handle them Ofcourse, you can check for each menu item in the WMSysCommandmethod, or you can try build-ing a smarter approach Since in Delphi it is easier to write a handler for the OnClickevent ofeach item, we can look for the item corresponding to the given identifier in the menu struc-ture Delphi helps us by providing a FindItemmethod

When (and if) we have found a main menu item that corresponds to the item selected in thesystem menu, we can call its Clickmethod (which invokes the OnClickhandler) Here is thecode I’ve added to the WMSysCommandmethod:

var

Item: TMenuItem;

begin

Item := MainMenu1.FindItem (Msg.CmdType, fkCommand);

if Item <> nil then

prede-wm_SysCommandmessage in the Windows API Help file

This application works but has one glitch If you click the right mouse button over theTaskbar icon representing the application, you get a plain system menu (actually differentfrom the default one) The reason is that this system menu belongs to a different window, thewindow of the Applicationglobal object I’ll discuss the Applicationobject, and update thisexample to make it work with the Taskbar button, in Chapter 9, “Working with Forms.”

NOTE Because this program uses low-level Windows features (API calls and messages), it is not possible

to compile it with CLX, so there is no Qt version of this example.

Working with Menus

Trang 30

Owner-Draw Controls and Styles

Let’s return briefly to menu graphics Besides using an ImageList to add glyphs to the menuitems, you can turn a menu into a completely graphical element, using the owner-draw tech-nique The same technique also works for other controls, such as list boxes In Windows, thesystem is usually responsible for painting buttons, list boxes, edit boxes, menu items, and similarelements Basically, these controls know how to paint themselves As an alternative, however, thesystem allows the owner of these controls, generally a form, to paint them This technique, avail-

able for buttons, list boxes, combo boxes, and menu items, is called owner-draw.

In VCL, the situation is slightly more complex The components can take care of paintingthemselves in this case (as in the TBitBtnclass for bitmap buttons) and possibly activate cor-responding events The system sends the request for painting to the owner (usually theform), and the form forwards the event back to the proper control, firing its event handlers

In CLX, some of the controls, such as ListBoxes and ComboBoxes, surface events verysimilar to Windows owner-draw, but menus lack them The native approach of Qt is to usestyles to determine the graphical behavior of all of the controls in the system, of a specificapplication, or of a given control I’ll introduce styles shortly, later in this section

NOTE Most of the Win32 common controls have support for the owner-draw technique, generally

called custom drawing You can fully customize the appearance of a ListView, TreeView,

Tab-Control, PageTab-Control, HeaderTab-Control, StatusBar, and ToolBar The ToolBar, ListView, and

Tree-View controls also support advanced custom drawing, a more fine-tuned drawing capability

introduced by Microsoft in the latest versions of the Win32 common controls library The downside to owner-draw is that when the Windows user interface style changes in the future (and it always does), your owner-draw controls that fit in perfectly with the current user inter- face styles will look outdated and out of place Since you are creating a custom user interface, you’ll need to keep it updated yourself By contrast, if you use the standard output of the con- trols, your applications will automatically adapt to a new version of such controls.

Owner-Draw Menu Items

VCL makes the development of graphical menu items quite simple compared to the tional approach of the Windows API: You set the OwnerDrawproperty of a menu item compo-nent to True and handle its OnMeasureItemand OnDrawItemevents This same feature is notavailable on CLX

tradi-In the OnMeasureItemevent, you can determine the size of the menu items This eventhandler is activated once for each menu item when the pull-down menu is displayed and hastwo reference parameters you can set:

procedure ColorMeasureItem (Sender: TObject; ACanvas: TCanvas;

var Width, Height: Integer);

Trang 31

The other parameter, ACanvas, is typically used to determine the height of the current font

In the OnDrawItemevent, you paint the actual image This event handler is activated everytime the item has to be repainted This happens when Windows first displays the items andeach time the status changes; for example, when the mouse moves over an item, it shouldbecome highlighted In fact, to paint the menu items, we have to consider all the possibilities,including drawing the highlighted items with specific colors, drawing the check mark ifrequired, and so on Luckily enough, the Delphi event passes to the handler the Canvaswhere it should paint, the output rectangle, and the status of the item (selected or not):

procedure ColorDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect;

Selected: Boolean);

In the ODMenu example, I’ll handle the highlighted color, but skip other advanced aspects(such as the check marks) I’ve set the OwnerDrawproperty of the menu and written handlersfor some of the menu items To write a single handler for each event of the three color-related menu items, I’ve set their Tagproperty to the value of the actual color in the OnCreateevent handler of the form This makes the handler of the actual OnClickevent of the itemsquite straightforward:

procedure TForm1.ColorClick(Sender: TObject);

procedure TForm1.ColorDrawItem(Sender: TObject; ACanvas: TCanvas;

ARect: TRect; Selected: Boolean);

// show the color

ACanvas.Brush.Color := (Sender as TComponent).Tag;

Trang 32

The three handlers for this event of the Shape pull-down menu items are all different,although they use similar code:

procedure TForm1.Ellipse1DrawItem(Sender: TObject; ACanvas: TCanvas;

ARect: TRect; Selected: Boolean);

NOTE To accommodate the increasing number of states in the Windows 2000 user interface style,

since version 5, Delphi has included the OnAdvancedDrawItem event for menus.

A ListBox of Colors

As we have just seen for menus, list boxes have an owner-draw capability, which means a gram can paint the items of a list box The same support is provided for combo boxes and isalso available on CLX To create an owner-draw list box, we set its Styleproperty to lbOwn-erDrawFixed or lbOwnerDrawVariable The first value indicates that we are going to set theheight of the items of the list box by specifying the ItemHeightproperty and that this will be

pro-F I G U R E 6 1 3 :

The owner-draw menu of

the ODMenu example

Trang 33

the height of each and every item The second owner-draw style indicates a list box withitems of different heights; in this case, the component will trigger the OnMeasureItemeventfor each item, to ask the program for their heights

In the ODList example (and its QODList version), I’ll stick with the first, simpler,

approach The example stores color information along with the items of the list box and thendraws the items in colors (instead of using a single color for the whole list)

The DFM or XFM file of every form, including this one, has a TextHeightattribute, whichindicates the number of pixels required to display text This is the value we should use for theItemHeightproperty of the list box An alternative solution is to compute this value at runtime, so that if we later change the font at design time, we don’t have to remember to set theheight of the items accordingly

NOTE I’ve just described TextHeight as an attribute of the form, not a property And in fact it isn’t a

property but a local value of the form If it is not a property, you might ask, how does Delphi save it in the DFM file? Well, the answer is that Delphi’s streaming mechanism is based on prop-

erties plus special property clones created by the DefineProperties method.

Since TextHeightis not a property, although it is listed in the form description, we cannot

access it directly Studying the VCL source code, I found that this value is computed by ing a private method of the form: GetTextHeight Since it is private, we cannot call this func-tion What we can do is duplicate its code (which is actually quite simple) in the FormCreatemethod of the form, after selecting the font of the list box:

for I := Low (Colors) to High (Colors) do

ListBox1.Items.AddObject (ColorToString (Colors[I]), TObject(Colors[I]));

end;

This method uses an open-array parameter, an array of an undetermined number of elements

of the same type For each item passed as a parameter, we add the name of the color to the list,and we add its value to the related data, by calling the AddObjectmethod To obtain the stringcorresponding to the color, we call the Delphi ColorToStringfunction This returns a string

Owner-Draw Controls and Styles

Trang 34

containing either the corresponding color constant, if any, or the hexadecimal value of thecolor The color data is added to the list box after casting its value to the TObjectdata type (afour-byte reference), as required by the AddObjectmethod.

TIP Besides ColorToString, which converts a color value into the corresponding string with the

identifier or the hexadecimal value, there is also a Delphi function to convert a properly matted string into a color, StringToColor.

for-In the ODList example, this method is called in the OnCreateevent handler of the form(after previously setting the height of the items):

AddColors ([clRed, clBlue, clYellow, clGreen, clFuchsia, clLime, clPurple, clGray, RGB (213, 23, 123), RGB (0, 0, 0), clAqua, clNavy, clOlive, clTeal]);

To compile the CLX version of this code, I’ve added to it the RGBfunction described earlier

in the section “Colors.” The code used to draw the items is not particularly complex Wesimply retrieve the color associated with the item, set it as the color of the font, and thendraw the text:

procedure TODListForm.ListBox1DrawItem(Control: TWinControl; Index: Integer;

Rect: TRect; State: TOwnerDrawState);

Canvas.Font.Color := TColor (Items.Objects [Index]);

Canvas.TextOut(Rect.Left, Rect.Top, Listbox1.Items[Index]);

end;

end;

The system already sets the proper background color, so the selected item is displayedproperly even without any extra code on our part You can see an example of the output ofthis program at startup in Figure 6.14

The example also allows you to add new items, by double-clicking the list box:

procedure TODListForm.ListBox1DblClick(Sender: TObject);

Trang 35

CLX Styles

In Windows, the system has full control of the user interface of the controls, unless the gram takes over using owner-draw or other advanced techniques In Qt (and in Linux in gen-eral), the user chooses the user interface style of the controls A system will generally offer afew basic styles, such as the Windows look-and-feel, the Motif one, and others A user canadd also install new styles in the system and make them available to applications

pro-NOTE The styles I’m discussing here refer to the user interface of the controls, not of the forms and

their borders This is generally configurable on Linux systems but is technically a separate ment of the user interface.

ele-Because this technique is embedded in Qt, it is also available on the Windows version ofthe library, and CLX makes it available to Delphi developers The Applicationglobal object

of CLX has a Styleproperty, which can be used to set a custom style or a default one, cated by the DefaultStylesubproperty For example, you can select a Motif look-and-feelwith this code:

indi-Application.Style.DefaultStyle := dsMotif;

F I G U R E 6 1 4 :

The output of the ODList

example, with a colored

owner-draw list box

Owner-Draw Controls and Styles

Trang 36

In the StylesDemo program, I’ve added, among various sample controls, a list box with thenames of the default styles, as indicated in the TDefaultStyleenumeration, and this code forits OnDblClickevent:

procedure TForm1.ListBox1DblClick(Sender: TObject);

Then we’ve started to explore some of the basic components available in Delphi, looking atboth libraries These components correspond to the standard Windows controls and some ofthe common controls, and they are extremely common in applications You’ve also seen how tocreate main menus and pop-up menus and how to add extra graphics to some of these controls.The next step, however, is to explore in depth the elements of a complete user interface,discussing other common controls, multipage forms, action lists, and the new Delphi 6Action Manager, to end up discussing technical details of forms All of these topics will be

F I G U R E 6 1 5 :

The StylesDemo program, a

Windows application

cur-rently with an unusual

Motif layout

Trang 38

In the preceding chapter, I discussed the core concepts of the TControlclass and itsderived classes in the VCL and VisualCLX libraries After that, I provided a sort of rapidtour of the key controls you can use to build a user interface, including editing components,lists, range selectors, and more.

This chapter provides more details on some of these components (such as the ListViewand TreeView) and then discusses other controls used to define the overall design of a form,such as the PageControl, TabControl, and Splitter The chapter also presents examples ofsplitting forms and resizing controls dynamically These topics are not particularly complex,but it is worth examining their key concepts briefly

After these components, I’ll introduce toolbars and status bars, including the tion of hints and other slightly more advanced features This will give us all the foundationmaterial for the following chapter, which covers actions and the new action manager archi-tecture of Delphi 6

customiza-ListView and TreeView Controls

In Chapter 6, I introduced all the various visual controls you can use to display lists of values.The standard list box and combo box components are still very common, but they are oftenreplaced by the more powerful ListView and TreeView controls Again, these two controls arepart of the Win32 common controls, stored in the ComCtl32.DLLlibrary Similar controlsare available in Qt and VisualCLX

A Graphical Reference List

When you use a ListView component, you can provide bitmaps both indicating the status ofthe element (for example, the selected item) and describing the contents of the item in agraphical way

How do we connect the images to a list or tree? We need to refer to the ImageList nent we’ve already used for the images of the menu A ListView can actually have three imagelists: one for the large icons (the LargeImagesproperty), one for the small icons (the SmallIm- agesproperty), and one used for the state of the items (the StateImagesproperty) In theRefList example on the companion CD, I’ve set the first two properties using two differentImageList components

compo-Each of the items of the ListView has an ImageIndex, which refers to its image in the list.For this to work properly, the elements in the two image lists should follow the same order.When you have a fixed image list, you can add items to it using Delphi’s ListView Item Edi-tor, which is connected to the Itemsproperty In this editor, you can define items and so-called subitems The subitems are displayed only in the detailed view (when you set the

Trang 39

The file format is simple, as you can see in the following saving code For each item of thelist, the program saves the caption on one line, the image index on another line (prefixed bythe @character), and the subitems on the following lines, indented with a tab character:procedure TForm1.FormDestroy(Sender: TObject);

// save the index

List.Add (‘@’ + IntToStr (ListView1.Items[I].ImageIndex));

// save the subitems (indented)

Trang 40

end;

end;

The items are then reloaded in the FormCreatemethod:

procedure TForm1.FormCreate(Sender: TObject);

NewItem.ImageIndex := StrToIntDef (List [I][2], 0) else

begin // a new item NewItem := ListView1.Items.Add;

NewItem.Caption := List [I];

Ngày đăng: 12/08/2014, 21:20

TỪ KHÓA LIÊN QUAN

w