Creating Windows Vista Ready Applications with Delphi

Update - January 2007

You have found my original article on creating Windows Vista ready applications with Delphi. Since originally publishing this article, I have posted a follow-up article that addresses many responses to this write-up. Please visit the update here for many more tips and tricks.

So, Your Applications “Work”

By now, hopefully, you, as a responsible application developer, have downloaded a few of the public beta releases of Microsoft’s next operating system, Windows Vista, and made sure your applications work under the new OS.

While 99% of well-written Delphi applications will work just fine under Vista (issues such as admin-rights, elevation, and UAC, are outside the scope of this article), they will certainly not lend to the new user experience improvements found in Windows Vista. In fact, the more you look into the new user experience features in Vista, the more you’ll find your applications stick out like a sore thumb.

However, do not fret! It is entirely possible, and fairly easy, to overcome these discrepancies, and make your applications look, and feel, 100% at home in the new user experience found in Windows Vista. Let’s examine some of the quirks found in Delphi-brewed applications and what we can do to overcome them, as well as some extra steps we can take to make our applications fit in.

Our Sample Application

The sample application we will be working on throughout this tutorial is a simple notepad-like program with a couple of forms and a few actions. It allows you to load and save text and show an “about” dialog. It will also prompt you to save your changes when opening a file or exiting the application, if changes have been made to the document.

Sample Application - Step 1

Download Sample Application – Step 1

Now, while our sample application works just fine under Windows Vista, there are many problems under the hood when it comes to our application fitting in with the user experience found in Vista.

Oh No…Not Again (or, New Fonts)

The first problem, easiest to fix – and most time consuming in full-sized applications – is the font. With Windows Vista comes a new default font for user interfaces: Segoe UI. Now, this is only half the problem. The interesting bit is, not only is there a new default font, but the size of the default font has been increased as well. By default, applications in Windows XP use Tahoma 8. Applications (that want to fit in) in Windows Vista, however, use Segoe UI 9. This means, not only do we need to change our fonts (if we are running under Windows Vista), but we’ll also need to check each screen to make sure all text is still visible, making many adjustments here and there as needed.

Let’s begin by creating a new unit called uVistaFuncs.pas. This is where we’ll be adding our Vista-specific methods. To this, we’ll add methods for checking to see if we’re running under Windows Vista and for applying the proper user interface font and size to a given form. Now, in the FormCreate of both our main form and our about dialog’s form, we simply need to call SetVistaFonts(Self), ensuring that uVistaFuncs is in our uses clause.

You’ll notice that the title in the about dialog still uses the default font, Tahoma. This is because we changed the font at design time and ParentFont is now False. You will see this a lot in your applications. The best way to make sure this is taken care of is to edit uVistaFuncs and change “Segoe UI” to “Segoe Script”. This will make it much more obvious, within the running application, where extra changes need to be made. In our case, we simply need to assign TitleLabel.Font.Name after calling SetVistaFonts.

Sample Application - Fixed Fonts

Download Sample Application – Step 2

Where’s My Induction (or, Why the Secret Window)?

Windows Vista features several elements that help to facilitate an inductive user experience. You can read an (albeit dated) article on inductive user interfaces here. Basically, an inductive UI actively assists the user in understanding the paradigms seen on screen.

Some examples of this in Windows Vista include the new animations seen when minimizing and maximizing an application. As Ales Holecek mentions in his interview here, many users do not understand the concept of a window and its corresponding taskbar button. They do not grasp where a window goes when it is minimized and its coupling with its taskbar button. To help remedy this (and show off the 3D UI, Aero) in Vista, windows are smoothly animated in 3D to, and from, their taskbar button when minimizing and maximizing a window.

Go ahead. Try it out for yourself…but not with a Delphi application! If you minimize or maximize a Delphi made application under Vista, the animations seen will be that of closing or showing a Window: the Window fades away as it falls backwards or fades back in as it is shown. The reason for this is that Delphi applications all have a hidden, secret “application form”. This hidden form is the owner of the taskbar button you see in your Delphi applications (and is also the reason your Delphi applications’ taskbar context menus are different than other applications). When you minimize or restore your forms in Delphi, the inner magic of Delphi hooks onto those messages and simply hides or shows the active forms instead. Your forms never really get minimized or maximized in the true sense of the words.

