I.T. Discussion Community!
-Collapse +Expand
Delphi
Search Delphi Group:

Advanced
-Collapse +Expand Delphi To/From
To/FromCODEGuides
-Collapse +Expand Delphi Store
PRESTWOODSTORE

Prestwood eMagazine

January Edition
Subscribe now! It's Free!
Enter your email:

   ► KBProgrammingDelphi for W...Prestwood De...   Print This     
 
Prestwood Delphi Gazette Archive:
Delphi Gazette, Volumn 1, Issue 11, Nov 1998
 
Posted 12 years ago on 12/9/2008 and updated 12/9/2008
Take Away: Prestwood Delphi Gazette Archive

KB101674

 

.
Prestwood
Delphi Gazette
. November 1998 . Volume 1, Issue 11 .


What's New?
Larry J.Rutledge

Well, I've been at my new job for about three weeks now and have to say it feels great to be programming in Delphi again. I didn't realize how much I enjoyed it over Java until I had to develop in Java and then got to return to Delphi.

Around the time of the last meeting I received an e-mail. It was from a gentleman in Yugoslavia. He discovered the on-line version of the "Delphi Gazette" and has become a devoted reader. He told me that he was preparing to develop a Delphi website in Serbian and wanted permission to translate and use articles from the newsletter on his site.

How exciting to know that our newsletter is going international. I look forward to future collaboration with him on his endeavors. He did have one request and I thought I'd pass it along in case someone can help. Here is the request from the e-mail as he wrote it (since he explained it better than I can).

"If you have any book or magazine about Delphi or Win32 programming and you don't find it useful for you any more, could you send it to me by mail? Why I'm asking you this? As I believe you know, Yugoslavia is in very hard political and economical situation for seven or eight years. Because of it , most western countries (including USA) don't have any economical contacts with us and, normally, there is no trade. This makes impossible for me to buy any book or magazine I would like to read. Delphi is not very popular language so local publishers don't translate foreign books to Serbian language (the only two books which have been translated to Serbian are "Delphi unleashed" and "Dummy guide to Delphi")."

Meeting News
This was a fairly quiet month, no major news and instead of a formal presentation, Mike chose to spend most of the time doing questions and answers. Unfortunately, I didn't take notes and so I can't remember what we did talk about except for the question that Robert asked before he had to leave and the question I asked.

Robert's question, with an answer, appears in the Question and Answer section. My question is answered in the Advanced Delphi article. It was written by Brendan Delumpa of the "The Delphi Corner" web-site.

What's In This Issue?
Because of the new job, this is going to be a fairly small issue. With the limited time I had this month I wasn't able to spend my usual time writing, so I have included articles I got from other sources.

As I mentioned Brendan Delumpa wrote the article for the Advanced Delphi section. Unfortunately, I don't remember where I got the other two articles, since I have had them for almost three years now. If you've never seen them before, however, they are still quite useful.

In Delphi Apprentice, the article shows how to make the forms in your Delphi application zoom open and closed like Win95 forms. In Components 101, the article shows how to make a form-size limiting component. Although this functionality now exists in Delphi 4, if you only have Delphi 3 this will be useful.
.
. Inside This Issue .
1 . What's New?
2 . Delphi Apprentice
3 . Components 101
4 . Calendar of Events
4 . Our Sponsors
5 . Advanced Delphi
6 . Questions and Answers
.
.
. 1998 Larry J. Rutledge . editor: Larry J. Rutledge .
. Return to Top .
 

Delphi Apprentice
Zooming main forms under Windows 95
Unknown

