Archive for the ‘VCL’ Category.

Introducing Demo Browser… (from the pile of never to be released…)

It is interesting what you find in your drafts folder. From January 20, 2012! I guess my interests got consumed elsewhere. Still think the idea has merit.

I’m sure everyone has a stack of ideas that never really get off the ground for one reason or another. This is just one of plenty for me!

NOTE: This was used for the JSDialog Pack demos way back when so it kind of got released for in-house use.


The Demo Browser is an application that allows component vendors to showcase the demos that come with their products.

One of the main issues developers face is how to make “my application” do what is being done in a screen shot shown on the particular vendors site. I know this because I am also a consumer of third party products and trying to work out how a component works can be time consuming. The aim of Demo Browser is two fold, firstly allow a products demos to be located in a common front end and secondly to help spotlight important features within those demos.

The idea originally came about when seeing a number of questions about Delphi specific features that were covered by sample applications that shipped with the product. It is fine to tell them to go and search through a bunch of directories (some Delphi versions he “hide” the demo folder is some obscure location – it has since become more obvious where the demos are, but I digress).

So a central location for demos is what Demo Browser is.

Some History

Like most things I develop I usually mention it to a number of people I consider my sounding board. They seem to be very good at determining what might be a good thing and what may not (even though we don’t always see eye to eye – which is a good thing). I first bounced the Demo Browser idea off a former Borland (yes a while ago now) employee whom I had coffee and social chats with on a regular basis. So the first Demo Browser prototype was dynamic. It could read in the demos folder of your Delphi installation and process the information into a nice central view. Here are some of the initial screen captures (these were created August 3, 2007).

Demo Browser prototype

Demo Browser: summary

Demo Browser: readme

Demo Browser prototype

Demo Browser: screen capture

Demo Browser: filtering

Demo Browser prototype

Demo Browser: filter view

Demo Browser: video

JSDialog Pack – Upcoming feature (custom command link colors)

A new feature in the next release of JSDialog Pack will be the ability to customise the colors used for command link buttons. Since the introduction of VCL Styles in XE2, dialogs can undertake a radial change in colors. While currently dialogs cater for this new feature, the next release will allow greater influence over the colors used for the dialog.

You can download a preview of the editor used to create, modify and save a color scheme. While the UI of this editor will most likely change – the file format shouldn’t need changes. So you can safely save any schemes you come up with for use when the feature is released.

http://www.jed-software.com/files/CommandLinkColorEditor.zip

Command Link Color Scheme - default

 

Command Link Color Scheme - custom

A toolbar that works on glass

While redesigning JSDialog Creator I decided to spruce up the user interface by taking advantage of the glass feature available in recent operating systems.

I wanted to have a toolbar at the top and the ability to change active pages at the bottom of the screen with no other visual distractions at the bottom. Initially for the toolbar I tried the TToolbar component. Unfortunately that control looks awful when double buffered (and not on glass), and looks just as bad when placed on glass.

Here are some captures showing the various issues with TToolbar rendering on glass.

TToolbar on glass
TToolbar on glass
TToolbar on glass
TToolbar on glass

What to do?

The solution is actually contained in the VCL already, it just hasn’t been surfaced for you. The Quick Access Toolbar of the TRibbon component paints correctly so we’ll take advantage of this.

First we need to create a new TActionToolbar descendant. For my use I’ve called it TJSActionToolbar, you can call it whatever you want. Only the WM_Paint message needs to be handled in this new class. Here is the implementation for the toolbar, but it isn’t the entire implementation (unfortunately).

unit JSActionToolbar;

interface

uses
  ActnCtrls, Messages;

type
  TJSActionToolbar = class(TActionToolbar)
  protected
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  end;

implementation

uses
  Windows, Classes, Graphics, Controls, uxTheme;

{ TJSActionToolbar }

procedure TJSActionToolbar.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  PS: TPaintStruct;
  PaintBuffer: HPAINTBUFFER;
  LBlackBrush: HBRUSH;