Let us digress for a moment to discuss another new feature of Windows Vista. We will bring the article back to our Secret Window soon. This new feature in Vista, also an element of the inductive user interface, allows us to see a preview of our windows simply by hovering over our application’s taskbar button. This eases searching for the right taskbar button to click and is, arguably, cool. This same technique is also used to render the new Flip 3D, a 3D representation of the classic ALT+TAB screen. If you try this with a normal, restored Delphi application, all works well. However, if you try this with a minimized Delphi application, you will see a blank window with your application’s icon instead of the real time preview.

Thumbnail Preview – Restored

Thumbnil Preview - Restored

Thumbnail Preview - Minimized

Thumbnil Preview - Minimized

Flip 3D with Minimized Delphi Application

Flip 3D with Minimized Delphi Application

Why do our Delphi applications behave differently? Is this some sort of evil plot? No! It’s the Secret Window! When you hover over the taskbar button, and a form is restored, the application’s active form is shown in the preview. However, when all forms are minimized, the hidden “application form” is shown in the preview.

Now, let’s get to work on fixing this problem! We will do so by employing techniques described by Peter Below here. The first thing we need to do is get rid of the taskbar button for our hidden “application form”. In the FormCreate of our application, we need to adjust the flags on our “application form”:

ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
  GetWindowLong(Application.Handle, GWL_EXSTYLE) and not WS_EX_APPWINDOW
  or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);

Next, for each form we would like to have its own taskbar button (at least our main form), we need to override CreateParams:

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin  
  inherited CreateParams(Params);  
  Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or
    WS_EX_APPWINDOW;
end;

Finally, for each form given both its own taskbar button and the ability to minimize, we need to handle the windows message WM_SYSCOMMAND:

interfaceprotected
procedure WMSyscommand(var Message: TWmSysCommand); message WM_SYSCOMMAND;
…
implementationprocedure TMainForm.WMSyscommand(var Message: TWmSysCommand);
begin
  case (Message.CmdType and $FFF0) of
    SC_MINIMIZE:
    begin
      ShowWindow(Handle, SW_MINIMIZE);
      Message.Result := 0;
    end;
    SC_RESTORE:
    begin
      ShowWindow(Handle, SW_RESTORE);
      Message.Result := 0;
    end;
  else
    inherited;  
  end;
end;

This stops Delphi’s internal workings from overriding our forms’ minimizing and maximizing, causing them to hide and show instead. Now our forms should behave properly. They animate properly when minimized and maximized, and they display their thumbnails properly when minimized to the taskbar.

However, we are left with one major problem. If we were to run our sample and click Help, followed by About, and then finally click our form’s taskbar button, the form would be brought in front of our about dialog! This is because, by default, Delphi sets the hidden “application form” as the parent of modal forms. To fix this, you must set Form.PopupParent before every call to Form.ShowModal:

procedure TMainForm.AboutActionExecute(Sender: TObject);
begin
  AboutForm := TAboutForm.Create(Self);
  try
    AboutForm.PopupParent := Self;
    AboutForm.ShowModal;
  finally
    FreeAndNil(AboutForm);
  end;
end;

Download Sample Application – Step 3

Make Your Applications Shine

Unless you’ve been living under a rock, you know that applications running under Windows Vista (on a machine with a 3D graphics accelerator) are rendered with a transparent, glass-like border and non-client area. This is called, aptly, Glass. There are API calls within the DWM (Desktop Windows Manager) that allow you to extend this Glass surface into the client area of your applications. While not necessary, it can help your application fit in as a “Vista” application.

We’ll get started by adding a few new functions to our uVistaFuncs.pas file: CompositingEnabled and ExtendGlass. The source for these methods is based on the code found here. CompositingEnabled returns whether 3D capabilities are currently enabled, and ExtendGlass allows you to extend the Glass surface into the client area of our forms. Next, we’ll add a panel to the bottom of our form. The reason for this is that the DWM API calls for extending Glass require a black surface on which to render. Finally, we add a new method to our main form that checks to see if compositing is enabled and, if so, set the panel black and calls ExtendGlass to draw Glass on our main form:

Sample Application - With Glass

Download Sample Application – Step 4

When is a Prompt not a Prompt (or, Task Dialogs)

