mark regions in (Gantt) Chart - delphi

[TeeChart 2015.14.150120]
I've a Gantt-Chart so the x axis is date based. I would need to mark time periods in the chart. So i.e. mark 2009 -2011 describing it with a certain description, 2011-2013 with another name ...
I've tried to use TColorBandTool but it had some drawbacks:
I wasn't able anymore to click the Chart-Entries (even if the tool was marked as "behind"
I could not show a description of the period.
So I've tried to use TColorLineTool which worked better (almost perfect) but:
the text panels for differnt period where shown at the same (vertical) position, so they overlapped some times.
When the last TextPanel was longer then the remaining part of the chart, it "dropped" out, the Chart was not redimensioned in order to show the Panel within the Diagram.
So, now I had another Idea: to use different series to build one line over the whole width of the chart, each series for one period to show. But I would have to show the description of these series in extra Legends (TExtraLegendTool) in oder to have space enough for the texts. But I couldn't get the TExtraLegendTool shown. I assume there is a bug in that version of TeeChart since also the demo put by the installer doesn't show that tool.
Now I'm rather at a loss how to go on. Anybody has an idea?

I've given a try at the first approach:
I've tried to use TColorBandTool but it had some drawbacks:
I wasn't able anymore to click the Chart-Entries (even if the tool was marked as "behind"
I could not show a description of the period.
And I think it works fine for me here. This is how it looks and below the code I used.
Note I disabled the TColorBandTool's AllowDrag, ResizeStart and ResizeEnd properties.
var gantt1: TGanttSeries;
greenBand, blueBand: TColorBandTool;
greenAnnot, blueAnnot: TAnnotationTool;
procedure TForm1.FormCreate(Sender: TObject);
begin
Chart1.Title.Visible:=false;
Chart1.View3D:=false;
Chart1.Legend.Alignment:=laBottom;
gantt1:=Chart1.AddSeries(TGanttSeries) as TGanttSeries;
gantt1.FillSampleValues(10);
greenBand:= Chart1.Tools.Add(TColorBandTool) as TColorBandTool;
with greenBand do
begin
Color:=clGreen;
Transparency:=50;
Axis:=Chart1.Axes.Bottom;
StartValue:=gantt1.StartValues[1];
EndValue:=gantt1.EndValues[2];
AllowDrag:=false;
ResizeStart:=false;
ResizeEnd:=false;
end;
blueBand:= Chart1.Tools.Add(TColorBandTool) as TColorBandTool;
with blueBand do
begin
Color:=clBlue;
Transparency:=50;
Axis:=Chart1.Axes.Bottom;
StartValue:=gantt1.StartValues[9];
EndValue:=gantt1.EndValues[8];
AllowDrag:=false;
ResizeStart:=false;
ResizeEnd:=false;
end;
Chart1.MarginTop:=10;
Chart1.Draw;
greenAnnot:=Chart1.Tools.Add(TAnnotationTool) as TAnnotationTool;
with greenAnnot do
begin
Shape.Transparent:=true;
Shape.Font.Color:=clGreen;
Text:='Green annotation';
Top:=Chart1.ChartRect.Top-15;
Left:=Chart1.Axes.Bottom.CalcPosValue(greenBand.StartValue+(greenBand.EndValue-greenBand.StartValue)/2) -
(Chart1.Canvas.TextWidth(Text) div 2);
end;
blueAnnot:=Chart1.Tools.Add(TAnnotationTool) as TAnnotationTool;
with blueAnnot do
begin
Shape.Transparent:=true;
Shape.Font.Color:=clBlue;
Text:='Blue annotation';
Top:=Chart1.ChartRect.Top-15;
Left:=Chart1.Axes.Bottom.CalcPosValue(blueBand.StartValue+(blueBand.EndValue-blueBand.StartValue)/2) -
(Chart1.Canvas.TextWidth(Text) div 2);
end;
end;

Related

TeeChart: How to bring a series to the front