begin
  if not (csDesigning in ComponentState) and DoubleBuffered and (csGlassPaint in ControlState) then
  begin
    DC := BeginPaint(Handle, PS);
    try
      PaintBuffer := BeginBufferedPaint(DC, PS.rcPaint, BPBF_COMPOSITED, nil, MemDC);
      if PaintBuffer <> 0 then
        try
          LBlackBrush := CreateSolidBrush(ColorToRGB(clBlack));
          FillRect(MemDC, ClientRect, LBlackBrush);
          DeleteObject(LBlackBrush);
          Perform(WM_PRINTCLIENT, WParam(MemDC), PRF_CLIENT);
        finally
          EndBufferedPaint(PaintBuffer, True);
        end;
    finally
      EndPaint(Handle, PS);
    end;
  end
  else
    inherited;
end;

end.


The problem is that the Quick Access Toolbar doesn’t allow captions for the commands, so painting text wasn’t an issue, where in this case it might be. To address this we need to add a couple of if statements into the ActnMan unit. Don’t worry, they are simple changes and easy to include.

The easiest way to make VCL unit changes is to copy the unit into the project folder you are going to be using it in. This is easily done with the File Actions expert included in XE Plus Pack, but I’ve got time to wait for you to do it manually………….

We need to paint on glass, which means using the specific theme DrawText method.

To do this I created a new local method called DrawOnGlass. Here it is:

procedure DrawOnGlass(ADC: HDC; const AText: UnicodeString; var ATextRect: TRect;
  ATextFlags: Cardinal; const AGlowSize: Integer; const AFontColor: TColor);
var
  Options: TDTTOpts;
  LDetail: TThemedElementDetails;
begin
  FillChar(Options, SizeOf(Options), 0);
  Options.dwSize := SizeOf(Options);
  Options.dwFlags := DTT_TEXTCOLOR or DTT_GLOWSIZE or DTT_COMPOSITED;
  if ATextFlags and DT_CALCRECT = DT_CALCRECT then
    Options.dwFlags := Options.dwFlags or DTT_CALCRECT;
  Options.crText := ColorToRGB(AFontColor);
  Options.iGlowSize := AGlowSize;
  LDetail := ThemeServices.GetElementDetails(teEditTextNormal);
  DrawThemeTextEx(ThemeServices.Theme[teEdit], ADC, LDetail.Part, LDetail.State, AText, Length(AText), ATextFlags, ATextRect, Options);
end;


Place this method above the TCustomActionControl.DrawText method in the ActnMan.pas unit. This code has been lifted from the TCustomLabel.DoDrawThemeTextEx method in the VCL, and reformatted to how I (mostly) format my code.

When glass was added (or the version after) a new ControlState was added to the VCL. This control state is csGlassPaint. If a control is to be painted on glass, the controls ControlState property includes csGlassPaint. This makes it easy to identify when we need to call the new DrawOnGlass method, instead of the Windows.DrawText method that is currently done.

Below is the updated version of the TCustomActionControl.DrawText method:

procedure TCustomActionControl.DrawText(var ARect: TRect; var Flags: Cardinal;
  Text: string);
begin
  if csGlassPaint in ControlState then
    DrawOnGlass(Canvas.Handle, Text, ARect, Flags, 5, Canvas.Font.Color)
  else
    Windows.DrawText(Canvas.Handle, Text, Length(Text), ARect, Flags);
end;


The same if statement needs to also be added to the TCustomActionControl.DrawShadowedText method in two places, to make sure disabled command text is displayed correctly.

There is one final step that is required to have this work. When you select the style on the TActionManager component, it MUST be one of the office styles. If you select a non-office style, the controls won’t paint correctly. Since the office styles use a 32-bit bitmap skin to paint elements, it works fine on glass.

NOTE: The only difference between the selected styles is the used font color. The hot, pressed, checked, down states are the same for each style.

If you’ve done everything correctly, it should look something like the images below.

TToolbar on glass
TToolbar on glass

Disclaimer: I’ve tested this under the following scenario – Large Normal Buttons. I don’t expect issues with button based items, but some of the more complex command styles available may have issues.