Windows Vista introduces a new user experience element called the Task Dialog. It brings many different dialogs under a single API to provide a consistent experience. While it is possible to create extremely complex dialogs with the Task Dialog API, we will start with a simple replacement for MessageDlg. Our method will use Vista Task Dialogs if available, and fall back on calls to MessageDlg when needed. There is a wonderful example of a method for doing this here.

Once we add the new method for using Task Dialogs to our uVistaFuncs.pas unit, a simple change to our SaveHandled method allows our prompt to both fit in better with the Vista user experience and provide more thorough feedback to the end user.

Regular Style Message Dialog

Regular Style Message Dialog

New Windows Vista Task Dialog

New Windows Vista Task Dialog

Download Sample Application – Step 5

Image is Everything

We’re almost there. This change really should have been made when porting our application’s look from Windows 2000 to Windows XP, but, in this example, our application interface is very far behind. What we need to do next is replace the flat, pixilated images in our application with nice, anti-aliased and, possibly shadowed, alpha blended images. I’ve found that the best way to do this is by using PngComponents and the TPngImageList, found here. This simple replacement for TImageList allows us to load alpha blended PNG images into an image list and use them throughout our application.

Sample Application - Alpha Blended Images

Download Sample Application – Step 6

New Common Dialogs

Windows Vista introduces new common dialogs, including redesigned open and save dialogs. These new common dialogs include several features new to Windows Vista, including the new Favorite Links area (customizable via right-click context menu), new views, and a collapsible save dialog.

Old Style Save Dialog

Old Style Save Dialog

New Style Save Dialog – Collapsed

New Style Save Dialog - Collapsed

New Style Save Dialog – Expanded

New Style Save Dialog - Expanded

Unfortunately, applications written in Delphi will not automatically use the new style for open and save dialogs. This is due to flags used in the Dialogs.pas source code, namely OFN_ENABLEHOOK and OFN_ENABLETEMPLATE. For more information, see the Quality Central report here.

To work around this problem:

  • First make a copy of Dialogs.pas and put that copy either in our application’s source directory or somewhere on our library path.
  • Second, in that copy of Dialogs.pas, search for “OFN_ENABLEHOOK”. You should see the code “Flags := OFN_ENABLEHOOK;”. Comment this out
  • Add the line “Flags := 0;” after the code commented out above
  • Search for “OFN_ENABLETEMPLATE”. Comment out the lines starting two lines above the first instance of OFN_ENABLETEMPLATE, “if Template <> nil then”, and ending with “hWndOwner := Application.Handle;”. This should be 20 or so lines of code.
  • Add the line “hWndOwner := ParentWnd;” after the code commented out above
  • Navigate to the method “TCommonDialog.Execute”. Comment out the “if”, “begin”, “end”, “else”, and “ParentWnd := Application.Handle”. This should leave you with code that always tries to set ParentWnd to the active form’s handle, falling back on Application.Handle.
  • Save your changes to your copy of Dialogs.pas

Our sample application should now use the new save and open dialogs. Step one allows us to use a modified copy of the VCL without modifying the original file itself. Step two removes the first offending flag, but also means our dialogs will not be centered. Step three is just an extension of step two, setting an initial flag of 0. Step four removes the second offending flag, plus code that no longer works to center our dialogs. Steps five and six fix the lack of centered dialogs, parenting our dialogs on the active form.

As this section involves altering the VCL source, I cannot post the source. However, following the steps above should allow all of your applications to use the new style open and save dialogs without any source changes. If you would rather not make changes to Dialogs.pas, see this page for steps on using the new open and save dialogs with API calls.

Download Final Compiled Executable

Conclusion

The user experience changes in Windows Vista, while major, are not as breaking as those found with Windows XP. With Windows XP, in order for our applications to fit in, we needed to either use a third party theme manager or wait for Delphi 7. With Windows Vista, as long as our applications were developed to work with Windows XP, we will automatically benefit from our applications matching the new visual style found in Windows Vista.

However, starting with Windows Vista, Microsoft is focusing much more on all of the small details that encompass the user experience delivered by the home computer. This means that, if we, as developers, want our applications to fit in with that consistent user experience, we too must focus on the details of our customers’ experiences.

While the next version of Delphi will undoubtedly address several of the discussed issues, I personally do not want to be held to upgrading my development environment just to have my applications fit in with a new operating system. The above code changes allow us to deliver a rich, consistent user experience with our applications, running under Windows Vista, today.

Below are some more links to articles and tutorials involving Windows Vista and user interfaces: