Q. How can I show the resource name in the allocation slot?
Q. Is it possible to show a discontinuous time range?
Q. How do I set the tag property for a resource or allocation?
Q. Have you considered a data-aware version of TResourceAllocationChart?
Q. Can I label the date axis with week numbers?
Q. Can I label the date axis with an irregularly-spaced date label such as the first day of each month?
Q. How do I add an allocation when the user right-clicks on the chart?
Q. Can the chart auto-scroll when the user drags an allocation past the edge?
Q. Can the user click and drag to create a new allocation?
Q. Can I add extra columns shown after the resource name?
If you can't find the answer to your question here, please email it to SadMan Software.
A. Each new version is emailed to all registered users when it is released. No action is required on your part.
A. Set the Options to include OwnerDrawAllocations, then insert the following code into the OnOwnerDrawAllocation event handler. Ensure that the RowHeight property is set to a large enough value for the font being used, or else no text will be shown.
procedure TMyForm.ResourceAllocationChart1OwnerDraw(
Sender: TObject;
Resource: TResource;
Allocation: TResourceAllocation;
Canvas: TCanvas;
Rct: TRect);
var
yb, yt, xs, xe, tw, th: integer;
begin
xs := Rct.Left;
xe := Rct.Right;
yt := Rct.Top;
yb := Rct.Bottom;
with Canvas do begin
// for simplicity ignore the Resource.Style
// and just draw blocks
if xs = xe then begin
MoveTo(xs, yt);
LineTo(xs, yb);
end else
Rectangle(xs, yt, xe, yb);
// don't show the description if we're
// drawing the shadow
if Pen.Color <> ResourceAllocationChart1.ShadowColor then begin
tw := TextWidth(Resource.Name);
th := TextHeight(Resource.Name);
// is there room for the text in the rectangle?
if (xe - xs > tw) and (yb - yt > th) then begin
Brush.Style := bsClear;
TextOut((xe + xs - tw) div 2, (yb + yt - th) div 2, Resource.Name);
end; // else leave the block unlabelled
end;
end;
end;
A. No. TResourceAllocationChart can only draw a continuous time range without any gaps.
A. Use something like this:
var
resource: TResource;
allocation: TResourceAllocation;
begin
// add a new resource and get a reference to it
resource := ResourceAllocationChart1.AddResource('Ballroom',
clRed);
resource.Tag := 1;
// add a new allocation and get a reference to it
allocation := resource.AddSlot(Now, Now + 1.0);
allocation.Tag := 2;
end;
A. Only vaguely. Our idea of a suitable database structure may not be the same as the component user's, and it's not difficult to create the resources and allocations from a database table.
A. Yes. This example labels the date and time axis with week numbers. For this example, set DateLabelBase to a Sunday, and DateLabelInterval to 7 days.
procedure TForm1.ResourceAllocationChart1GetDateTimeLabelText(
Sender: TObject;
DateTime: TDateTime;
var DateText, TimeText: string);
var
WeekNumber: integer;
begin
TimeText := '';
if DayOfWeek(DateTime) = 1 then begin // label Sundays
// ISOWeekNumber function is in JCLDateTime from
// http://sourceforge.net/projects/jcl/
WeekNumber := ISOWeekNumber(DateTime);
DateText := IntToStr(WeekNumber);
end else
DateText := '';
end;
A. Yes. Use an OnDrawDateLabel event handler like this: This handler is called repeatedly to return the date and time of each label in turn.
procedure TForm1.ResourceAllocationChart1DrawDateLabel(
Sender: TObject;
var DateTime: TDateTime);
var
y, m, d: word;
begin
DecodeDate(DateTime, y, m, d);
inc(m);
if m = 13 then begin
m := 1;
inc(y);
end;
DateTime := EncodeDate(y, m, 1);
end;
A. Use something like this.
procedure TForm1.ResourceAllocationChart1RightClick(Sender: TObject);
var
resource: TResource;
slot: TResourceAllocation;
pt: TPoint;
StartAt, EndAt: TDateTime;
begin
// convert the mouse position to local co-ordinates
pt := ResourceAllocationChart1.ScreenToClient(Mouse.CursorPos);
// find the nearest resource
resource := ResourceAllocationChart1.FindResourceFromY(pt.y);
if Assigned(resource) then begin
// find the date/time equivalent to where the user clicked
StartAt := ResourceAllocationChart1.XToDateTime(pt.x);
// make the allocation 2 hours long by default,
// centered on the click
StartAt := StartAt - EncodeTime(1,0,0,0);
EndAt := StartAt + EncodeTime(1,0,0,0);
// if this slot is available then add a new allocation
if resource.IsSlotFree(StartAt, EndAt) then
slot := resource.AddSlot(StartAt, EndAt)
end;
end;
A. Drop a timer on the form, and attach the following events:
procedure TForm1.TResourceAllocationChart1AllocationDragStart(Sender: TObject;
Resource: TResource;
Allocation: TResourceAllocation;
var AllowDrag: Boolean);
begin
Timer1.Interval := 100;
Timer1.Enabled := true;
end;
procedure TForm1.TResourceAllocationChart1AllocationDragEnd(Sender: TObject;
Resource: TResource;
Allocation: TResourceAllocation);
begin
Timer1.Enabled := false;
end;
procedure TForm1.TResourceAllocationChart1AllocationDragCancel(Sender: TObject;
Resource: TResource;
Allocation: TResourceAllocation);
begin
Timer1.Enabled := false;
end;
procedure TForm1.TResourceAllocationChart1AllocationSwitchResource(Sender: TObject;
FromResource, ToResource: TResource;
Allocation: TResourceAllocation);
begin
Timer1.Enabled := false;
end;
// this event handler only fires when an allocation is being dragged
procedure TForm1.Timer1Timer(Sender: TObject);
var
pt: TPoint;
begin
with TResourceAllocationChart1 do begin
pt := ScreenToClient(Mouse.CursorPos);
// scroll horizontally?
if pt.x < PlotRect.Left then
ScrollChart(-(EndAt - StartAt) / 25.0)
else if pt.x > PlotRect.Right then
ScrollChart((EndAt - StartAt) / 25.0);
// scroll vertically?
if pt.y > PlotRect.Bottom then
FirstDisplayedIndex := FirstDisplayedIndex + 1
else if pt.y < PlotRect.Top then
FirstDisplayedIndex := FirstDisplayedIndex - 1;
end;
end;
A. Try this, it looks really good. Note: RAC is an instance of TResourceAllocationChart. (Our thanks to Marc Hoffmann for this snippet.)
type
TUnleashedChart = class(TResourceAllocationChart);
procedure TMainForm.RACMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Resource: TResource;
NewAlloc: TResourceAllocation;
StartAt, EndAt, MinimumSpan: TDateTime;
Pt: TPoint;
begin
// don't create allocations if dragging the chart
if not (racAllowDragScroll in RAC.Options) then begin
Resource := RAC.FindResourceFromY(Y);
if Assigned(Resource) then begin
StartAt := RAC.XToDateTime(X);
// need at least three pixels to allow the
// sizing handle to be grabbed
EndAt := RAC.XToDateTime(X + 3);
MinimumSpan := EncodeTime(0, 15, 0, 0);
if EndAt < StartAt + MinimumSpan then
EndAt := StartAt + MinimumSpan;
if Resource.IsSlotFree(StartAt, EndAt) then begin
NewAlloc := Resource.AddSlot(StartAt, EndAt);
NewAlloc.Style := rsBlock;
Pt := RAC.ClientToScreen(
NewAlloc.DisplayRect.BottomRight);
Pt.X := Pt.X - 1;
Pt.Y := Pt.Y - (NewAlloc.DisplayRect.Bottom -
NewAlloc.DisplayRect.Top) div 2;
SetCursorPos(Pt.X, Pt.Y);
Pt := RAC.ScreenToClient(Pt);
// hook into the normal MouseDown handler
TUnleashedChart(RAC).MouseDown(Button, Shift,
Pt.X, Pt.Y);
end;
end;
end;
end;
A. Of course. This example adds two columns. Add integer fields to the form for FTextWidth, FCol1X, FLine1X, FCol2X and FLine2X.
procedure TForm1.RACAdjustPlotRect(Sender: TObject;
Canvas: TCanvas;
var PlotRect: TRect);
var
i, w, mw, sw: integer;
begin
mw := 0;
Canvas.Font.Assign(RAC.Font);
for i := 0 to RAC.Count - 1 do begin
w := Canvas.TextWidth(RAC.Resources[i].Name);
if w > mw then
mw := w;
end;
// allow room for our data and lines
sw := Canvas.TextWidth(' ');
FTextWidth := Canvas.TextWidth('12.34');
// must add to existing PlotRect.left - as when
// printing, PlotRect may already contain print area
PlotRect.Left := PlotRect.Left + mw + FTextWidth * 2 + sw * 6;
// calculate column coordinates
FCol2X := PlotRect.Left - FTextWidth - sw;
FLine2X := FCol2X - sw;
FCol1X := FLine2X - FTextWidth - sw;
FLine1X := FCol1X - sw;
end;
procedure TForm1.RACOwnerDrawResourceName(Sender: TObject;
Resource: TResource;
Canvas: TCanvas;
Rct: TRect);
var
y: integer;
str: string;
begin
with Canvas do begin
Font.Assign(RAC.Font);
// display resource names plus addition column data
y := (rct.Bottom + rct.Top - TextHeight('Hg')) div 2;
TextOut(rct.Left + 2, y, Resource.Name);
str := Format('%4.2f', [V1]); // first column
TextOut(FCol1X + FTextWidth - TextWidth(str), y, str);
str := Format('%4.2f', [V2]); // second column
TextOut(FCol2X + FTextWidth - TextWidth(str), y, str);
end;
end;
procedure TForm1.RACDrawProgress(Sender: TObject;
Canvas: TCanvas;
DrawRect: TRect;
Progress: TDrawProgress);
begin
if Progress = dpBackgroundDone then
// draw column background colours
with RAC, Canvas do begin
with Pen do begin
Color := clBlack;
Style := psSolid;
Width := 1;
end;
Brush.Color := $B8E4CE;
Brush.Style := bsSolid;
Rectangle(FLine1X, PlotRect.Top,
FLine2X+1, PlotRect.Bottom);
Brush.Color := $E6E2B7;
Rectangle(FLine2X, PlotRect.Top,
PlotRect.Left+1, PlotRect.Bottom);
end;
end;
