The Web Data Module by design does not allow us to Drop TControl descendants on it. This is normally not a problem, since an ISAPI/CGI application is a server side application. But in this case, since we want to generate a graph as an image and have the web browser display the graph as an image, we would have liked to be able to drop a TChart onto the web data module and use it. The solution is to create the chart on the fly at run time. Once we've created the chart, we can set the various properties we're interested in and have it generate the graph the way we'd like to see it.
Years Experience in Programming Languages
Motorcycles used for Years
Have a look at the source of this page to see how the parameters are being sent to the ISAPI application and relate it to the graph you see for each link.
procedure TWebModule1.WebModule1waDrawChartAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin { The Query String for this action should be of the Format : Title=xxxx&Bar1Title=Bar1Value&Bar2Title=Bar2Value.... If "Titles" has "spaces" then they should be replaced with "_" (underscore) this pertains to all titles } Response.ContentType := 'image/jpeg'; Response.ContentStream := GenerateChart; Response.SendResponse; end;
function TWebModule1.GenerateChart : TMemoryStream; var ChartTitle : string; NoOfBars : Integer; BarTitles : array of string; BarValues : array of Integer; i : Integer; MemStrm : TMemoryStream; begin MemStrm := TMemoryStream.Create; try { No of Bars is 1 less than count, since TITLE is the first parameter } NoOfBars := Request.QueryFields.Count -1; { Set the size of the dynamic arrays } SetLength(BarTitles,NoOfBars); SetLength(BarValues, NoOfBars); { Get the Title of the Chart } ChartTitle := StringReplace(Request.QueryFields.Values['Title'],'_',' ',[rfReplaceAll]); { Start from 1 since we don't need the Chart Title here } for i := 1 to Request.QueryFields.Count -1 do begin { Don't Remove the underscores as yet ! } { Dynamic arrays are zero based ! } BarTitles[i-1] := Request.QueryFields.Names[i]; BarValues[i-1] := StrToInt(Request.QueryFields.Values[BarTitles[i-1]]); end; { We now have a populated arrays of Bar Titles and their respective values } finally end; DrawChart(MemStrm, ChartTitle, BarTitles, BarValues); Result := MemStrm; end;
procedure TWebModule1.DrawChart(var MemStream : TMemoryStream;ChartTitle: string; BarTitles: array of string; BarValues : array of Integer); var HorizBarSeries : THorizBarSeries; FChart : TChart; FJPEG : TJPEGImage; Bitmap : TBitmap; Rect : TRect; i : Integer; begin { This is the function the actually creates the TChart on the fly and sends back a MemoryStream (containing a JPEG image You need to add the units: JPEG, Chart, Series, Graphics, Controls, TeEngine to the uses clause in the implementation section. } FChart := TChart.Create(nil); FJPEG := TJPEGImage.Create; Bitmap := nil; try { Initialize Chart Properties } FChart.Color := clWhite; FChart.BevelOuter := bvNone; FChart.Legend.Visible := False; { Define the Size of the Chart Image } Rect.Left := 0; Rect.Top := 0; Rect.Right := 250; Rect.Bottom := 180; { Draw the Chart } HorizBarSeries := THorizBarSeries.Create(FChart); HorizBarSeries.BarStyle := bsRectGradient; HorizBarSeries.ParentChart := FChart; HorizBarSeries.ShowInLegend := False; HorizBarSeries.Marks.Style := smsValue; Randomize; with FChart do begin SeriesList.Clear; for i := Low(BarTitles) to High(BarTitles) do begin with HorizBarSeries do begin Add(BarValues[i], StringReplace(BarTitles[i],'_',' ',[rfReplaceAll]),Random(2147483648)); end; end; { for i := Low(BarTitles) to High(BarTitles) do } SeriesList.Add(HorizBarSeries); with Title do begin Font.Size := 10; Font.Color := clBlack; Text.Clear; Text.Add(ChartTitle); end; end; { Generate the Chart as a Bitmap } Bitmap := FChart.TeeCreateBitmap(clWhite, Rect); FJPEG.Assign(Bitmap); FJPEG.SaveToStream(MemStream); MemStream.Position := 0; finally Bitmap.Free; FChart.Free; FJPEG.Free; { No need to Free MemStrm. The ISAPI Framework will take care of that } end; end;
The Topic Ratings image that is produced is actually 4 (or more) images "concatenated" to form one single image. Basically, in this case, I generate the individual images and then draw them onto a larger image that is as big as would be required depending on the layout. Each image is drawn onto a specific portion of the larger image. By the time of all this is done, the result is what you see at the end of each tutorial. Once single image.
You might want to try the above technique as an exercise after you've played around with this project a bit.