Monday, March 23, 2015In my childhood, I was always fascinated by Lego. I spent countless hours building all kinds of things with Lego, many times a town, all kinds of houses but never forgetting a few sporty cars to accompany each house. What I could build with Lego was mostly limited by my imagination and the number of blocks I had (which was eventually quite a lot as I didn't let any anniversary or special celebration go by to ask for more Lego blocks as a gift). What is so nice about Lego is the easy to use interface between all kinds of blocks. All blocks fit on other blocks and can be effortlessly put together.
From my Lego time, fast-forward about 20 years, in 1995 Delphi brought us the concept of TComponent, an evolution of the TObject originated in Turbo Pascal. TComponent was an invention as magic as the Lego building block. While seamlessly interfacing with its parent, siblings, childs, the major difference with the basic Lego block is that a component can be heavily characterized through its properties. This makes 'playing' with components even more fun, interesting & challenging than playing with Lego was. At TMS software, it is always my objective to build interesting components that are as intuitive as possible to use and that could do interesting things while requiring to write a minimum amount of plumbing code to use them. With a wave of new components we released in the past quarter, I set a challenge for myself to explore and smoothen the ways to interface these building blocks to each other. In this article I wanted to highlight the easiness of bringing together quite sophisticated stand-alone components or building blocks to create powerful solutions.
To demonstrate this, we start with the versatile TMS TAdvStringGrid. With a few properties, this turns into an editable 2D data structure. In our sample, the grid is used to present or capture sales information for a 12 month period. The majority of the effort to write code for this sample goes into initializing the grid. In this case, all we still need to do in code is initialize the data contained in the grid:
All other settings are done by setting properties at design-time. Now, what we want to do is represent the information entered in the grid in a chart. The 3 columns of sales related data are represented in 3 series bar charts in TAdvChartView, the TMS chart component. With the easy interfaces of Lego in mind, we also want and can do this without writing much code (3 lines actually to be exact). We drop a TAdvChartView on the form and also 3 TAdvChartLink components. The TAdvChartLink is a component that takes on the interface between the grid data and a chart series data. The TAdvChartLink is so smart to update the chart series when the grid data changes through editing. All we need to do is setup the relationship between a specific range of cells and a series. Grid and chart are connected by assigning the grid to AdvChartLink.Grid and the chart to AdvChartLink.ChartView. What data from the grid is connected to what series is also configured at design-time via properties through the AdvChartLink.GridValues property. The chart itself can be configured at design-time with a special pane and series design-time editor that allows to customize the X-axis, Y-axis, colors, markers, ... everything about the chart actually. So, the only code left to write is the activation of the chartlink after the data is set in the grid:
var i: integer; begin AdvStringGrid1.EditLink := AdvRichEditorEditLink1; AdvStringGrid1.Cells[1,1] := ''; AdvStringGrid1.Cells[2,1] := 'Sales of development tools products per month'; AdvStringGrid1.MergeCells(2,1,2,1); AdvStringGrid1.Cells[0,1] := 'Name'; AdvStringGrid1.Cells[0,0] := 'Month'; AdvStringGrid1.Cells[1,0] := 'Units'; AdvStringGrid1.Cells[2,0] := 'Sales'; AdvStringGrid1.Cells[3,0] := '% Market'; for i := 2 to 13 do begin AdvStringGrid1.Cells[0,i] := FormatSettings.ShortMonthNames[i - 1]; AdvStringGrid1.Ints[1,i] := Random(100); AdvStringGrid1.Ints[2,i] := Random(10000); AdvStringGrid1.Ints[3,i] := Random(50); end; end;
Next step, we want to do is display the chart within an editable rich formatted document as we want to allow the user to create a customizable sales report that includes the chart. To do this, TAdvRichEditor is put on the form as well as a TAdvRichEditorEditToolBar and TAdvRichEditorFormatToolBar. We could have opted for a ribbon UI as well with TAdvRichEditorClipboardRibbonToolBar, TAdvRichEditorFontRibbonToolBar, TAdvRichEditorParagraphRibbonToolBar. TAdvRichEditorEditToolBar and TAdvRichEditorFormatToolBar or the ribbon toolbars are building blocks derived from our TMS Advanced ToolBars & Menus product that provides docking toolbars or ribbon controls. These 3 components together are already sufficient to have a rich formatted document editor without needing to write a single line of code.
AdvChartLink1.Active := true; AdvChartLink2.Active := true; AdvChartLink3.Active := true;
All we do is initialize the content of the document a little bit with:
With this initialization, we have inserted a merge field and a chart instance in the document. The merge field will be used to insert the addressee name with data from the grid.
AdvRichEditor1.InsertMultiLineText('Dear Mr. NAME'#13); AdvRichEditor1.SelStart := 9; AdvRichEditor1.SelLength := 4; AdvRichEditor1.SetSelectionMergeField('NAME'); AdvRichEditor1.InsertMultiLineText('Included is the chart of sales for our development tool product.'#13#13); AdvRichEditor1.AddGraphic(500,400,'CHART');
The chart is added with initial dimensions 500 x 400px and with ID CHART. To have the chart displayed in the document, the event handler to draw custom graphic objects in the document needs to be coded:
The merge of the addressee, NAME merge field in the document is done from the grid's OnCellValidate event for the cell 1,1 that holds the name. The code in this event handler is:
procedure TForm1.AdvRichEditor1DrawGraphic(Sender: TObject; ACanvas: TCanvas; ARect: TRect; AID: string); begin if AID = 'CHART' then AdvChartView1.PrintAllPanes(ACanvas, ARect); end;
But we are not satisfied yet. Wanting to generate a PDF document from the TAdvRichEditor content, a TAdvRichEditorPDFIO component is dropped on the form and connected to the TAdvRichEditor. All we need to do to get the PDF document from the TAdvRichEditor is add the code:
procedure TForm1.AdvStringGrid1CellValidate(Sender: TObject; ACol, ARow: Integer; var Value: string; var Valid: Boolean); var sl: TStringList; begin if (ACol = 1) and (ARow = 1) then begin AdvRichEditor1.UnMerge; sl := TStringList.Create; try sl.Values['NAME'] := Value; AdvRichEditor1.Merge(sl); finally sl.Free; end; end; end;
This will prompt for the filename to use and can optionally automatically display the generated PDF in the Windows default PDF viewer, again all without needing to write any additional code.
begin AdvRichEditorPDFIO1.Save; end;
While not really functionally used in the sample, we just wanted to show yet another seamless integration between the TAdvRichEditor and TAdvStringGrid. In this case, the integration means we can use the TAdvRichEditor as inplace editor in the grid. And here comes yet another integration as this inplace TAdvRichEditor can optionally have a popup formatting toolbar for on-the-fly formatting of the editor content without the need for extra screen estate to put another toolbar or ribbon.
Get started by playing with these building blocks with the sample you can download here and the latest versions of TMS Component Pack and TMS Advanced Charts. We hope this article is an inspiration to have you create other powerful integrations. We're curious to see your results and hear about any suggestions you might have for new integrations or things that can make the integrations even smoother.
This blog post has received 1 comment.
Previous | Next | Index