TAChart: chart is sometimes fully or partially unpainted
Original Reporter info from Mantis: Marcin Wiazowski
-
Reporter name:
Original Reporter info from Mantis: Marcin Wiazowski
- Reporter name:
Description:
Let's make some experiments first.
Test 1:
- load Reproduce1 application in IDE - it will be shown, as it can be seen on the upper part of the attached Reproduce1.png
- in Object Inspector, select Chart1ExpressionSeries (red sine wave)
- in Object Inspector, go to Domain property and write there just "x" (without apostrophes) and press enter
- error message will be shown: "Cannot evaluate: empty expression" - select OK to close the message
- now the whole chart will disappear from the designed form, as can be seen on the middle part of the Reproduce1.png; alternatively, chart may be only partially visible (i.e. has not been fully erased)
- minimize and then restore the designed form - the chart will be drawn properly again, although without the red sine wave; chart's extent will be adjusted accordingly (see bottom of the Reproduce1.png)
Test 2:
- launch the attached Reproduce2 application WITHOUT THE DEBUGGER
- press the "Test!" button - most probably, application will be terminated immediately
Explanation 1: "x" in the Domain property is not a valid domain string, so series' extent cannot be calculated. Chart, during its drawing process, asks all the active series about their extents, which is performed in TChart.GetFullExtent() method - we can see the following piece of code there:
for s in Series do begin
try
JoinBounds(s.GetGraphBounds);
except
s.Active := false;
raise;
end;
Because Chart1ExpressionSeries can't calculate its extent, it raises an exception in the GetGraphBounds() call. The exception is caught, series' Active property is set to False, and then the exception is reraised. Due to setting Active to False, Chart1ExpressionSeries will never be drawn again, which makes sense - there must be some serious failure in the series, if it cannot even return its extent.
But the problem is that reading the series' extents - i.e. making GetGraphBounds() calls - is performed at the very beginning of the chart drawing process; exception raised in this phase leaves the chart in fact fully unpainted - and this is why we can see just nothing at the designed form, when the error message is closed. After next chart drawing (i.e. after minimizing and then restoring the designed form), Chart1ExpressionSeries is omitted due to its Active = False setting, so no exception is raised anymore, and the chart is drawn properly at last - of course, Chart1ExpressionSeries is no more shown.
Explanation 2: this time, series' Expression property is set to empty string (well, this is a bit artificial example, but useful to reproduce the problem), which is performed on both existing Chart1ExpressionSeries1 and Chart1ExpressionSeries2 (green and red line on Reproduce2.png). Although I haven't debugged this in detail, it seems that Chart1ExpressionSeries1 is being drawn first, which causes an exception. When showing the exception message's window, chart gets drawn again - and, this time, Chart1ExpressionSeries2 raises a next exception, although the previous one is still not handled - which is assumed to be a fatal error, so the application is terminated immediately.
Similar situation may occur not only at the very beginning of the chart drawing process, but also at later phases, as can be seen in TChart.TChart.DisplaySeries():
procedure DrawOrDeactivate(
ASeries: TBasicChartSeries; ATransparency: TChartTransparency);
begin
try
...
except
ASeries.Active := false;
raise;
end;
end;
and in TCustomColorMapSeries.GetZRange():
procedure TCustomColorMapSeries.GetZRange(ARect: TRect; dx, dy: Integer);
begin
...
try
...
except
FActive := false;
raise;
end;
end;
If we could repaint the chart immediately after catching the exception, we could avoid the described problems.
The possible solution is quite simple: when some series becomes inactive during the chart drawing process, this can be easily detected (just by counting active series). In this case we should go back to the beginning of the chart drawing process. We should try again as long, as some chart series is made inactive; since there is a limited number of existing series, we are not going to be trapped in an infinite loop. Finally, after the chart has been successfully drawn, we can raise an exception to report the already caught exceptions.
The attached patch solves the described problems.
Mantis conversion info:
- Mantis ID: 35317
- Build: 60823
- Version: 2.1 (SVN)