IDE: c++ Builder XE5 Update 2
TeeChart Build: 2014.11.140512
I am trying to bring certain series on a TChart component to the front(as you would typically do with a BringToFront() function).
I've done some reading and found the following options/suggestions:
A. Change the ZOrder property of the series.
B. Use TChart.ExchangeSeries()
Using TChart.ExchangeSeries() is not a proper way of changing the z-order of a series. Its primary function is to swap around two series in a TChart component's SeriesList(which then inherently changes the z-order of those series). If you require your series-ordering to be fixed(fixed ordering in SeriesList), then this will not work.
Changing the ZOrder properties of the series delivered better results. However, changing the ZOrder of the first series(Series[0]) apparently does nothing. Series[0] seem to like sitting at the back of the class.
The above might be the result of my implementation. In which case, some more details:
On my TChart component I have multiple series. The series-types can be changed dynamically. The types to which the series can be changed are limited to TLineSeries and TBarSeries.
I always want the TLineSeries at the front.
Any advice on how this can be done?
(Will we ever see that elusive TChartSeries.BringToFront() function?) :)
Any advice on how this can be done?
Looks like mixing series styles and changing ZOrder is not working very well. For example, using this code snippet:
uses Series;
procedure TForm1.FormCreate(Sender: TObject);
begin
Chart1.AddSeries(TLineSeries.Create(Self)).FillSampleValues(10);
Chart1.AddSeries(TLineSeries.Create(Self)).FillSampleValues(10);
Chart1.AddSeries(TBarSeries.Create(Self)).FillSampleValues(10);
Chart1.AddSeries(TBarSeries.Create(Self)).FillSampleValues(10);
Chart1.AddSeries(TBarSeries.Create(Self)).FillSampleValues(10);
end;
procedure TForm1.Button1Click(Sender: TObject);
var i: Integer;
begin
for i:=0 to Chart1.SeriesCount-1 do
begin
Chart1[i].Marks.Visible:=False;
if Chart1[i] is TLineSeries then
Chart1[i].ZOrder:=Chart1.SeriesCount - 1 - i
else
Chart1[i].ZOrder:=i;
end;
end;
Line series are brought to the front but bar series style remain there. I have also tried with bar series StackGroups: However, it doesn't make much of a difference. We'd need StackGroups for line series as well for this work.
(Will we ever see that elusive TChartSeries.BringToFront() function?)
:)
Why not? I have added your request to Steema Software's bugzilla platform: http://bugs.teechart.net/show_bug.cgi?id=853. Feel free to sign up and add yourself to the CC List to receive automatic issue updates.

TScrollPagerTool issue