Summary

For this to work correctly remember the following points:

  1. Create a new TActionToolbar descendant that handles the WM_PAINT message
  2. Copy the ActnMan.pas unit to your projects folder
  3. Add the DrawOnGlass method to the unit
  4. Modify the TCustomActionControl.DrawText and TCustomActionControl.DrawShadowText methods to check and use the new draw on glass method
  5. When using the new toolbar in an application, make sure the Action Manager is using an office based style

When I get a chance (motivated), I’ll add a QC entry and perhaps this might be included in a future release, without this workaround being needed.

JSDialog Pack: Dialog Creator Preview

A tool to help make the creation of JSDialog Pack dialogs even easier is currently in the works. With the Dialog Creator application you can complete the sections you want the dialog to support. Once finished you can instantly preview the final look and then either copy the code required to generate the dialog, or copy the dfm required to paste it directly on a form.

Dialog Creator

Preview

Code Generation

To prove it is currently being tested, the code generation should probably call the method that actually displays the dialog!

The initial release will just support the creation of TJSDialog dialogs, with other dialog (Edit, Dual Edit [new in next release], Password and Check) support to be added in the future. As well as other code generation features and also a reusable gallery that contains the ability to save and load regularly used dialogs.

A new version of JSDialog Pack is almost ready to be released, hopefully sometime next week.

 

JSDialog Pack users get a 50% discount off the initial XE Plus Pack purchase, making it just 20 Euros! Contact support for details.

Sneak Peak: XE Plus Pack

I’m getting very close to releasing a new product called XE Plus Pack.

This product will include a number of experts that I’ve been personally using. Some for a number of years and some very new. Ultimately the idea behind the XE Plus Pack is that it will continue to grow over time to include new Experts, Property Editors, Component Editors and Utilities for newer IDE versions.

The first release will cover Delphi XE.

While the sneak peak shows five experts listed in the new JED Software Third Party Options section, only four will be released initially. The fifth (Bookmark Guide) still needs to bake a little bit before being released.

 

XE Plus Pack will sell for 40 Euros (50% discount for JSDialog Pack users and Free for current Visual Forms users). Yearly renewal of 20 Euros applies for updates and to receive newer experts. Users will have the opportunity to vote for which expert gets released next (I have an extensive list earmarked for the product).

 

Initially XE Plus Pack will include:

  • Visual Forms
  • Activate Project
  • File Actions
  • Code Template Wizard

 

Upcoming items that will be included with this product:

  • (Expert) Bookmark Guide
  • (Expert/Utility) Revamped Delphi Configuration Manager
  • (Utility) Enhanced Configuration Launcher
  • (Utility/Expert) Extensive IDE Options import/export application that can run from removable media
  • (Utility) Client DataSet Editor – developed while creating QC Plus

NOTE: Some utilities won’t be limited to Delphi XE IDE versions.

 

I will be doing some videos to introduce the collection and specific functionality.

XE Plus Pack - Options Integration

XE Plus Pack - Options Integration

VCL Ribbon – Ribbon Group Component Editor

I’ve created a component editor specifically for the TRibbonGroup component. With this component editor it will hopefully make it a little easier to layout your ribbon groups.

It has a number of commands for adding controls to the group as well as changing the alignment and vertical row count for the group.

Download

Delphi 2010 version

Delphi 2009 version

Install

  1. Open up your Delphi version.
  2. Select the Components | Install Packages menu item.
  3. Click on the Add button and select the extracted BPL from the downloaded zip file.

The component editor should now be available when you click on a group on the ribbon.

Some screen captures

Available commands for the Component Editor

Available commands for the Component Editor

When you select a command, you can also choose what actions to create as that command.

When you select a command, you can also choose what actions to create as that command.

You can create the different types of Small and Large buttons. NOTE: Selecting the dropdown button is a little different to the others.

You can create the different types of Small and Large buttons. NOTE: Selecting the dropdown button is a little different to the others.

When you are creating a dropdown button you are selecting that actions that should be added to the button as dropdown items. Only one button is created. You can also enter in the button caption.

