The TListBox Component


The TListBox component displays a list of items. The most important property of the TListBox component is the Items property, which is an object property of type TStrings. The TStrings class represents a list of strings and enables us to manipulate the strings in the list. So, if you want to manipulate the items in the list box, you have to call the methods of the Items property. For instance, to add a new item to the list box, you have to call the Add method of the Items property. The Add method accepts a single string parameter — the string value that will be added to the end of the list.

image from book
Figure 14-6: The TListBox component

Listing 14-9: Adding a new item to the list box

image from book
procedure TForm1.Button1Click(Sender: TObject); begin   ListBox1.Items.Add(Edit1.Text); end;
image from book

The Assign Method

In this section, we'll use the TListBox component to create an application that will enable us to view all installed fonts on the computer. The list of installed fonts is provided by yet another global object — the Screen object. The list of font names is contained in the Fonts property, which is also declared as a TStrings type property.

In order to display the font list in a list box, we have to copy the entire contents of the font list to the list box. Although we can manually copy the items from one list to another, there is a better way to do this — by using the Assign method. The Assign method is almost omnipresent in the VCL and is used to copy the contents of a source object to the destination object. In the case of a string list, the Assign method is used to copy all strings from a source string list to the destination list.

Now, add a TListBox component to the Designer Surface, double-click the form to create the empty event handler for the OnCreate event, and write the code to copy the contents of the font list to the list box.

Listing 14-10: Copying an entire string list

image from book
procedure TForm1.FormCreate(Sender: TObject); begin   ListBox1.Items.Assign(Screen.Fonts); end; 
image from book

image from book
Figure 14-7: Previewing installed fonts

To enable the user to preview the selected font, you first have to determine which item is selected. The selected string list item is specified by the ItemIndex property of the list box. The ItemIndex property is an integer property that contains –1 if no item is selected, 0 if the first item is selected, and Items.Count –1 if the last item in the list box is selected.

To preview the selected font, you have to do the following:

  1. Add a TLabel component to the Designer Surface and rename it PreviewLabel.

  2. Add an event handler for the OnClick event of the TListBox component.

  3. Add the following code to the OnClick event handler of the list box.

Listing 14-11: Changing the label's font

image from book
procedure TForm1.ListBox1Click(Sender: TObject); var   SelectedFont: string; begin   { get selected item }   SelectedFont := ListBox1.Items[ListBox1.ItemIndex];   { change font & caption }   PreviewLabel.Font.Name := SelectedFont;   PreviewLabel.Caption := SelectedFont; end;
image from book

Selecting Multiple Items

By default, the TListBox component enables us to select only one item from the list. If you want to select multiple items from the list box, you have to set the MultiSelect property to True. When you set the MultiSelect property to True, you won't be able to use the ItemIndex property to determine the selected items. In the case of a MultiSelect list box, the ItemIndex property only identifies the item that has the focus.

image from book
Figure 14-8: A MultiSelect TListBox component

To determine which items in a MultiSelect list box are selected, you have to use the Selected property. The Selected property is an indexed property that enables you to determine whether or not an item at the specified index is selected. For instance, the following code shows how to find out if the first item in a list box is selected:

if ListBox1.Selected[0] then   Caption := 'The first item is selected.';

If you want to work with all selected items, you have to write a loop that tests each item to see if the item is selected or not.

Now, let's create a simple application that enables the user to select multiple items in one list and move them to another list. Drop two TListBox components to the Designer Surface. The ListBox1 component will serve as the source list and the ListBox2 component will be the target list.

To enable the user to select and move multiple items from the source to the destination list box, you have to first set the MultiSelect property of the ListBox1 component to True and then add several items to it. To add items to a TListBox component at design time, you have to use the String List Editor. The String List Editor is displayed by the Object Inspector when you click the
(...) button next to the Items property of the selected list box.

image from book
Figure 14-9: The String List Editor

Now, add a TButton component to the Designer Surface, place it somewhere between the two TListBox controls, and set its Caption property to ">>". We'll use the button's OnClick event to move the items from the source to the destination list box.

Listing 14-12: Moving multiple items from one list box to another

image from book
procedure TForm1.Button1Click(Sender: TObject); var   i: Integer; begin   { copy the selected items }   for i := 0 to Pred(ListBox1.Items.Count) do   begin     if ListBox1.Selected[i] then       ListBox2.Items.Add(ListBox1.Items[i]);   end;   { remove the selected items }   for i := Pred(ListBox1.Items.Count) downto 0 do   begin     if ListBox1.Selected[i] then       ListBox1.Items.Delete(i);   end; end;
image from book

We can copy the items from the source to the destination list box using the standard for loop because we aren't changing the contents of the source list box. But to delete the selected items from the source list box, we have to use the downto loop because the Delete method changes the contents (and the indexes) of the source list box.

Listing 14-12 shows how to manually remove the selected items from a list box. If you only have to remove the selected items, you can also use the DeleteSelected method.

procedure TForm1.Button1Click(Sender: TObject); var   i: Integer; begin   { copy the selected items }   for i := 0 to Pred(ListBox1.Items.Count) do   begin     if ListBox1.Selected[i] then       ListBox2.Items.Add(ListBox1.Items[i]);   end;   ListBox1.DeleteSelected; end;

The BeginUpdate and EndUpdate Methods

Normally, every time you add a new item to a list box or otherwise change the contents of the list box, the list box repaints itself in order to display the new item. When you add items to a list box in a loop, you should consider using the BeginUpdate and EndUpdate methods of the Items property because they enable us to temporarily disable the repainting of the list box.

The BeginUpdate method temporarily disables repainting of the list box and the EndUpdate method forces the list box to repaint itself. BeginUpdate is usually called before the loop that modifies the contents of the list box, and EndUpdate is called after the loop to repaint the list box and display the changes made to the list box contents.

Listing 14-13: Using the BeginUpdate and EndUpdate methods to optimize list box operations

image from book
procedure TForm1.Button1Click(Sender: TObject); var   i: Integer; begin   { disable repainting }   ListBox2.Items.BeginUpdate;   for i := 0 to Pred(ListBox1.Items.Count) do   begin     if ListBox1.Selected[i] then       ListBox2.Items.Add(ListBox1.Items[i]);   end;   { disable repaint before deleting }   ListBox1.Items.BeginUpdate;   ListBox1.DeleteSelected;   ListBox1.Items.EndUpdate;   { enable painting and refresh the list box }   ListBox2.Items.EndUpdate; end;
image from book

The BeginUpdate and EndUpdate methods really make a difference, especially in large loops. For instance, moving 5,000 selected items from one list box to another normally takes around 2 seconds on my machine. When the BeginUpdate and EndUpdate methods are used, the same loop takes about 0.2 seconds to finish.

image from book
Figure 14-10: Speed gained by using the BeginUpdate and EndUpdate methods

The IndexOf Method

The IndexOf method enables us to search for a string in a string list. This method accepts only one string parameter and returns the index of the passed string if the string is found. If the passed string doesn't exist in the string list, the IndexOf method returns –1.

The IndexOf method can be used, for instance, to allow the user to add only unique values to the list box. The following example illustrates not only how to use the IndexOf method, but also how to use the MessageDlg function to query the user. The MessageDlg function is used to ask the user if he or she wants to add a value to the list when the value already exists.

Before we continue, you'll need to add three components to the Designer Surface — TListBox, TEdit, and TButton components (see Figure 14-11).

image from book
Figure 14-11: Components used in the IndexOf example

Listing 14-14: Adding unique items to a list box

image from book
procedure TForm1.Button1Click(Sender: TObject); begin   if ListBox1.Items.IndexOf(Edit1.Text) = -1 then     ListBox1.Items.Add(Edit1.Text)   else begin     if MessageDlg('This item already exists. Add anyway?',       mtConfirmation, mbYesNo, 0) = mrYes then         ListBox1.Items.Add(Edit1.Text);   end; end;
image from book

As you can see, the MessageDlg function can be used to get some kind of response from the user. The third parameter is a set parameter that enables you to define which buttons are displayed on the dialog box. You can either construct the set manually or use one of the predefined button sets. Listing 14-15 gives all the data types and constants that can be used to customize the MessageDlg dialog box.

Listing 14-15: MessageDlg related data types and constants

image from book
type   TMsgDlgType = (mtWarning, mtError,     mtInformation, mtConfirmation, mtCustom);   TMsgDlgBtn = (mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry,     mbIgnore, mbAll, mbNoToAll, mbYesToAll, mbHelp);   TMsgDlgButtons = set of TMsgDlgBtn; const   mbYesNo = [mbYes, mbNo];   mbYesNoCancel = [mbYes, mbNo, mbCancel];   mbYesAllNoAllCancel = [mbYes, mbYesToAll, mbNo, mbNoToAll, mbCancel];   mbOKCancel = [mbOK, mbCancel];   mbAbortRetryIgnore = [mbAbort, mbRetry, mbIgnore];   mbAbortIgnore = [mbAbort, mbIgnore];
image from book

If you want, you can also take advantage of short-circuit evaluation here and rewrite Listing 14-14 like this:

Listing 14-16: Adding unique items to a list box, revisited

image from book
procedure TForm1.Button1Click(Sender: TObject); begin   if (ListBox1.Items.IndexOf(Edit1.Text) = -1) or      (MessageDlg('This item already exists. Add anyway?',       mtConfirmation, mbYesNo, 0) = mrYes) then         ListBox1.Items.Add(Edit1.Text); end;
image from book

image from book
Figure 14-12: Adding items to a list box

The Names and Values Properties

The TStrings class defines two more properties that can be used to access strings in the string list. The Names and Values properties are indexed properties that allow us to access a part of a string that contains a name-value pair. By default, the character that separates the name part from the value part in a string is the equal sign (=). Figure 14-13 shows several name-value pairs that are used in the following example.

image from book
Figure 14-13: Name-value pairs