I am trying the TeeChart software before I buy and was directed from their website to here for support.
I am trying to get my head around using the TScrollPagerTool as it seems perfect for what I am trying to achieve but I am experiencing performance issues when resizing.
In the resize event for the chart it is advised to add the following line if you are using this scrolling tool:
ScrollPagerTool.Series := Chart1.Series[0];
I don't know why you have to reassign the series but the chart won't resize without it. Further more that line effectively adds the Series again, meaning I have twice the series now and next resize, 3 times the series and so on, therefore I get a performance problem with scrolling after a few resizes.
Can someone tell me if I am doing this right or of an alternate method here?
I can post more code if required.
First note that TScrollPagerTool uses a TSubChartTool with a clone of the main series in it. That's why the the SubChart is set when the Series is assigned, because it has no sense without a series.
In some TeeChart versions, the SubChart needs the main chart to be already drawn when you assign a series to the tool in order to calculate the space each chart should use (the ChartRects). That's why we assign the series at the OnResize event, but I see in TeeChart VCL you can do it all at creation time:
uses Series, TeeScrollPagerTool;
procedure TForm1.FormCreate(Sender: TObject);
begin
Chart1.AddSeries(TLineSeries).FillSampleValues;
(Chart1.Tools.Add(TScrollPagerTool) as TScrollPagerTool).Series:=Chart1[0];
end;
BUG:
I've seen there was actually a bug, as you said. Assigning the series in the OnResize event to make the chart and the subchart resize with the form, it made the source series to be cloned again and again:
uses Series, TeeScrollPagerTool;
var scrollPager1: TScrollPagerTool;
procedure TForm1.FormCreate(Sender: TObject);
begin
Chart1.Align:=alClient;
scrollPager1:=Chart1.Tools.Add(TScrollPagerTool) as TScrollPagerTool;
Chart1.AddSeries(TLineSeries).FillSampleValues;
scrollPager1.Series:=Chart1[0];
end;
procedure TForm1.Chart1Resize(Sender: TObject);
begin
scrollPager1.Series:=Chart1[0];
end;
I've made some changes to fix it:
In the next version, the Series property will remove all the series in the subchart and will clone the series assigned. So calling it several times won't end up in more and more clones. It will also call SetUpScrollPager that calculates the ChartRect for both the main chart and the subchart.
I've also made the SetUpScrollPager public so this will be the one to call at OnResize event to adjust the ChartRects without needing to add or remove series.
procedure TForm1.Chart1Resize(Sender: TObject);
begin
scrollPager1.SetUpScrollPager;
end;
WORKAROUND:
In the meanwhile, with the actual version you still could remove all the series in the SubChart and use the Series property to clone the source series, all at the OnResize event:
procedure TForm1.Chart1Resize(Sender: TObject);
begin
scrollPager1.SubChartTChart.RemoveAllSeries;
scrollPager1.Series:=Chart1[0];
end;
RELATED FEATURE:
How to have multiple series in the ScollPager? You can use the SubChartTChart property to access the subchart and add/remove any series to/from it. But you still need one series in the Series property. So it gets a bit tricky:
uses Series, TeeScrollPagerTool;
var scrollPager1: TScrollPagerTool;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.Align:=alClient;
scrollPager1:=Chart1.Tools.Add(TScrollPagerTool) as TScrollPagerTool;
for i:=0 to 4 do
begin
Chart1.AddSeries(TLineSeries).FillSampleValues;
if scrollPager1.Series=nil then
scrollPager1.Series:=Chart1[i]
else
scrollPager1.SubChartTChart.AddSeries(CloneChartSeries(Chart1[i]));
end;
end;
That's why I've also added a new AddSeries(const Value: TChartSeries) method. Use it to add several series to the ScrollPager:
for i:=0 to 4 do
begin
Chart1.AddSeries(TLineSeries).FillSampleValues;
scrollPager1.AddSeries(Chart1[i]);
end;

Richedit style formatting changes on its own

(Someone edit the title if you can understand and define my problem better.)
The problem which I am having is with style formatting of a RichEdit "reverting" back to the default "nothing" aka [] and then back to whatever I set it to, bold or italic for example.
The thing that is at fault - I assume, since I have no idea how it is breaking things - is a procedure (REMainLinesCheck) that checks for amount of lines in the RichEdit and deletes the first one until a certain point is reached (to show a maximum of 14 lines at once) like so:
while REMain.Lines.Count > 14 do
REMain.Lines.Delete(0);
I have 6 occurrences of the above procedure in other procedures that add lines to the RichEdit, but none of them change RichEdit.SelAttributes.Style but one, which was adding only one Bold line like so:
REMain.SelAttributes.Style := [fsBold];
REMain.Lines.Add('something');
REMainLinesCheck;
So I have removed all occurrences except that one and started poking around, it didn't take long to see that it was working in fact fine, regular and bold lines where being added normally and excess lines where being deleted - no problems. But as soon as I reintroduced REMainLinesCheck procedure into another procedure (for clarity purposes, lets call it Proc3Lines, because that's what it does: adds 3 lines and then calls the check for excess lines), every line that follows this Proc3Lines that should be Bold is not... From what I have experienced here it seems that REMainLinesCheck does something in Proc3Lines, since without it everything is fine.
Obviously it's not a circle of procedures that call each other, but the other parts of the code have nothing to do with this RichEdit, not to mention that I don't change RichEdit.SelAttributes.Style anywhere for REMain except that one place that I have shown, there is another RichEdit in the same unit that I do change its line's style like that, but that cannot possibly be related in any way... could it? (No it does not, I just checked.)
Basically: what the hell Delphi? It cannot get any simpler than this and I am still managing to fail, can someone explain and/or fix this? Ask questions, I'll elaborate as much as I can if something is not clear.
To apply a format to a new added line, use the following:
procedure TForm1.Button1Click(Sender: TObject);
var
LineIndex: Integer;
begin
LineIndex := RichEdit1.Lines.Add('Something');
RichEdit1.SelStart := RichEdit1.Perform(EM_LINEINDEX, LineIndex, 0);
RichEdit1.SelLength := RichEdit1.Perform(EM_LINELENGTH, RichEdit1.SelStart, 0);
RichEdit1.SelAttributes.Style := [fsBold];
end;
This has worked for me:
procedure TformStart.Proc;
var
endtxtpos: integer;
begin
endtxtpos := Length(REMain.Text);
REMain.Lines.Add('something');
REMain.SelStart := endtxtpos-(REMain.Lines.Count-1);
REMain.SelLength := Length('something');
REMain.SelAttributes.Style := [fsBold];
end;
But since I don't know any better, please criticize and suggest how I can do it better.