When you are creating a dropdown button you are selecting that actions that should be added to the button as dropdown items. Only one button is created. You can also enter in the button caption.

VCL Ribbon – Context Tabs

Context Tabs was bought up in a comment to my MDI post.

I’ve been putting considerable effort into getting this working for one of my personal projects. I think it looks pretty good.

Design Time

Design Time

 

Runtime

Runtime

 

Runtime (second tab)

Runtime (second tab)

Unfortunately it isn’t viable to release these changes to the public. This just proves that it is possible though!

If you posted a comment on the previous post, I’ve responded to all comments now.

VCL Ribbon MDI Fix

Disclaimer: I wrote the VCL Ribbon implementation in Delphi 2009. At the time there was no scope for MDI support in the Ribbon however the number of posts about this issue made me look into fixing it. Today I set aside some time to look at a solution and I now present this solution.

The MDI Ribbon bug is evident when your application is a MDI application and the MDI Children are maximized. The screen capture below shows this bug.

VCL Ribbon MDI Bug

VCL Ribbon MDI Bug

The fix is to drop a new component onto the Form with the Ribbon on it. Then you just need to set the Ribbon property to be the TRibbon component on the form, that is all that needs to be done.

The screen shot below shows the component at design time.

VCL Ribbon MDI Bug Fix

VCL Ribbon MDI Bug Fix

When you toggle the Enable MDI Fix check box, you need to restore and maximum a MDI Child form. For real use, you wouldn’t want to disable it anyway.

Various screen captures of the demo application with the fix active.

Fixed Ribbon MDI Bug

Fixed Ribbon MDI Bug

Fixed Ribbon MDI Bug

Fixed Ribbon MDI Bug

Fixed Ribbon MDI Bug

Fixed Ribbon MDI Bug

 

Downloading the Fix

You can download the component and demo application from my site. I may also put the fix on CodeCentral in future.

Ribbon VCL MDI Bug Fix Download

To install the component into the IDE, open the RibbonMDIFix2010.dpk file in Delphi 2010 (should also work, but is untested). In the project manager, select the Install command. Make sure the path to the RibbonMDIFix.pas unit is on your library path so that the compiler can find the file when compiling your application.

 

Further Ribbon Details

If you search for my QualityCentral entries on the Ribbon, most of the issues I raise I also provide the workaround for. Most are simple changes that can be done by copying the Ribbon units into your project folder. IIRC none are interface breaking (I’ve saved those).

 

Performance issue with MDI Applications using the TActionManager framework

There is also an issue with the MDI “detection” when a TActionMainMenuBar component (and descendants) are used in an application. This is actually a bug that was raised with Borland/CodeGear support several years ago. When you have an action that calls GetActiveMDIChild you may see processor usage increase significantly. On the Window group in the application feature throughout this post you’ll see the Close button. This is the TWindowClose standard MDI action. This actions update handler calls ActiveMDIChild. If you are running an MDI application that utilises TActionManager CPU usage will increase because of this. This occurs because of the Window Hook installed for menu processing. It handles the WM_MDIGETACTIVE message, so if an action is calling that method, the window hook is also processing it.

My solution (which also uses a window hook) avoids increasing CPU usage unnecessarily by listening to more specific MDI messages. There is no need to listen to the WM_MDIGETACTIVE (that I can see).

Themed Grids in Delphi 2010

I was reminded of a new feature in Delphi 2010 when someone recently posted a new comment on a very old blog post. It was the post about my CodeCentral entry that provided a themed TDBGrid component.

In Delphi 2010 the TStringGrid, TDrawGrid and TDBGrid components now have a DrawingStyle property. The default value for this property is gdsThemed which means all grids in your Delphi 2010 applications will be themed by default. If you application isn’t themed, then your grids will use the gdsClassic style, which is how they appeared before Delphi 2010.

You actually get three different drawing style options. Themed, Classic and Gradient.

  • The Classic style is just the previous drawing style.
  • The Themed style uses the current theme information (if enabled) .
  • The Gradient style uses two new properties called GradientStartColor and GradientEndColor to paint the fixed row cells.

