Working with Diagram Studio programmatically
Inserting objects in diagram
To insert objects in diagram, you must:
Creates the object, instantiating the class of object.
Sets the owner of object as the same owner of the diagram.
Sets all other visual properties of the object you might want to change.
Sets the diagram property to the diagram component.
Example:
MyBlock := TDiagramBlock.Create(atDiagram1.Owner);
with MyBlock do
begin
Left := 10;
Top := 10;
Width := 150;
Height := 50;
Diagram := atDiagram1;
end;
Removing an object from diagram
To remove an object from the diagram, simply destroy the object. Some examples:
MyDiagramBlock1.Free; //Destroys the object MyDiagramBlock1
{Destroy all lines in diagram}
while atDiagram.LinkCount > 0 do
atDiagram.Links[0].Free;
Alternatively, you can remove the currently selected objects in diagram:
atDiagram1.DeleteSelecteds;
Adding a line between two blocks (linking blocks)
A line start (or end) can be attached to a link point. To link two blocks, you must attach the start of the line to a block linkpoint, and attach the end of line to another block linkpoint. Example:
MyLine := TDiagramLine.Create(atDiagram1.Owner);
with MyLine do
begin
Diagram := atDiagram1;
SourceLinkPoint.AnchorLink := SomeBlock.LinkPoints[0]; //link start point to someblock
TargetLinkPoint.AnchorLink := AnotherBlock.LinkPoints[1]; // link end point to anotherblock
end;
Creating a TDiagramPolyLine: line with multiple points
The Handles property of a diagram line control the position of its intermediate points. That's valid for all line types, including polylines, bezier, side lines, etc:
curDataBlock := TDiagramPolyLine.Create(atDiagram1.Owner);
with curDataBlock do
begin
Handles.Clear;
Handles.Add.OrPoint := Dot(49,115);
Handles.Add.OrPoint := Dot(151,179);
Handles.Add.OrPoint := Dot(191,124);
Handles.Add.OrPoint := Dot(212,84);
Diagram := atDiagram1;
end;
Using a metafile as the block shape
If you want to use a metafile picture as the block shape, you need to load the metafile to block, and also set the shape to none, so the shape block is not drawn, only the picture. Example:
with atDiagram1.Blocks[0] do
begin
Shape := bsNoShape;
Picture.LoadFromFile('mymetafile.wmf');
end;
Creating linkpoints in a block
To create linkpoints, the LinkPoints collection must be used. Each linkpoint is a collection item. The position of the linkpoint must be indicated as a relative point related to the rect specified by the Drawer.OriginalRect property. By default, OriginalRect is (0, 0, 100, 100), making it easy to define link points positions in terms of % of block width/height.
The following example, from FlowchartBlocks.pas
unit, shows how to
define four link points for the block, in the top, left, right and
bottom sides of the block, in the middle of each side segment.
procedure TFlowchartBlock.UpdateLinkPoints;
begin
LinkPoints.Clear;
with Drawer.OriginalRect do
begin
LinkPoints.Add((Right - Left) / 2, Top, aoUp);
LinkPoints.Add((Right - Left) / 2, Bottom, aoDown);
LinkPoints.Add(Left, (Bottom - Top) / 2, aoLeft);
LinkPoints.Add(Right, (Bottom - Top) / 2, aoRight);
end;
end;
Accessing TextCell of a line object
The line objects has at least one text cell defined by default. If you want to access a text cell of a TDiagramLine object, for example, you can use this code:
TextCell := LinkLine.TextCells[0];
Changing category of blocks in toolbar
Calling RegisterDControl for any existing block will re-register it. So it's possible to change the category, or caption for the block. The following code "moves" all the blocks to the "MyBlocks" category, and sets that category in the toolbar.
procedure TForm1.FormCreate(Sender: TObject);
begin
DiagramToolBar1.Category := 'MyBlocks';
RegisterDControl(TDiagramBlock, '', 'Simple block', 'MyBlocks');
RegisterDControl(TDiagramLine, '', 'Line', 'MyBlocks');
RegisterDControl(TTextBlock, '', 'Text block', 'MyBlocks');
RegisterDControl(TDiagramSideLine, '', 'Side line', 'MyBlocks');
DiagramToolBar1.Rebuild;
end;
Prevent a line from being dettached from a block
The following sample code is obsolete since version 2.1, which introduced a new TDiagramLine.RequiresConnections property. If you don't want a line to be dettached from a block, just set its RequiresConnections property to true:
DiagramLine1.RequiresConnections := true;
But for learning purposes, we will keep the old code here:
The following sample code does not allow an user to remove a line from a linkpoint unless it connects it to another linkpoint
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, atDiagram, ExtCtrls, DiagramExtra, FlowChartBlocks;
const
CM_CONNECTLINKPOINTS = WM_USER + 1000;
type
TForm1 = class(TForm)
DiagramToolBar1: TDiagramToolBar;
atDiagram1: TatDiagram;
procedure atDiagram1LinkRemoved(Sender: TObject;
ADControl: TDiagramControl; ALink: TCustomDiagramLine;
ALinkPoint: TLinkPoint);
private
procedure CMConnectLinkPoints(var Msg: TMessage); message CM_CONNECTLINKPOINTS;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.atDiagram1LinkRemoved(Sender: TObject;
ADControl: TDiagramControl; ALink: TCustomDiagramLine;
ALinkPoint: TLinkPoint);
var
c: integer;
BlockLinkPoint: TLinkPoint;
begin
if not (csDestroying in ALink.ComponentState) then
if ADControl is TCustomDiagramBlock then
begin
BlockLinkPoint := nil;
// Find the linkpoint of the block where the line was connected to
for c := 0 to TCustomDiagramBlock(ADControl).LinkPoints.Count - 1 do
if ALinkPoint.AnchorLink = TCustomDiagramBlock(ADControl).LinkPoints[c] then
begin
BlockLinkPoint := TCustomDiagramBlock(ADControl).LinkPoints[c];
break;
end;
// Reconnect line to the block link point
PostMessage(Handle, CM_CONNECTLINKPOINTS, integer(ALinkPoint), integer(BlockLinkPoint));
end;
end;
procedure TForm1.CMConnectLinkPoints(Var Msg: TMessage);
begin
if TLinkPoint(Msg.wParam).AnchorLink = nil then
TLinkPoint(Msg.wParam).AnchorLink := TLinkPoint(Msg.LParam);
end;
end.
Using Diagram Studio in a DLL
Diagram Studio has some restrictions in order to be used in a DLL. It's a limitation of GDI+ graphical library from Microsoft. Diagram Studio initializes and finalizes the GDI+ library automatically, except when it's inside a DLL (IsLibrary = true), since doing that can cause deadlocks or system crashes.
So in order to use Diagram Studio in a DLL, you must initialize and finalize GDI+ library by yourself, from your EXE application. We have public procedures in Diagram units that make that job for you, so you may just export those functions in your DLL and call them from your application.
The DLL containing Diagram Studio must export the InitializeGdiPlus and
FinalizeGdiPlus procedures, both declared at DgrGdipObj
unit.
library DiagramDLL;
uses
SysUtils,
Classes,
DgrGdipObj;
{$R *.res}
exports
InitializeGdiPlus,
FinalizeGdiPlus;
begin
end.
The application using DLL must call InitializeGdiPlus before using Diagram Studio (or at beginning of program) and FinalizeGdiPlus before it terminates.
procedure InitializeGdiPlus; external 'DiagramDLL.dll';
procedure FinalizeGdiPlus; external 'DiagramDLL.dll';
procedure TestLoadUnloadDiagramDLL;
var
HInst: HMODULE;
begin
HInst := LoadLibrary('DiagramDLL.dll');
if HInst <> 0 then
begin
InitializeGdiPlus;
ShowMessage('Loaded');
FinalizeGdiPlus;
if FreeLibrary(HInst) then
ShowMessage('Unloaded');
end
else
ShowMessage('Not Loaded!');
end;
Auto-layout
You can perform an automatic layout of the diagram. To do that, just call method ApplyLayout passing a valid layout algorithm object:
atDiagram1.ApplyLayout(Layout);
Currently there is only one layout algorithm available, which is
TForceLayout, declared in unit AutoLayout.Force
:
uses
AutoLayout.Force;
var
Layout: TForceLayout;
begin
Layout := TForceLayout.Create;
try
Layout.DisplacementThreshold := 10;
Layout.DefaultSpringLength := 100;
Layout.MaxIterations := 500;
Layout.Damping := 0.5
Layout.RepulsionConstant := 100000;
Layout.AttractionConstant := 0.2
atDiagram1.ApplyLayout(Layout);
finally
Layout.Free;
end;
end;
Each layout algorithm has its own parameters. The code above shows the available parameters for force algorithm. You don't need to set any of those, since default values are already defined.