How to stop the sort symbol from hiding when TVirtualTreeView (TVirtualStringTree) header is double clicked

I am using a VirtualStringTree control as a list view and using the sort features. However when I double click the VirtualStringTree header the sort direction symbol hides until I click the header again.
Can that behaviour be disabled?
Things that I have tried but do not work:
I have searched the properties and cannot find a related setting
I have linked the double click header event to the click header event
My environment is Delphi 2007 Pro, Windows 7 Pro 64bit.
I had the same issue with double-click and hiding of sorting triangle and instead I just wanted a simple toggle up/down with nothing else. This issue is present unfortunately in latest VirtualTreeView (4.8.7) as well.
Here is a bit of code that fixes the issue - put something like this in your OnHeaderClick event (not OnHeaderDblClick !).
The relevant line is if HitInfo.Column = NoColumn then Exit; which fixes the double-click problem. You may or may not use the rest of code for your own purposes but it may be useful to someone else. The rest of explanation is in the code comments.
You don't need to define OnHeaderDblClick event - it may be empty if not needed so you may want to remove that from your code.
UPDATE
Also read comments from TLama as it seems that with version 5.0.0. this fix may not operate as intended. With the current version it does though.
{**
A column header of a VirtualStringTree was clicked: Toggle the sort direction
}
procedure TMainForm.vstHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
begin
// Don't call sorting procedure on right click
// Some list-headers have a contextmenu which should popup then.
if HitInfo.Button = mbRight then Exit;
// Beginning with VT's r181, this proc is also called when doubleclicking-to-autofit
// Seems buggy in VT as this suddenly calls it with Column=-1 in those cases.
// See also issue #1150
if HitInfo.Column = NoColumn then Exit;
if Sender.SortColumn <> HitInfo.Column then Sender.SortColumn := HitInfo.Column
else if Sender.SortDirection = sdAscending then Sender.SortDirection := sdDescending
else Sender.SortDirection := sdAscending;
Sender.Treeview.SortTree( HitInfo.Column, Sender.SortDirection );
end;

How do I copy a form as an image to the clipboard

