An area of the VCL new in XE6 and XE7 is the support for Windows taskbar buttons customization with two new components, TTaskBar and TJumpList

As promised earlier (blog.marcocantu.com/blog/2014-september-building-vcl-apps-xe7.html) here is a first technical blog post on the VCL in Delphi XE7.

While most existing Windows applications run fine on new versions of the operating system, they often lack a good level of integration with some of the new OS features introduced in Windows 7 and Windows 8. One of these features is the ability to customize the TaskBar button, the graphical element indicating that an application is running and placed in a taskbar that is by default at the bottom of the screen.

TaskBar Buttons in Windows 7 and Windows 8

The operating system allows for several customizations of these taskbar buttons:

Adding overlay icons on top of the program icon, to indicate status. An example could be showing you have pending notifications or to do items

Showing a progress bar into the taskbar button, so that a user can see the status of a lengthy operation (for example, when you copy or download a file)

Customizing the preview displayed as you hoover over the taskbar button, allowing also multiple elements (multiple forms, multiple tabs)

Adding action buttons in the frame of this preview

Adding custom items to the taskbar button menu, using jump lists

Now the great news is Delphi XE7 directly support all of the features above with specific easy-to-use components. All of the features save for the last one were supported in XE6 using the Taskbar component, while the last one is supported in XE7 using the JumpList component:

Taskbar Buttons

There are several operations you can do on the Taskbar component, highlighted by a few demos that ship since XE6. For example, you can set a progress bar in the application task bar button with two lines of code:

Taskbar1.ProgressMaxValue := TrackBar1.Max;

Taskbar1.ProgressValue := TrackBar1.Position;

Another handy feature is adding a overlay icon, given an image:

Taskbar1.OverlayIcon := Image1.Picture.Icon;

This is the taskbar button of a Delphi application with a progress bar and a email icon overlay:

There is a lot more you can easily do, like trimming the preview and adding extra border icons. This is the result of these settings (for the code you an refer to the OneFormApp demo under the VCL/Taskbar section):

Jump List

Another relevant feature of the taskbar buttons is the ability to customize their local menu. This is a feature Microsoft calls jump list. You can add files or special information to those menu items, like Chrome does:

Defining these items using the new TJumpList component is quite simple. You have a main list plus multiple categories with sub-lists. In this case the sub-list is initially empty:

object JumpList1: TJumpList AutoRefresh = True Enabled = True ApplicationID = 'JumpListAppID' CustomCategories = < item CategoryName = 'Dropped Files' Items = <> end> ShowRecent = True ShowFrequent = True TaskList = < item FriendlyName = 'Sample' Arguments = 'SampleParameter' end item FriendlyName = 'Second' Arguments = 'SecondParameter' end> end

If you look at the events of the component, though, it is not obvious how to process the selection of these menus. When a user selects these items, in fact, the application is not directly notified: The system starts a new instance of the application, passing the specific value as a command line parameter.

If all you need to create multiple instances (for example, because each document is managed by a different application instance) the system behavior makes it easy. But if you want to show multiple documents in a single tab, locate a record, open a new form within the same application, than there is some work you have to do. In short, you have to let the new instance notify the existing one that an operation has been requested.

A way to handle this scenario is to use a global mutex to keep track of the execution status, and if an existing instance exists, find its main form and forward the information received by the second instance as a command line parameter to the main form of the existing instance. Clear as mud? Here it is in code. First, change the VCL project startup code:

// check if mutex already exists HMutex := CreateMutex(nil, False, 'JumpListFilesDemoMutex'); if WaitForSingleObject(hMutex, 0) <> wait_TimeOut then begin Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TJumpListForm, JumpListForm); Application.Run; end else ActivateWindow (TJumpListForm);

Now what’s missing is the code for ActivateWindow (which I’ve made generic enough so it can be reused). The procedure sets a couple of global variables needed by the EnumWndProc function (omitted from this code) which scans all of the current running applications for the correct form. If that form is found, the ActivateWindow procedure creates the proper data structure to pass the command line parameter received using Windows wm_CopyData message:

procedure ActivateWindow (aWindowClass: TClass); var cds: CopyDataStruct; fFileName: string; i: integer; begin MainWindowClass := aWindowClass; // get the current module name SetLength(ModuleName, 1024); GetModuleFileName(HInstance, PChar(ModuleName), Length(ModuleName)); ModuleName := PChar(ModuleName); // adjust length // find window of previous instance EnumWindows(@EnumWndProc, 0); if FoundWnd <> 0 then begin // show the window, eventually if not IsWindowVisible(FoundWnd) then PostMessage(FoundWnd, wm_User, 0, 0); SetForegroundWindow(FoundWnd); // grab the file name(s) from the command line parameters for i := 1 to ParamCount do begin fFileName := ParamStr(i); // prepare the data to copy cds.dwData := 0; cds.cbData := (length(fFileName) + 1) * 2; cds.lpData := PChar(fFileName); // send the data SendMessage(FoundWnd, wm_CopyData, 0, Integer(@cds)); end; end; end;

So the last bit of code is in the main form, to receive this message, and also handle dropping files to it:

// in TJumpListForm class procedure DropFiles(var Msg: TWmDropFiles); message wm_DropFiles; procedure CopyData(var Msg: TWmCopyData); message wm_CopyData;

This is the method for receiving the data from the other instance, and calling OpenFile (which in my implementation just adds the file name to a listbox):

procedure TJumpListForm.CopyData(var Msg: TWmCopyData); var Filename: string; begin // restore the window if minimized if IsIconic(Application.Handle) then Application.Restore; // extract the filename from the data SetLength(FileName, Msg.CopyDataStruct.cbData); StrCopy(PChar(FileName), Msg.CopyDataStruct.lpData); Filename := PChar(FileName); // open the file OpenFile (FileName); end;

Finally, if you drag and drop a file to the form from Windows resource explorer, it is added to the display, but also to the jump list:

procedure TJumpListForm.DropFiles(var Msg: TWmDropFiles); var nFiles, I: Integer; Filename: string; jumpItem: TJumpListItem; begin // get the number of dropped files nFiles := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, 0); // for each file try for I := 0 to nFiles - 1 do begin // allocate memory SetLength(Filename, 1024); // read the file name DragQueryFile(Msg.Drop, I, PChar(Filename), 1024 * 2); // normalize file Filename := PChar(Filename); // open the file OpenFile(FileName); // add file to Jump List jumpItem := JumpList1.CustomCategories[0].Items.Add as TJumpListItem; jumpItem.FriendlyName := ExtractFilename (Filename); jumpItem.Arguments := Filename; // full path end; finally DragFinish(Msg.Drop); end; end;



This is a sample output of the program:



Now this seems quite a lot of work, but this is not required to handle JumpLists in case of a single document interface with multiple instances of the same application. The extra code is required to handle multiple flies or documents or pages in a single instance but the same code you’ll have to write, for example, to implement associating the file extension to your application, or dragging a file over its executable. Once you have that structure, you’ll be ready to implement jump lists in no time.

Conclusion

While the first demo, the TaskBar one, ships with Delphi, the JumpList is an old demo I adapted. Most of the code is here, but the complete source is avaialble at code.marcocantu.com/trac/marcocantu_delphi_sessions_demos/browser/windows/JumpListFilesDemo. So these two combined are a significant feature to let your VCL applications behave properly and look modern in Windows 7 and Windows 8. Adding styles might be next...