The Names and Values properties allow us to, for instance, easily create a simple dictionary. We'll now create a simple English-Croatian and English- Klingon dictionary using the Values property (see Figure 14-14).

image from book
Figure 14-14: A simple dictionary that uses the Values property

The first thing we need to do is fill two separate text files with English-Croatian and English-Klingon name-value pairs. You can see a portion of these files in Figure 14-13. The best place to keep these data files is the root directory of the application. Name the files image from book Croatian.txt and image from book Klingon.txt.

In order to use the Values property to extract the value part of a string, we have to load the image from book Croatian.txt and image from book Klingon.txt text files into two string lists. If you don't need to display the string list on the form, you shouldn't use a TListBox component or any other control that can display a string list because you would unnecessarily waste system resources.

If you have to work with string lists in the background, you should use the TStringList class. We cannot use the TStrings class directly because it is an abstract class. An abstract class is a class that is never instantiated because at least one of its methods has no implementation, only the interface that needs to be implemented in a descendant class. One of the classes that implements the abstract methods of the TStrings class is the TStringList class.

Listing 14-17: Portions of the TStrings and TStringList class declarations

image from book
  TStrings = class(TPersistent)   public     destructor Destroy; override;     function Add(const S: string): Integer; virtual;     function AddObject(const S: string; AObject: TObject): Integer; virtual;     procedure Append(const S: string);     procedure AddStrings(Strings: TStrings); virtual;     procedure Assign(Source: TPersistent); override;     procedure BeginUpdate;     procedure Clear; virtual; abstract;     procedure Delete(Index: Integer); virtual; abstract;     procedure EndUpdate;     function IndexOf(const S: string): Integer; virtual;     function IndexOfName(const Name: string): Integer; virtual;     function IndexOfObject(AObject: TObject): Integer; virtual;     procedure Insert(Index: Integer; const S: string); virtual; abstract;     procedure InsertObject(Index: Integer; const S: string;       AObject: TObject); virtual;     procedure LoadFromFile(const FileName: string); virtual;     procedure Move(CurIndex, NewIndex: Integer); virtual;     procedure SaveToFile(const FileName: string); virtual;     property Count: Integer read GetCount;     property Names[Index: Integer]: string read GetName;     property Values[const Name: string]: string read GetValue write SetValue;     property Strings[Index: Integer]: string read Get write Put; default;  end; TStringList = class(TStrings)     function Add(const S: string): Integer; override;     procedure Clear; override;     procedure Delete(Index: Integer); override;     function IndexOf(const S: string): Integer; override;     procedure Insert(Index: Integer; const S: string); override; end;
image from book

Our first job in this application is to dynamically create two TStringList objects and use them to load the image from book Croatian.txt and image from book Klingon.txt text files located in the root directory of the application. Although you can manually read the text file and use the TStringList.Add method to add strings to the list, the best way to load a text file into a string list is to call the TStringList.LoadFromFile method.

Listing 14-18: Loading the text files to TStringList objects

image from book
type   TForm1 = class(TForm)   private     { Private declarations }     Croatian: TStringList;     Klingon: TStringList;   public     { Public declarations }   end; var   Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); var   AppPath: string; begin   { get root directory }   AppPath := ExtractFilePath(Application.ExeName);   { create lists }   Klingon := TStringList.Create;   Croatian := TStringList.Create;   { load files from the root directory }   Klingon.LoadFromFile(AppPath + 'Klingon.txt');   Croatian.LoadFromFile(AppPath + 'Croatian.txt'); end;
image from book

The first line in the OnCreate event handler determines the root directory of the application by extracting the drive and directory parts from the ExeName property. The ExeName property of the global Application object always contains the fully qualified path (drive, directory, file name, and extension).

Notice how the TStringList objects are created. The constructor takes no parameters, which means that the TStringList class doesn't descend from the TComponent class and that the memory for the TStringList objects isn't automatically managed by an Owner component. Thus, we have to manually free all TStringList instances when we're done using them. In this case, we need to free the Klingon and Croatian TStringList instances in the OnDestroy event of the main form, at the end of application execution.

Listing 14-19: Releasing the string lists from memory

image from book
procedure TForm1.FormDestroy(Sender: TObject); begin   Croatian.Free;   Klingon.Free; end;
image from book

The last thing we have to do is write code that will use the Values property to translate from English to Klingon or Croatian, or both. This code is located in the OnClick event of the Translate button.

Listing 14-20: Locating a value using the Values property of the TStringList class

image from book
procedure TForm1.Button1Click(Sender: TObject); begin   if CroatianCheckBox.Checked then     CroLabel.Caption := Croatian.Values[Edit1.Text]   else     CroLabel.Caption := '';   if KlingonCheckBox.Checked then     KlingLabel.Caption := Klingon.Values[Edit1.Text]   else     KlingLabel.Caption := ''; end;
image from book



Inside Delphi 2006
Inside Delphi 2006 (Wordware Delphi Developers Library)
ISBN: 1598220039
EAN: 2147483647
Year: 2004
Pages: 212
Authors: Ivan Hladni

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net