I need to copy a form (Delphi 2007) to the clipboard as an image to paste what the user can see into a word document. The clipboard part is not really a problem. The questions is how to get a bitmap for the form.
Searching has turned up multiple options.
Call GetFormImage
Use the PrintWindow API function that is part of the GDI+
Send a WM_PRINT message
Copy the Canvas for the current form using Canvas.CopyRect
I also found a component called TExcellentFormPrinter that has a solution that claims to works better than any of these options, but I don't know what method it is using.
All of these options seem to have different problems. Most of the information I am finding seems to be outdated. I can't seem any good source that compares the different options with enough detail for me to make a choice. Any advice on which option to go with.
I have tried these on my form and they all seem to work OK, I just trying to avoid problems down the road. Any advice on what solution to go with?
Update: What Potential Problems with GetFormImage?
Andreas asked what the problem is with GetFormImage. Hopefully nothing anymore, that is part of what I am trying to get an answer to. What has me concerned is so many of my search results seem to be suggesting creative alternatives to using GetFormImage. I was hoping the answers would clear up the waters a little bit.
I would be really happy with an answer that got a lot of up votes that said - GetFormImage used to have some problems but there is no reason not to use it now. :-)
As to the actual problem with GetFormImage. One issue for some users was only the visible part of the form would appear in the image (i.e. you can't capture a hidden or overlapped window). That is not really an issue for me as my entire form is visible.
1) The bigger issues deal with specific support required from the controls on your form. The Delphi 4 Fixes and Known issues page list has this entry (note it is listed as "Deferred to Next"). I could not find a QC entry that showed this resolved:
Area: vcl\core vcl classes
Reference Number: 1088 (Published: 12/16/98)
Status: Deferred to Next
Rel Date Reported: 8/6/98 Severity:
Commonly Encountered Type: Basic
Functionality Failure Problem:
The problem is with GetFormImage most nest windows controls like comboboxes, etc. are drawn blank.
2) I am also using the DevExpress controls. At one time their controls (fixed at the end of 2006) did not support the PaintTo messages that GetFormImage was using. This is fixed in the DevExpress release I am using, but it raises other issues with me, what is the chance that other control I am using may not work correctly?
3) Here is a more recent (2010) post on the Embarcadero Groups. The user was having trouble using GetFormImage where part of the graph they were showing on screen did not appear in the final image. They also needed the form caption included (which I do not) and they took the Canvas.CopyRect approach outlined in this post.
4) Here is the quote from the TExcellentImagePrinter page. I would have no problem buying their product if needed. There component looks like it was last updated in 2002 (There is a Delphi 2007 trial version though). I can't tell if I really need to go that direction or not.
You can try using GetFormImage or
Form.Print. Try dropping a ComboBox
down on a form, then call GetFormImage
or Form.Print. If you get a
printout, do you see the text in the
ComboBox? No? Neither does anyone
else! This is only a small example of
the problems you will encounter when
printing VCL forms.
You can also try using Borland's
TI-3155 "A better way to print a
form". I wrote the TI when I worked at
Borland as a stop gap measure. While
it will print the combobox text, it
will fail on many printers, it can't
print the entire form if your user has
resized the form, and it can't print
forms that are hidden from view or is
located partially off the screen. The
code basically produces a screenshot,
and to print an image reliably, you
would probably want to take a look at
our TExcellentImagePrinter product!
Why? Simply put, it can require a
couple of thousand lines of low level
graphics code to get bitmaps to print
well under Windows.
I do not know what the problem is with GetFormImage, but an option that you have not tried (at least not explicitly) is
procedure TForm1.FormClick(Sender: TObject);
var
bm: TBitmap;
begin
bm := TBitmap.Create;
try
bm.SetSize(ClientWidth, ClientHeight);
BitBlt(bm.Canvas.Handle, 0, 0, ClientWidth, ClientHeight, Canvas.Handle, 0, 0, SRCCOPY);
Clipboard.Assign(bm);
finally
bm.Free;
end;
end;
In almost all cases I would expect this to produce the same result as
bm := GetFormImage;
try
Clipboard.Assign(bm);
finally
bm.Free;
end;
though. (Also, the Canvas.CopyRect procedure employes StretchBlt which I would expect to produce the same result as BitBlt when no stretching is applied.)
Method 2
You can always use Print Screen:
procedure TForm1.FormClick(Sender: TObject);
begin
keybd_event(VK_SNAPSHOT, 1, 0, 0);
end;
This will also capture the border and the title bar. If you only wish to obtain the client area, you can crop the image:
procedure TForm1.FormClick(Sender: TObject);
var
bm, bm2: TBitmap;
DX, DY: integer;
begin
Clipboard.Clear;
keybd_event(VK_SNAPSHOT, 1, 0, 0);
repeat
Application.ProcessMessages;
until Clipboard.HasFormat(CF_BITMAP);
bm := TBitmap.Create;
try
bm.Assign(Clipboard);
bm2 := TBitmap.Create;
try
bm2.SetSize(ClientWidth, ClientHeight);
DX := (Width - ClientWidth) div 2;
DY := GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME );
BitBlt(bm2.Canvas.Handle, 0, 0, ClientWidth, ClientHeight, bm.Canvas.Handle, DX, DY, SRCCOPY);
Clipboard.Assign(bm2);
finally
bm2.Free;
end;
finally
bm.Free;
end;
end;

Resources