Windows 95 adds several visual elements and actions that enhance the user's perception of how the system operates (editor's note: This also applies to Windows 98 and NT 4.0). One such enhancement is window zoom animation, which displays an animated, shrinking window that disappears into the window's icon on the Taskbar when you minimize the application. However, if you've run a Delphi 2.0 (editor's note: This applies to Delphi 3 and 4 as well) application on a Windows 95 system, you may have noticed that instead of seeing the standard zoom animation, the window simply disappears. In this article, we'll explain why this happens and how you can work around this disappearing act to enable zoom animation for your Delphi 2.0 applications.

Suspended Animation
If you haven't even noticed that Delphi 2.0 applications don't display zoom animation when you minimize them, you're not alone. Like most developers, you're probably more concerned with behavior of your application than with these types of details. Happily, you don't need to create a new project to test for this unusual behavior. Since Delphi 2.0 is also a Delphi 2.0 application, you'll notice that it, too, fails to display zoom animation when you minimize it. Instead, the main Delphi 2.0 window just disappears.

Who's zoomin' who?
It may surprise you to find out why Delphi 2.0 applications don't display zoom animation. In fact, part of the answer is that the 32-bit version of the Visual Component Library (VCL) source code deliberately goes to great lengths to ensure that you don't see this particular effect. To fully understand why Borland used this approach, remember that a typical Delphi application has two windows: the obvious window for the main form, and an invisible window that the Application object creates and maintains. As you may recall from previous articles, every Delphi application creates a zero-height, zero-width window at the center of the screen.

Delphi creates this invisible window to function as an interface between the Application object and the Windows operating system. In fact, this window is responsible for responding to messages the application receives when you've minimized it. Since most non-Delphi Windows applications have only one main window, minimizing and restoring that window is a simple process. When the application receives a WM_SYSCOMMAND message that contains the SC_MINIMIZE command, the application typically calls
              ShowWindow(hwndHandle, SW_MINIMIZE);
   
to minimize the window. Conversely, when the application receives a WM_SYSCOMMAND message that contains the SC_RESTORE command, it typically calls
              ShowWindow(hwndHandle, SW_RESTORE);
   
to restore the window. In sharp contrast, Delphi applications use two separate windows to respond to different messages. As a result, minimizing and restoring a Delphi application is a much more complex process, which the VCL code handles for you automatically. When you click on the Minimize button of a Delphi application's main form, the VCL code performs the following events:
  • The form window receives a WM_SYSCOMMAND message that contains the SC_MINIMIZE command.
  • If it's the main form, it calls the Minimize method of the Application object.
Up to this point, Delphi 1.0 and 2.0 applications behave identically. However, in the body of the TApplication.Minimize method, the two versions take different paths. In Delphi 1.0, the body of the TApplication.Minimize method is almost exactly what you'd expect:
              if not IsIconic(FHandle) then
              begin
                NormalizeTopMosts;
                SetActiveWindow(FHandle);
                ShowWindow(FHandle, SW_MINIMIZE);
                if Assigned(FOnMinimize) then
                  FOnMinimize(Self);
              end;
     
After disabling any stay-on-top windows, this code makes the Application object's window the active window, calls the ShowWindow() function we're expecting, and then executes the OnMinimize event-handling method, if appropriate. In Delphi 2.0, the body of the method TApplication.Minimize contains a very important difference:
              if not isIconic(FHandle) then
              begin
                NormalizeTopMosts;
                SetActiveWindow(FHandle);
                ShowWinNoAnimate(FHandle, SW_MINIMIZE);
                if Assigned(FOnMinimize) then
                  FOnMinimize(Self);
              end;
   
Thus, instead of calling the ShowWindow() function, it calls a global function named ShowWinNoAnimate(). As you might guess, this function disables zoom animation if it's currently active, calls the ShowWindow() function, and then resets zoom animation to its previous state. Odd as it may sound, a Delphi application disables zoom animation on a system-wide basis, but only for as long as it takes the system to minimize the Application window.

When you restore a minimized Delphi application, the Application window receives a WM_SYSCOMMAND message that contains the SC_RESTORE command. (As you'll recall, this occurs because the Application window is responsible for displaying the minimized application icon and for responding to the WM_SYSCOMMAND message.) Since the main form doesn't need to handle the restore operation, the Application window's window procedure simply calls the TApplication.Restore method when the window receives the message WM_SYSCOMMAND.

Not surprisingly, the Restore method simple reverses the operations of the Minimize method. As you'd expect from examining the Minimize method, the Restore method of Delphi 2.0 uses the ShowWinNoAnimate() function instead of calling the ShowWindow() function directly.

Application windows can really zoom!
To force a Delphi 2.0 application to display the missing zoom animation, we'll need to prevent the existing form and application code from executing. Unfortunately, there are several obstacles in our way. First, neither the Minimize or Restore methods of the TApplication class are virtual or dynamic, so we can't simply derive a new class from TApplication and override those methods. Similarly, we can't create event-handling methods for the OnMinimize and OnRestore events, because the Minimize and Restore methods call those event handlers after performing the animation.

Instead, we'll need to resort to a brute force approach: Catch and process WM_SYSCOMMAND messages headed for the Application window and for the main form window. To intercept a message for the form's window, we'll provide a new message-handling method. To intercept messages for the Application window, we'll need to create an application message-handler, and assign it to the Application object's OnMessage event at startup.

Now let's create a do-nothing application that displays zoom animation like non-Delphi applications do. To accomplish this effect, we'll bring together all the elements we've discussed so far.

Zoom the position
To begin, create a blank-form Single-Document Interface application, and double-click on the main form to create an OnCreate event-handling method. In the source code-editing window, change the source code for the main form to match Listing A. After you finish entering the code, save the form's source file as ZOOMER.PAS, and the project file as WIN_ZOOM.DPR. When you build and run the application, you'll see the familiar zoom animation as the window zooms down to the icon on the Taskbar.

If you pay close attention to this application's behavior on a slow-system, you may be able to see some of the details of our tip. First, the form window or the Application window receives a WM_SYSCOMMAND message. Then, the source code implements the following step to minimize this application:
  • Call the ZoomMinimized method if the command value is SC_MINIMZE.
  • Change any stay-on-top windows for this application into normal windows.
  • Set the Application window as the active window.
  • Set the window position of the Application window to the current position of the main form.
  • Change the caption of the Application window to match the name of the main form.
  • Minimize the window in the normal manner.
  • Change the caption of the Application window back to its original value.
Fortunately, all of these events occur so quickly the user won't notice that the main form itself didn't zoom to the taskbar.

Conclusion
Because Delphi applications use two windows to interact with the user, even simple tasks - such as minimizing the application - execute a great deal more code than you might think, including code that disables Windows 95's zoom animation. Using the technique we've shown here, you can add window zooming to your Delphi applications to help them conform to your users' expectations.

To download the source code, visit the file repository web-site.


unit zoomer;

interface

uses
  Windows, Messages, SysUtils, Classes, 
  Graphics, Controls, Forms, Dialogs, 
  StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    AppWindowName : String;
    procedure AppMessge(var Msg: TMsg; 
                       var Handled: Boolean);
    procedure WMSysCommand(var Message: 
                           TMessage);
              message WM_SYSCOMMAND;
    procedure ZoomMinimized;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMSysCommand(var Message: TMessage);
begin
  if (Message.wParam = SC_MINIMZE) then
    ZoomMinimized
  else
    Inherited;
end;

procedure TForm1.ZoomMinimized;
begin
  Application.NormalizeTopMosts;
  SetActiveWindow(Application.Handle);
  SetWindowPos(Application.Handle, 
               HWND_BOTTOM, Left, Top, Width, 
               Height, SWP_NOZORDER);
  SetWindowText(Application.Handle, 
        PChar(Application.MainForm.Caption));
  ShowWindow(Application.Handle, 
             SW_MINIMIZE);
  SetWindowText(Application.Handle, 
        PChar(AppWindowName));
end;

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
  if Msg.message = WM_SYSCOMMAND then
    if Msg.wParam = SC_MINIMZE then
    begin
      ZoomMinimized;
      Handled := True;
    end else
    if Msg.wParam = SC_RESTORE then
    begin
      SetActiveWindow(Application.Handle);
      SetWindowText(Application.Handle, 
        PChar(Application.MainForm.Caption));
      ShowWindow(Application.Handle, 
                 SW_RESTORE);
      SetWindowText(Application.Handle, 
                    PChar(AppWindowName));
      SetWindowPos(Application.Handle, 
                HWND_TOP, Screen.Width div 2, 
                Screen.Height div 2, 0, 0, 
                SWP_NOZORDER);
      Application.RestoreTopMosts;
      Handled := True;
    end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Length : Integer;

begin
  Length := GetWindowTextLength(
                       Application.Handle);
  SetLength(AppWindowName, Length);
  GetWindowText(Application.Handle, 
               PChar(AppWindowName), Length);
  Application.OnMessage := AppMessage;
end;

end.
   
.
.
.
. 1998 Larry J. Rutledge . editor: Larry J. Rutledge .
. Return to Top .
 

Components 101
Form Size Limiting
Unknown

Windows sends the WM_GETMINMAXINFO message when the user tries to resize a window either by clicking the maximize button or dragging the borders of the window. By creating a suitable message-response method, you can specify the minimum and maximum window sizes for that form.

If you want to limit a form's size using this technique, however, you'll need to make the modifications we suggested to each form's source code. Granted, you can simply copy and paste the source code from one project to the next, but it can be annoying to repeat this task for each size-limited form. As an alternative, you could create a form template, but you may find that you want to limit the size of forms you create from other templates.

In this article, we'll show you how to create the FormSizeLimit component, which encapsulates the work of responding to the WM_GETMINMAXINFO message. In addition, this component makes it easy for you to adjust the form's size limits dynamically, simply by changing the component's appropriate properties.

Messing with form messages
To respond directly in a form to the message WM_GETMINMAXINFO, you must create a new method that responds to this Windows message. Delphi simplifies this assignment by mapping all the standard messages. All you have to do is to add the message keyword and the corresponding message identifier to the end of a form-procedure method. Delphi will automatically call the new method when the form receives the WM_GETMINMAXINFO message. Unfortunately, it's not so simple to capture this message from a component on a form. After all, Windows won't send the WM_GETMINMAXINFO message to a component, since it's not trying to resize the component.

The secret to capturing this message from a component lies in providing a new window procedure for the form that will respond to the appropriate messages, and pass all others on to the form's original window procedure. If the user removes the component that provides the new window procedure, you'll need to make sure you reinstall the old window procedure.

Specifically, we'll want our component to respond to two messages that Windows will send to the enclosing form: WM_SIZE and WM_GETMINMAXINFO. We'll respond to the WM_SIZE message to add some handy design-time capabilities, and we'll respond to the WM_GETMINMAXINFO message to perform the runtime size-limiting function.

Sizing up FormSizeLimit
To use the FormSizeLimit component, simply add it to an existing form. When you do, you'll see the properties of the FormSizeLimit component appear in the Object Inspector. Most of the properties for this component are self-explanatory. For example, the FormSizeLimit component will limit the size of the form only when you've set the Active property to True. However, the AutoTrack property requires a bit of explanation. If you set the AutoTrack property to True, the FormSizeLimit component will monitor the changes you make to the size of the form at design-time, and it will adjust the MaxHeight and MaxWidth properties to the design size. If you reduce the size of the form to less than the values specified in the MinHeight and MinWIdth properties, the component will automatically reduce those properties to reflect the new size. This adjustment prevents a situation in which the minimum dimensions are greater than the maximum dimensions. To take advantage of the AutoTrack property, we suggest you use the following steps to set the minimum and maximum sizes:
  • Set the AutoTrack property to True.
  • Set the MinHeight and MinWidth properties to values that are a few pixels smaller than the MaxHeight and MaxWidth properties.
  • Reduce the size of the form to the minimum size you want at runtime.
  • Reset the AutoTrack property to False.
To download the source code, visit the file repository web-site.


unit Frmszlmt;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, 
  Classes;

type
  TMinSizeError     = class(Exception);
  TNotSizeableErro  = class(Exception);
  TNotOwnerError    = class(Exception);
  TDuplicationError = class(Exception);

  TFormSizeLimit = class(TComponent)
  private
    FActive        : Boolean;
    FAutoTrack     : Boolean;
    FAbsMinHeight  : Word;
    FAbsMinWidth   : Word;
    FMaxWidth      : Word;
    FMinWidth      : Word;
    FMaxHeight     : Word;
    FMinHeight     : Word;
    FFormHandle    : Hwnd;
    FWndHandlerPtr : Pointer;
    FOldWndHandler : Pointer;
    FMaxRange      : TPoint;
    FMinRange      : TPoint;

    procedure AdjustMinMaxMessage(MMPointer : 
                                  Pointer);
    procedure ChkMaxHeight(Setting : Word);
    procedure ChkMaxWidth(Setting : Word);
    procedure ChkMinHeight(Setting : Word);
    procedure ChkMinWidth(Setting : Word);
    procedure NWndProc(var Messg: TMessage);
    procedure SetNewSize(NewCLWidth, 
                       NewCLHeight : Word);
  protected
    { Protected declarations }
  public
    constructor Create(AOwner : TComponent); 
                override;
    destructor Destroy; override;

    procedure Loaded; override;
  published
    property Active    : Boolean  
        read FActive write FActive;
    property AutoTrack : Boolean
        read FAutoTrack write FAutoTrack;
    property MaxHeight : Word 
        read FMaxHeight write FMaxHeight;
    property MinHeight : Word 
        read FMinHeight write FMinHeight;
    property MaxWidth  : Word 
        read FMaxWidth write FMaxWidth;
    property MinWidth  : Word 
        read FMinWidth write MinWidth;
  end;

procedure Register;

implementation

uses
  Forms, Controls;

constructor TFormSizeLimit.Create(AOwner : TComponent);
var
  I                  : Word;
  FormSizeLimitCount : Byte;

begin
  FormSizeLimitCount := 0;
  inherited Create(AOwner);

  if (AOwner is TForm) then
  begin
    with (AOwner as TForm) do
    begin
      if csDesigning in ComponentState then
      begin
        for i := 0 to ComponentCount - 1 do
          if Components[i] is TFormSizeLimit 
            then inc(FormSizeLimitCount);

        if FormSizeLimitCount > 1 then
          raise TDuplicationError.Create(
                 'Already a FormSizeLimit ' +
                 'component on selected  ' +
                 'form');
        if BorderStyle <> bsSizeable then
          raise TNotSizeableError.Create(
                 'Form does not have ' +          
                 'BorderStyle of  '+
                 'bsSizeable');

        FMinHeight := 
                  GetSystemMetrics(SM_CYMIN);
        FAbsMinHeight := FMinHeight;
        FMinWidth := 
                  GetSystemMetrics(SM_CXMIN);
        FAbsMinWidth := FMinWidth;
        FMaxHeight := Height;
        FMaxWidth  := Width;
      end;
      FFormHandle := Handle;
      FOldWndHandler := Pointer(
                  GetWindowLong(FFormHandle, 
                  GWL_WNDPROC));
      FWndHandlerPtr := MakeObjectInstance(
                         NWndProc);

      if FWndHandlerPtr = nil then
        raise EOutOfResources.Create(
              'Windows Resources exhausted');
      SetWindowLong(FFormHandle, GWL_WNDPROC, 
                    LongInt(FWndHandlerPtr));
    end;
  end else
    raise TNotOwnerError.Create('A Form ' +
                       'MUST be the owner');
end;

procedure TFormSizeLimit.Loaded;
begin
  inherited Loaded;

  FMaxRange.X := FMaxWidth;
  FMaxRange.Y := FMaxHeight;
  FMinRange.X := FMinWidth;
  FMinRange.Y := FMinHeight;
end;

destructor TFormSizeLimit.Destroy;
begin
  FAutoTrack := False;

  SetWindowLong(FFormHandle, GWL_WNDPROC, 
                LongInt(FOldWndHandler));
  if FWndHandlerPtr <> nil then
    FreeObjectInstance(FWndHandlerPtr);

  inherited Destroy;
end;

procedure TFormSizeLimit.ChkMaxHeight(Setting 
                                     : Word);
begin
  if (csDesigning in ComponentState) and
     (not (csLoading in ComponentState)) then
    if (Setting < MinHeight) then
      raise TMinSizeError.Create('The ' + 
           'Maximum Height must not be' +        
            Chr(13) + 
           'less than the Minimum Height');

  FMaxHeight := Setting;
  FMaxRange.Y := Setting;
end;

procedure TFormSizeLimit.ChkMaxWidth(Setting     
                                    : Word);
begin
  if (csDesigning in ComponentState) and
     (not (csLoading in ComponentState)) then
    if (Setting < MinWidth) then
      raise TMinSizeError.Create('The ' + 
            'Maximum Width must not be' +            
            Chr(13) + 
            'less than the Minimum width');

  FMaxWidth := Setting;
  FMaxRange.X := Setting;
end;

procedure TFormSizeLimit.ChkMinHeight( 
                         Setting: Word);
begin
  if (csDesigning in ComponentState) and
     (not (csLoading in ComponentState)) then
  begin
    if (Setting < FAbsMinHeight) then
      raise TMinSizeError.Create('The ' +
              'Minimum Height must not' +  
               Chr(13) + 
              'be less than the caption  ' +
              'height');
    if (Setting >= FMaxHeight) then
      raise TMinSizeError.Create('The ' +  
           'Minimum Height must not be' + 
            Chr(13) +
           'greater than the Maximum ' +
           'height');
  end;

  FMinHeight := Setting;
  FMinRange.Y := Setting;
end;

procedure TFormSizeLimit.ChkMinWidth(Setting: Word);
begin
  if (csDesigning in ComponentState) and
     (not (csLoading in ComponentState)) then
  begin
    if (Setting < FAbsMinWidth) then
      raise TMinSizeError.Create('The ' +
       'Minimum width must not be less' +       
       Chr(13) + 
       'than the caption controls width');
    if (Setting >= FMaxWidth) then
      raise TMinSizeError.Create('The ' +
            'Minimum width must not be' + 
            Chr(13) + 
            'greater than the Maximum ' +
            'width');
  end;

  FMinWidth := Setting;
  FMinRange.X := Setting;
end;

procedure TFormSizeLimit.SetNewSize(NewCLWidth, 
                         NewCLHeight : Word);
var
  NewWidth  : Word;
  NewHeight : Word;
  SavActive : Boolean; 

begin
  NewWidth := NewCLWidth + 
          (GetSystemMetrics(SM_CXFRAME) * 2);
  NewHeight := NewCLHeight + 
         (GetSystemMetrics(SM_CYFRAME) * 2) + 
         (GetSystemMetrics(SM_CYCAPTION) - 
         (GetSystemMetrics(SM_CYBORDER));

  SavActive := FActive;
  FActive := False;

  if (NewWidth < MinWidth) then
    FMinWidth := NewWidth;

  if (NewHeight < MinHeight) then
    FMinHeight := NewHeight;

  MaxWidth  := NewWidth;
  MaxHeight := NewHeight;
  FActive   := SavActive;
end;

procedure TFormSizeLimit.AdjustMinMaxMessage(
                        MMPointer : Pointer);
var
  MinMaxPtr : ^TMinMaxInfo;

begin
  MinMaxPtr := MMPointer;
  MinMaxPtr^.ptMinTrackSize := FMinRange;
  MinMaxPtr^.ptMaxTrackSize := FMaxRange;
end;

procedure TFormSizeLimit.NWndProc(var Messg: TMessage);
begin
  with Messg do
  begin
    case Msg of
      WM_GETMINMAXINFO : if (FActive and not 
        (csDesigning in ComponentState)) then
        AdjustMinMaxMessage(Pointer(lParam));
      WM_SIZE : if (csDesigning in 
                    ComponentState) and 
                   (FAutoTrack) and
               (wParam = SIZE_RESTORED)) then
          SetNewSize(LoWord(lParam), 
                     HiWord(lParam));
    end;

    result := CallWindowProc(FOldWndHandler, 
              FFormHandle, Msg, wParam, 
              lParam);
  end;
end;

procedure Register;
begin
  RegisterComponents('Additional', 
                     [TFormSizeLimit]);
end;

end.
   
.
.
.
. 1998 Larry J. Rutledge . editor: Larry J. Rutledge .
. Return to Top .
 

Calendar of Events

Delphi SIG of the Sacramento PC User Group
Second Monday of every month at 6:30 pm
New Horizons Building - 1215 Howe Avenue



Sacramento PC User Group
Third Wednesday of every month at 7:00 pm
Sacramento Association of Realtors - 2003 Howe Avenue
For more information, visit their web-site




Our Sponsors


/




 
http://www.imiwest.com



http://www.newhorizons.com
.
.
.
. 1998 Larry J. Rutledge . editor: Larry J. Rutledge .
. Return to Top .


Advanced Delphi
Brendan Delumpa

Can I resize the drop-down list of a TComboBox so that I can see the complete text of its items?
There are two ways to approach this. One's quick and dirty, the other's a bit more involved, but is much more reusable (read: it's a component). You can decide which one you want to use. Let's start out with the quick and dirty method, shall we?

To resize the list box of a combobox, you merely need to send the Windows API message: CB_SETDROPPEDWIDTH to the combo box. Using the procedure function to implement the message, your call would be as follows:
ComboBox1.Perform(CB_SETDROPPEDWIDTH, <PixelWidth as Integer>, 0);
   
The best place to put this call would be in the OnDropDown method of the combo box. Here's how I implemented it:
procedure TForm1.ComboBox1DropDown(Sender: TObject);
begin
  // Set the width of the list to 200
  ComboBox1.Perform(CB_SETDROPPEDWIDTH, 200, 0);
end;
   
Pretty straight-forward, right? But there's one glaring weakness with this methodology: Your string items almost invariably will be different lengths, so the size you set may not be big enough. In that case, you have to loop through each item in the list and figure what the biggest one is, then set the size based on that.

I could give you the code that does that - in fact, you could probably figure that part out yourself. But think about this a moment, this would be pretty useful in all your applications that use combo boxes, and to have to cut and paste code would be a real chore everytime you wanted to implement this behavior. Do you see what I'm getting at? Right. Let's build a component.

The comments pretty much explain all the logic behind the component, so I won't bore you with those details. But you should note that I implemented both sizing behaviors in the code through the DropDownFixedWidth property. If the value of DropDownFixedWidth is greater than 0, then the component will size its list box to the size set for the property. If the value is zero, then it'll dynamically size to the largest string in the Items list. I did this to provide greater flexibility when using the component. While I'd personally just let the component decide the size of the list box, there just might be times when I'd want to set the width explicitly. With this component, I can do that.

There's a weakness in the design of this code, though. Can you figure it out? Install the component, drop it onto a new test application and run it. Read no further until you've done that .

Flawed Design?
If you haven't figured it out, place the component near the right edge of the form. Run your program again, and drop down the list. It gets cut off. For time's sake, I haven't even researched how to move the list box to accommodate form alignment of the combo box. If you figure it out, please let me know. I'll add your solution to this article and put your name in lights! Well, that's it for now!

To download the source code visit the file repository web-site.

{======================================================
 Enhanced TComboBox - Copyright   1998 Brendan V. Delumpa

 With this component, you have two ways to resize the
 drop-down list. The first way is to directly set the
 DropDownFixedWidth property to some integer value
 greater than zero. The second way involves leaving
 the DropDownFixedValue property at 0, and the list
 will automatically be sized to the longest string.
 ========================================================}
unit EnhCombo;

interface

uses
  Windows, Messages, SysUtils, Classes, 
  Graphics, Controls, Forms, Dialogs, 
  StdCtrls;

type
  TEnhCombo = class(TComboBox)
  private
    FItemWidth : Integer;
    FDropDownFixedWidth : Integer;

    procedure SetDropDownFixedWidth(
               const Value : Integer);
    function GetTextWidth(S : String) : 
             Integer;
  protected
    procedure DropDown; override;
  public
    constructor Create(AOwner : TComponent); 
                override;
    property ItemWidth : Integer 
             read FItemWidth 
            write FItemWidth;
  published
    property DropDownFixedWidth : Integer 
             read FDropDownFixedWidth 
            write FDropDownFixedWidth;
  end;

procedure Register;

implementation

constructor TEnhCombo.Create(AOwner : 
                             TComponent);
begin
  inherited Create(AOwner);
end;

procedure TEnhCombo.DropDown;
var
  I : Integer;

begin
  inherited DropDown;
  ItemWidth := 0;

  { Check to see if DropDownFixedWidth > 0. 
    Then just set the width of the list box. 
    Otherwise, loop through the items and set 
    the width of the list box to 8 pixels > 
    then the widest string to buffer the 
    right side. Anything less than 8 for some 
    reason touches the end of the item on 
    high-res monitor settings.}

if (FDropDownFixedWidth > 0) then
    Self.Perform(CB_SETDROPPEDWIDTH, 
                 FDropDownFixedWidth, 0)
  else
  begin
    for I := 0 to Items.Count - 1 do
      if (GetTextWidth(Items[I]) > ItemWidth) then
        ItemWidth := GetTextWidth(Items[I]) + 8;
    Self.Perform(CB_SETDROPPEDWIDTH, ItemWidth, 0);
  end;
end;

function TEnhCombo.GetTextWidth(S : String) : Integer;
begin
  result := TForm(Owner).Canvas.TextWidth(S);
end;

procedure TEnhCombo.SetDropDownFixedWidth(const Value : Integer);
begin
  FDropDownFixedWidth := Value;
end;

procedure Register;
begin
  RegisterComponents('BD', [TEnhCombo]);
end;

end.
   
.
.
.
. 1998 Larry J. Rutledge . editor: Larry J. Rutledge .
. Return to Top .
 

Questions and Answers


Question:

I have a form with two TEdit components on it. For the OnChange event for both, it clears the contents of the TEdit that is not changing. However, when clearing on TEdit the OnChange fires and clears the other TEdit, this then causes the OnChange in the other TEdit to fire. Fortunately, the second time the OnChange hits the original TEdit, it is already clear and nothing happens.

How can I prevent the circular event firing?


Answer:

The simplest way to prevent the circular event firing is to check the form's ActiveControl property. This property indicates which component currently has the focus.

The OnChange event should read as follows:
procedure TForm1.EditChange(Sender: TObject);
begin
if TEdit(Sender).Name <>    
   TEdit(ActiveControl).Name then
     begin
       // do processing
    end;
  end;
   
.
.
.
. 1998 Larry J. Rutledge . editor: Larry J. Rutledge .
. Return to Top .


Comments

0 Comments.
Share a thought or comment...
 
Write a Comment...
...
Sign in...

If you are a member, Sign In. Or, you can Create a Free account now.


Anonymous Post (text-only, no HTML):

Enter your name and security key.

Your Name:
Security key = P1202A1
Enter key:
KB Post Contributed By Larry J. Rutledge :
I worked for Prestwood Software as a Delphi developer from 1997 through 2002. During that time I enjoyed working with Mike Prestwood and the other developers at Prestwood.
Visit Profile

 KB Article #101674 Counter
7035
Since 12/9/2008

Follow PrestwoodBoards on: 


©1995-2020 PrestwoodBoards  [Security & Privacy]
Professional IT Services: Coding | Websites | Computer Tech