The following screen captures show each of the new styles.

Grids Classic Drawing Style

Grids Classic Drawing Style

Grids Themed Drawing Style

Grids Themed Drawing Style

Grids Gradient Drawing Style

Grids Gradient Drawing Style

Indicator Painting Issue

If you strain your eyes for long enough, you might see a painting issue with the Themed and Gradient drawing style images above. To be fair, I didn’t notice it until I wrote the sample application to demonstrate the feature. I had a look to see if the issue has been raised in QualityCentral yet and it hasn’t.

The issue is a lot more obvious when you change the gradient colors a little. As shown in the screen shot below.

Grids Gradient Drawing Style Issue

The indicator transparency issue

The indicator isn’t drawn as transparent. This is a simple fix and only occurs for the TDBGrid component since the indicator isn’t valid for the other grids. Internally the indicator image is stored in a TImageList. When the image from the image list is drawn it isn’t drawn with transparency.

The Fix

  1. Copy the DBGrids.pas file to your applications folder. It is located in the <InstallDir>\Source\Win32\DB folder.
  2. Search for FIndicators.Draw (There is only one occurrence in the entire unit located in the DrawCell method of TCustomDBGrid.)
  3. Change the line under the FIndicators.Draw line to the following:
(ARect.Top + ARect.Bottom - FIndicators.Height) shr 1, Indicator, dsTransparent, itImage, True);

The red bolded parameters are the additional parameters added to the Draw method call (which starts on the preceeding line).

The following screen shot was created with the above fix applied to the DBGrids unit.

Grids Gradient Drawing Style Issue Fixed

The indicator transparency issue fixed

It’s a shame the indicator is not actually exposed for easy customisation. Even more of a shame is the inability to customise the move column painting.

Source Code

The source code and a compiled executable is available for download.

Source Code Download

D2006: What on earth are these Explicit* properties in my DFM

If you need to make sure your application can still be compiled in previous Delphi versions you need to watch out for some new properties that are added by Delphi 2006 to your DFM. These new properties are for all TControl descendants and are called ExplicitTop, ExplicitLeft, ExplicitHeight and ExplicitWidth. These are not actually published properties; you won’t find them in the object inspector.

The Explicit* properties remember the previous bounds of a control before the Align or Anchor properties are changed from their defaults. The only time the Explicit* properties are not written is when the Align property is set back to its default value of alNone. This is when the Explicit* properties are actually used by the control to reset its bounds to what it was previously.

You can download the example here to play along at home if you like (executable included):
http://www.jed-software.com/files/ExplicitProps.zip

This example takes a TPanel and changes its align property value from

alNone -> alClient -> alNone

Example Usage:

  1. Run the application
  2. Click on the Get Values button
  3. Values from Figure 1 are displayed in the Memo control
  4. Click on the Align Client button
  5. Click on the Get Values button again and Figure 2 displays the added Memo control contents
  6. Click on the Align None button
  7. Click on the Get Values button a final time and the Memo control should have the same values added to it that were added in Step 3.

So what has changed?
In previous Delphi versions if the align property was changed to alClient and then back to alNone, the control would stay as if it was aligned client still. These new properties mean that the original bounds is restored when this occurs. As noted with the example.

When the panel was aligned client it up the left over client area of the form; as expected. When clicking on the Align None button the panel was restored back to its size prior to setting Align to alClient.

If you want to know the specific conditions for having these properties written to the DFM check out the DoWriteExplicit function in the DefineProperties method for TControl.

Figure 1

Align is alNone

ExplicitTop: 40

ExplicitLeft: 256

ExplicitHeight: 185

ExplicitWidth: 329

Top: 40

Left: 256

Height: 185

Width: 329

----------------------
Figure 2

Align is alClient

ExplicitTop: 40

ExplicitLeft: 256

ExplicitHeight: 185

ExplicitWidth: 329

Top: 0

Left: 193

Height: 456

Width: 450

----------------------