Blog
All Blog Posts | Next Post | Previous Post
Easy extensibility with poly list controls
Sunday, June 20, 2010
The TMS Advanced Poly List controls were designed from the ground up with extensibility in mind. Both at design time and at run time, custom item class instances can be added to the list. The developers guide explains how this can be done with a simple sample showing how to create an item class that shows a triangle. While this shows the techniques needed, displaying a triangle is of limited usefulness. For an application, we needed an item with a progress bar. We could reuse the smooth controls progress bar drawing with the added bonus that it looks nicer than an average progress bar. All in all, in about 30 minutes, a new item class that shows a progress bar is ready to be used.You can download the sample with the full source code of the new progress bar item class here so you can easily add this progress bar in any of the TMS Poly List controls you're using.
Now, for those interested to learn how this was created, here is a short description:
1) Create class descending from TCustomItem
TCustomItem is defined in the unit GDIPCustomItem. Create a new unit with a new class descending from TCustomItem
type TProgressItem = class(TCustomItem) end;
function CreateNewItem(AOwner: TComponent): TCustomItem;
This function creates a new instance of the item
function GetClassType: TComponentClass;
This function returns the type of the class
function CustomClassName: String;
This function returns a friendly class description
3) Add properties relevant for the progress bar
This consists of the progress bar minimum, maximum and position properties. A property to control whether the value is displayed and the format to use for displaying the value and most importantly, the Appearance property that holds all color / gradient settings for the progressbar:
type
TProgressItem = class(TCustomItem)
published
property Appearance: TGDIPProgress;
property Min: integer;
property Max: integer;
property Margin: integer;
property Position: integer;
property ShowValue: boolean;
property ValueFormat: string;
end;
4) Implement the custom drawing
The custom drawing is implemented by overriding the virtual method DrawInRect()
The DrawInRect method parameters return the graphics device context, the default item appearance and the rectangle of the item. The default item appearance holds settings such as normal state background fill, selected state background fill, hovered state background fill. In the case of this TProgressItem, the TMS smooth controls TGDIPProgress class is used. This class has a method that draws the progress bar on the graphics device context.
5) Register the class
In the Register procedure, the function RegisterPolyItem(TProgressItem) is called and when installed via a package in the IDE, this makes the progressbar available in the designer.
Using the new TProgressItem class
Using the new class is equally simple. The code snippet here shows how a new instance of the TProgressItem is inserted in the poly list control and its properties being initialized:
var pi: TProgressItem; begin pi := TProgressItem(AdvVerticalPolyList2.InsertItem(1, TProgressItem)); pi.Min := 0; pi.Max := 100; pi.Position := 75; pi.ValueFormat := 'Download progress: %d%%'; pi.Margin := 4; end;
The full source code of the TProgressItem class is here:
unit GDIPProgressItem;
interface
uses
Classes, GDIPCustomItem, AdvGDIP, GDIPFill;
type
TProgressItem = class(TCustomItem)
private
FAppearance: TGDIPProgress;
FMax: integer;
FMin: integer;
FPosition: integer;
FValueFormat: string;
FShowValue: boolean;
FMargin: integer;
procedure SetAppearance(const Value: TGDIPProgress);
procedure SetMax(const Value: integer);
procedure SetMin(const Value: integer);
procedure SetPosition(const Value: integer);
procedure SetShowValue(const Value: boolean);
procedure SetValueFormat(const Value: string);
procedure SetMargin(const Value: integer);
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function CreateNewItem(AOwner: TComponent): TCustomItem; override;
function GetClassType: TComponentClass; override;
class function CustomClassName: String; override;
procedure DrawInRect(g: TGPGraphics; ItemAppearance: TItemAppearance; R: TGPRectF); override;
published
property Appearance: TGDIPProgress read FAppearance write SetAppearance;
property Min: integer read FMin write SetMin default 0;
property Max: integer read FMax write SetMax default 100;
property Margin: integer read FMargin write SetMargin default 2;
property Position: integer read FPosition write SetPosition default 0;
property ShowValue: boolean read FShowValue write SetShowValue default true;
property ValueFormat: string read FValueFormat write SetValueFormat;
end;
procedure Register;
implementation
uses
SysUtils, Graphics, Windows;
{ TProgressItem }
procedure TProgressItem.Assign(Source: TPersistent);
begin
inherited;
FAppearance.Assign((Source as TProgressItem).Appearance);
end;
constructor TProgressItem.Create(AOwner: TComponent);
begin
inherited;
FShowValue := true;
FValueFormat := '%d%%';
FMargin := 2;
Height := 40;
FAppearance := TGDIPProgress.Create;
with FAppearance do
begin
BackGroundFill.Color := $00FFD2AF;
BackGroundFill.ColorTo := $00FFD2AF;
BackGroundFill.BorderColor := $00C0C0C0;
ProgressFill.Color := $00EBFDFF;
ProgressFill.ColorTo := $00ABEBFF;
ProgressFill.ColorMirror := $0069D6FF;
ProgressFill.ColorMirrorTo := $0096E4FF;
ProgressFill.BorderColor := clSilver;
ProgressFill.GradientMirrorType := gtVertical;
end;
end;
function TProgressItem.CreateNewItem(AOwner: TComponent): TCustomItem;
begin
Result := TProgressItem.Create(AOwner);
end;
class function TProgressItem.CustomClassName: String;
begin
Result := 'Progressbar item';
end;
destructor TProgressItem.Destroy;
begin
FAppearance.Free;
inherited;
end;
procedure TProgressItem.DrawInRect(g: TGPGraphics;
ItemAppearance: TItemAppearance; R: TGPRectF);
var
f: TGDIPFill;
dr,tr: TRect;
valstr: string;
h,tw,th: integer;
begin
if Visible then
begin
DoItemStartDraw(Self, g, Self, r);
DoInternalItemStartDraw(Self, g, Self, r);
f := GetFill(ItemAppearance);
if Assigned(f) then
f.Fill(g, r);
if ItemAppearance.Focus and (State = isSelected) then
DrawFocus(g, r, ItemAppearance);
dr := Rect(Trunc(r.X), Trunc(r.Y), Trunc(R.X + R.Width), Trunc(R.Y + R.Height));
InflateRect(dr, -Margin, - Margin);
h := dr.Bottom - dr.Top;
if ShowValue then
h := h div 2;
tr := Rect(dr.Left, dr.Top, dr.Right, dr.Top + h);
// draw progressbar
FAppearance.Draw(g, tr ,FMin,FMax,FPosition);
if ShowValue then
begin
tr := Rect(dr.Left, dr.Top + h, dr.Right, dr.Top + 2*h);
valstr := Format(FValueFormat, [FPosition]);
// measure text value size
th := g.DrawText(valstr, length(valstr), tr, GetFont(ItemAppearance), DT_CALCRECT);
tw := tr.Right - tr.Left;
// center text
tr := Rect(dr.Left + (dr.Right - dr.Left - tw) div 2, dr.Top + (dr.Bottom - dr.Top - th + h) div 2, dr.Right, dr.Bottom);
// draw text
g.DrawText(valstr, length(valstr), tr, GetFont(ItemAppearance), 0);
end;
DoItemEndDraw(Self, g, Self, r);
DoInternalItemEndDraw(Self, g, Self, r);
end;
end;
function TProgressItem.GetClassType: TComponentClass;
begin
Result := TProgressItem;
end;
procedure TProgressItem.SetAppearance(const Value: TGDIPProgress);
begin
FAppearance.Assign(Value);
end;
procedure TProgressItem.SetMargin(const Value: integer);
begin
if (FMargin <> Value) then
begin
FMargin := Value;
Changed;
end;
end;
procedure TProgressItem.SetMax(const Value: integer);
begin
if (FMax <> Value) then
begin
FMax := Value;
Changed;
end;
end;
procedure TProgressItem.SetMin(const Value: integer);
begin
if (FMin <> Value) then
begin
FMin := Value;
Changed;
end;
end;
procedure TProgressItem.SetPosition(const Value: integer);
begin
if (FPosition <> Value) then
begin
FPosition := Value;
Changed;
end;
end;
procedure TProgressItem.SetShowValue(const Value: boolean);
begin
if FShowValue <> Value then
begin
FShowValue := Value;
Changed;
end;
end;
procedure TProgressItem.SetValueFormat(const Value: string);
begin
if (FValueFormat <> Value) then
begin
FValueFormat := Value;
Changed;
end;
end;
procedure Register;
begin
RegisterPolyItem(TProgressItem);
end;
end.Bruno Fierens
This blog post has not received any comments yet.
All Blog Posts | Next Post | Previous Post