You can easily create your own annotation objects with LEADTOOLS Annotations framework. This secion shows you how to create two new annotations objects and how to write the required designer for them to plug these objects into the LEADTOOLS Annotations Automation framework:
The first object we are going to create is a simple triangle object. This object will have three points for the end points of the triangle and it will support a pen to stroke the triangle edges and a brush to fill the interior.
Add a new class to your project, name this class MyTriangleObject. Following is the source code for this class:
Imports SystemImports System.DrawingImports System.Drawing.Drawing2DImports System.Runtime.SerializationImports Leadtools.Annotations'' Triangle annotation object class' This class will have a 3 points for a triangle objects that can be stroked with a pen and filled with a brush'' our class must be serializable to play well with the annotation load/save and undo/redo features<Serializable()> _Public Class MyTriangleObjectInherits AnnObject ' must derive from AnnObject or one of its derived classes'' our private variables'' the three points that define our triangle<NonSerialized()> Private _firstPoint As AnnPoint<NonSerialized()> Private _secondPoint As AnnPoint<NonSerialized()> Private _thirdPoint As AnnPoint'' constructor'Public Sub New()' supportsPen = yes, supportsBrush = yes, supportsFont = noMyBase.New( _True, _True, _False)' initialize the points_firstPoint = AnnPoint.Empty_secondPoint = AnnPoint.Empty_thirdPoint = AnnPoint.EmptyEnd Sub'' ISerializable implementation'Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)MyBase.New(info, context) ' do not forget to call the base class version' we need to deserialize our private variables here_firstPoint = DirectCast(info.GetValue("FirstPointName", GetType(AnnPoint)), AnnPoint)_secondPoint = DirectCast(info.GetValue("SecondPointName", GetType(AnnPoint)), AnnPoint)_thirdPoint = DirectCast(info.GetValue("ThirdPointName", GetType(AnnPoint)), AnnPoint)End SubPublic Overrides Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)' we need to serialize our private variables here' call the base class versionMyBase.GetObjectData(info, context)' serialize the pointsinfo.AddValue("FirstPointName", _firstPoint, GetType(AnnPoint))info.AddValue("SecondPointName", _secondPoint, GetType(AnnPoint))info.AddValue("ThirdPointName", _thirdPoint, GetType(AnnPoint))End Sub'' accessors to the points'Public Property FirstPoint() As AnnPointGetReturn _firstPointEnd GetSet(ByVal Value As AnnPoint)_firstPoint = ValueEnd SetEnd PropertyPublic Property SecondPoint() As AnnPointGetReturn _secondPointEnd GetSet(ByVal Value As AnnPoint)_secondPoint = ValueEnd SetEnd PropertyPublic Property ThirdPoint() As AnnPointGetReturn _thirdPointEnd GetSet(ByVal Value As AnnPoint)_thirdPoint = ValueEnd SetEnd Property'' AnnObject overrides'Protected Overrides Function Create() As AnnObject' must return a new instance of our classReturn New MyTriangleObjectEnd FunctionPublic Overrides Function Clone() As Object' override the clone method' first call the base implementationDim obj As MyTriangleObject = DirectCast(MyBase.Clone(), MyTriangleObject)' next, copy the pointsobj.FirstPoint = FirstPointobj.SecondPoint = SecondPointobj.ThirdPoint = ThirdPointReturn objEnd FunctionPublic Overrides Function GetGraphicsPath(ByVal mode As AnnGetGraphicsPathMode) As GraphicsPath' must a return a graphics path representation of our object' Note: this object does not require us to override AnnObject.DrawObject since we can' use a graphics path to represents the object completely.' create a new graphics pathDim path As New GraphicsPath' add the triangle points as a series of lines' convert the points to pixels PointFDim pts() As PointF = _{ _FirstPoint.ConvertTo(UnitConverter, AnnUnit.Pixel).ToPointF(), _SecondPoint.ConvertTo(UnitConverter, AnnUnit.Pixel).ToPointF(), _ThirdPoint.ConvertTo(UnitConverter, AnnUnit.Pixel).ToPointF() _}path.AddLines(pts)path.CloseFigure()Return pathEnd FunctionEnd Class
using System;using System.Drawing;using System.Drawing.Drawing2D;using System.Runtime.Serialization;using Leadtools.Annotations;namespace MyNamespace{//// Triangle annotation object class// This class will have a 3 points for a triangle objects that can be stroked with a pen and filled with a brush//[Serializable] // our class must be serializable to play well with the annotation load/save and undo/redo featurespublic class MyTriangleObject:AnnObject // must derive from AnnObject or one of its derived classes{//// our private variables//// the three points that define our triangle[NonSerialized()] private AnnPoint _firstPoint;[NonSerialized()] private AnnPoint _secondPoint;[NonSerialized()] private AnnPoint _thirdPoint;//// constructor//public MyTriangleObject() :base(true, // yes, we require a pentrue, // yes, we require a brushfalse) // no, we do not require a font{// initialize the points_firstPoint = AnnPoint.Empty;_secondPoint = AnnPoint.Empty;_thirdPoint = AnnPoint.Empty;}//// ISerializable implementation//protected MyTriangleObject(SerializationInfo info, StreamingContext context) :base(info, context) // do not forget to call the base class version{// we need to deserialize our private variables here_firstPoint = (AnnPoint)info.GetValue("FirstPointName", typeof(AnnPoint));_secondPoint = (AnnPoint)info.GetValue("SecondPointName", typeof(AnnPoint));_thirdPoint = (AnnPoint)info.GetValue("ThirdPointName", typeof(AnnPoint));}public override void GetObjectData(SerializationInfo info, StreamingContext context){// we need to serialize our private variables here// call the base class versionbase.GetObjectData(info, context);// serialize the pointsinfo.AddValue("FirstPointName", _firstPoint, typeof(AnnPoint));info.AddValue("SecondPointName", _secondPoint, typeof(AnnPoint));info.AddValue("ThirdPointName", _thirdPoint, typeof(AnnPoint));}//// accessors to the points//public AnnPoint FirstPoint{get{return _firstPoint;}set{_firstPoint = value;}}public AnnPoint SecondPoint{get{return _secondPoint;}set{_secondPoint = value;}}public AnnPoint ThirdPoint{get{return _thirdPoint;}set{_thirdPoint = value;}}//// AnnObject overrides//protected override AnnObject Create(){// must return a new instance of our classreturn new MyTriangleObject();}public override object Clone(){// override the clone method// first call the base implementationMyTriangleObject obj = base.Clone() as MyTriangleObject;// next, copy the pointsobj.FirstPoint = FirstPoint;obj.SecondPoint = SecondPoint;obj.ThirdPoint = ThirdPoint;return obj;}public override GraphicsPath GetGraphicsPath(AnnGetGraphicsPathMode mode){// must a return a graphics path representation of our object// Note: this object does not require us to override AnnObject.DrawObject since we can// use a graphics path to represent the object completely.// create a new graphics pathGraphicsPath path = new GraphicsPath();// add the triangle points as a series of lines// convert the points to pixels PointFPointF[] pts ={FirstPoint.ConvertTo(UnitConverter, AnnUnit.Pixel).ToPointF(),SecondPoint.ConvertTo(UnitConverter, AnnUnit.Pixel).ToPointF(),ThirdPoint.ConvertTo(UnitConverter, AnnUnit.Pixel).ToPointF()};path.AddLines(pts);path.CloseFigure();return path;}}}
This enough to manually add, remove, load, save and draw this object to an annotation container. However, we want to be able to draw and edit this object using the annotation designers. Since we cannot find a suitable draw and edit designer to use with this class, we need to create our own.
First, the draw designer, add a new class to your project, name this class "MyTriangleObjectDrawDesigner" and add the following code:
Imports SystemImports System.DrawingImports System.Windows.FormsImports Leadtools.Annotations'' MyTriangleObject draw designer' Will require the user to click 3 times once for each point'Public Class MyTriangleObjectDrawDesignerInherits AnnDrawDesigner ' must derive from AnnDrawDesigner or one of its derived classes'' private variables'' we need to keep track on next point to addPrivate _clickCount As Integer'' constructor'Public Sub New()_clickCount = 0End Sub'' AnnDrawDesigner overrides'Public Overrides Function MouseDown(ByVal e As MouseEventArgs) As BooleanDim handled As Boolean = False' only process left button clicksIf (e.Button = MouseButtons.Left) Then' check if we have not started drawing yet, DrawObject will be nullIf (IsNothing(DrawObject)) Then' yes, create a new MyTriangleObject from ObjectTemplateDim obj As MyTriangleObject = DirectCast(ObjectTemplate.Clone(), MyTriangleObject)' setup the pointsDim pt As AnnPoint = GetLogicalAnnPoint(e.X, e.Y, obj.FirstPoint.Unit)obj.FirstPoint = ptobj.SecondPoint = ptobj.ThirdPoint = pt' start drawing this new objectStartWorking(obj)handled = True' we processed first click_clickCount = _clickCount + 1Else' an object is already being drawn, so process next click' get our object and assign next point to itDim obj As MyTriangleObject = DirectCast(DrawObject, MyTriangleObject)Dim pt As AnnPoint = GetLogicalAnnPoint(e.X, e.Y, obj.FirstPoint.Unit)If (_clickCount = 1) Then' second pointobj.SecondPoint = pt_clickCount = _clickCount + 1ElseIf (_clickCount = 2) Then' third pointobj.ThirdPoint = pt' we are done!EndWorking()End IfEnd IfElse' we want to cancel the drawing if any other button has been clickedIf (Not IsNothing(DrawObject)) ThenCancel()handled = TrueEnd IfEnd IfReturn handledEnd FunctionPublic Overrides Function MouseMove(ByVal e As MouseEventArgs) As BooleanDim handled As Boolean = False' check if we are already drawing an objectIf (Not IsNothing(DrawObject)) Then' yes, get this object and assign the next point' first, save the old invalid rectangleDim rcOld As Rectangle = DrawObject.InvalidRectangle' get out object and assign the pointDim obj As MyTriangleObject = DirectCast(DrawObject, MyTriangleObject)Dim pt As AnnPoint = GetLogicalAnnPoint(e.X, e.Y, obj.FirstPoint.Unit)If (_clickCount = 1) Thenobj.SecondPoint = ptElseIf (_clickCount = 2) Thenobj.ThirdPoint = ptEnd If' get the new invalid rectangleDim rcNew As Rectangle = DrawObject.InvalidRectangle' continue drawing this objectWorking(Rectangle.Union(rcOld, rcNew))handled = TrueEnd IfReturn handledEnd FunctionPublic Overrides Function MouseUp(ByVal e As MouseEventArgs) As Boolean' we do not need to do anything special on mouse up.' so just see if we are drawing to return true (we handled it)Dim handled As Boolean = FalseIf (IsNothing(DrawObject)) Thenhandled = TrueEnd IfReturn handledEnd FunctionEnd Class
using System;using System.Drawing;using System.Windows.Forms;using Leadtools.Annotations;namespace MyNamespace{//// MyTriangleObject draw designer// Will require the user to click 3 times once for each point//public class MyTriangleObjectDrawDesigner :AnnDrawDesigner // must derive from AnnDrawDesigner or one of its derived classes{// private variables//// we need to keep track on next point to addprivate int _clickCount;//// constructor//public MyTriangleObjectDrawDesigner(){_clickCount = 0;}//// AnnDrawDesigner overrides//public override bool MouseDown(MouseEventArgs e){bool handled = false;// only process left button clicksif(e.Button == MouseButtons.Left){// check if we have not started drawing yet, DrawObject will be nullif(DrawObject == null){// yes, create a new MyTriangleObject from ObjectTemplateMyTriangleObject obj = ObjectTemplate.Clone() as MyTriangleObject;// setup the pointsAnnPoint pt = GetLogicalAnnPoint(e.X, e.Y, obj.FirstPoint.Unit);obj.FirstPoint = pt;obj.SecondPoint = pt;obj.ThirdPoint = pt;// start drawing this new objectStartWorking(obj);handled = true;// we processed first click_clickCount++;}else{// an object is already being drawn, so process next click// get our object and assign next point to itMyTriangleObject obj = DrawObject as MyTriangleObject;AnnPoint pt = GetLogicalAnnPoint(e.X, e.Y, obj.FirstPoint.Unit);if(_clickCount == 1){// second pointobj.SecondPoint = pt;_clickCount++;}else if(_clickCount == 2){// third pointobj.ThirdPoint = pt;// we are done!EndWorking();}}}else{// we want to cancel the drawing if any other button has been clickedif(DrawObject != null){Cancel();handled = true;}}return handled;}public override bool MouseMove(MouseEventArgs e){bool handled = false;// check if we are already drawing an objectif(DrawObject != null){// yes, get this object and assign the next point// first, save the old invalid rectangleRectangle rcOld = DrawObject.InvalidRectangle;// get out object and assign the pointMyTriangleObject obj = DrawObject as MyTriangleObject;AnnPoint pt = GetLogicalAnnPoint(e.X, e.Y, obj.FirstPoint.Unit);if(_clickCount == 1)obj.SecondPoint = pt;else if(_clickCount == 2)obj.ThirdPoint = pt;// get the new invalid rectangleRectangle rcNew = DrawObject.InvalidRectangle;// continue drawing this objectWorking(Rectangle.Union(rcOld, rcNew));handled = true;}return handled;}public override bool MouseUp(MouseEventArgs e){// we do not need to do anything special on mouse up.// so just see if we are drawing to return true (we handled it)bool handled = false;if(DrawObject == null)handled = true;return handled;}}}
Next, the edit designer, add a new class to your project, name this class "MyTriangleObjectEditDesigner" and add the following code:
Imports SystemImports System.DrawingImports Leadtools.Annotations'' MyTriangleObject edit designer' User can click on any of the points and move them around as well as clicking and dragging the object itself.'Public Class MyTriangleObjectEditDesignerInherits AnnEditDesigner ' must derive from AnnEditDesigner or one of its derived classes'' constructor'Public Sub New()End Sub'' AnnEditDesigner overrides'Public Overrides ReadOnly Property ControlPointCount() As IntegerGet' return the number of control points we need' in this case 3, one for each point in our triangleReturn 3End GetEnd PropertyPublic Overrides Function GetControlPointsLocation() As AnnPoint()' return the position of these control points' in this case, same as the points from our objectDim obj As MyTriangleObject = DirectCast(EditObject, MyTriangleObject)Return New AnnPoint() _{ _obj.FirstPoint, _obj.SecondPoint, _obj.ThirdPoint() _}End PropertyProtected Overrides Sub MoveControlPoint(ByVal controlPointIndex As Integer, ByVal pt As AnnPoint)' user has clicked and moved a point.' based on the index, we can tell if the user dragged the first, second or third pointDim obj As MyTriangleObject = DirectCast(EditObject, MyTriangleObject)Select Case (controlPointIndex)Case 0' first pointobj.FirstPoint = pt.ConvertTo(Container.UnitConverter, obj.FirstPoint.Unit)Case 1' second pointobj.SecondPoint = pt.ConvertTo(Container.UnitConverter, obj.SecondPoint.Unit)Case 2' third pointobj.ThirdPoint = pt.ConvertTo(Container.UnitConverter, obj.ThirdPoint.Unit)End SelectEnd Sub' Note, we will not override Move or MoveName since the default implementation is good enough for our objectEnd Class
using System;using System.Drawing;using Leadtools.Annotations;namespace MyNamespace{//// MyTriangleObject edit designer// The user can click on any of the points and move them around, as well as clicking and dragging the object itself.//public class MyTriangleObjectEditDesigner :AnnEditDesigner // must derive from AnnEditDesigner or one of its derived classes{//// constructor//public MyTriangleObjectEditDesigner(){}//// AnnEditDesigner overrides//public override int ControlPointCount{get{// return the number of control points we need// in this case 3, one for each point in our trianglereturn 3;}}public override AnnPoint[] GetControlPointsLocation(){// return the position of these control points// in this case, same as the points from our objectMyTriangleObject obj = EditObject as MyTriangleObject;return new AnnPoint[]{obj.FirstPoint,obj.SecondPoint,obj.ThirdPoint};}protected override void MoveControlPoint(int controlPointIndex, AnnPoint pt){// user has clicked and moved a point.// based on the index, we can tell if the user dragged the first, second or third pointMyTriangleObject obj = EditObject as MyTriangleObject;switch(controlPointIndex){case 0:// first pointobj.FirstPoint = pt.ConvertTo(Container.UnitConverter, obj.FirstPoint.Unit);break;case 1:// second pointobj.SecondPoint = pt.ConvertTo(Container.UnitConverter, obj.SecondPoint.Unit);break;case 2:// third pointobj.ThirdPoint = pt.ConvertTo(Container.UnitConverter, obj.ThirdPoint.Unit);break;}}// Note, we will not override Move or MoveName since the default implementation is good enough for our object}}
Now we are going to add another object. The CheckBox class will use the ControlPaint class to draw a check box along with a text string. Our class will not support a pen, but a brush and a text. We will use the AnnTextObject as the base class for our CheckBox class.
Start by adding a new class to your project, name this class MyCheckBoxObject. The following is the source code for this class:
Imports SystemImports System.DrawingImports System.Drawing.Drawing2DImports System.Windows.FormsImports System.Runtime.SerializationImports Leadtools.Annotations'' CheckBox annotation object class' our class will use the ControlPaint class to draw a check box along with text.' our class will not support a pen, but a brush and a text'' our class must be serializable to play well with the annotation load/save and undo/redo features<Serializable()> _Public Class MyCheckBoxObjectInherits AnnTextObject ' We will derive from AnnTextObject so we already have the text'' our private variables'' the checkbox state<NonSerialized()> Private _checked As Boolean'' constructor'Public Sub New()' supportsPen = no, supportsBrush = yes, supportsFont = yesMyBase.New( _False, _True, _True)' initialize the state_checked = FalseEnd Sub'' ISerializable implementation'Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)MyBase.New(info, context) ' do not forget to call the base class version' we need to deserialize our private variables here_checked = info.GetBoolean("Checked")End SubPublic Overrides Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)' we need to serialize our private variables here' call the base class versionMyBase.GetObjectData(info, context)' serialize the stateinfo.AddValue("Checked", _checked)End Sub'' accessors'Public Property Checked() As BooleanGetReturn _checkedEnd GetSet(ByVal Value As Boolean)_checked = ValueEnd SetEnd Property'' AnnObject overrides'Protected Overrides Function Create() As AnnObject' must return a new instance of our classReturn New MyCheckBoxObjectEnd FunctionPublic Overrides Function Clone() As Object' override the clone method' first call the base implementationDim obj As MyCheckBoxObject = DirectCast(MyBase.Clone(), MyCheckBoxObject)Return objEnd Function' we will not override GetGraphicsPath, the base class version is' suitable for hittesting and finding the object bounding rectangle.' however, we will override DrawObject to manually paint the objectProtected Overrides Sub DrawObject(ByVal g As Graphics)' begin the drawingDim gState As GraphicsState = BeginDraw(g)If (Not gState Is Nothing) Then' get the bounding rectangle in pixelsDim boundsPixels As RectangleF = Bounds.ConvertTo(UnitConverter, AnnUnit.Pixel).ToRectangleF()' first, see if we have a brush, if so, fill this rectangleIf (HasBrush) ThenDim br As Brush = Brush.Create(UnitConverter, Bounds)g.FillRectangle(br, boundsPixels)br.Dispose()End IfIf (IsNonEmptyRectangle(boundsPixels)) Then' use the ControlPaint class to paint the check box on the left of the objectDim rc As Rectangle = Rectangle.Round(boundsPixels)If (IsNonEmptyRectangle(rc)) ThenDim checkBoxRectangle As New Rectangle(rc.Left, rc.Top, rc.Height, rc.Height)If (Checked) ThenControlPaint.DrawCheckBox(g, checkBoxRectangle, ButtonState.Checked)ElseControlPaint.DrawCheckBox(g, checkBoxRectangle, ButtonState.Normal)End If' update the bounds rectangleboundsPixels = RectangleF.FromLTRB(checkBoxRectangle.Right, checkBoxRectangle.Top, boundsPixels.Right, boundsPixels.Bottom)End IfEnd If' get the edge margin in pixelsDim edgeMarginPixels As Single = EdgeMargin.Converted(UnitConverter, AnnUnit.Pixel)boundsPixels.Inflate(-edgeMarginPixels, -edgeMarginPixels)' finally draw the textIf (HasFont AndAlso Not IsNothing(Text) AndAlso Text <> String.Empty AndAlso Text.Length > 0 AndAlso IsNonEmptyRectangle(boundsPixels)) ThenDim sf As StringFormat = DirectCast(StringFormat.GenericDefault.Clone(), StringFormat)sf.Alignment = Alignmentsf.LineAlignment = LineAlignmentDim fnt As Font = Font.Create(g, UnitConverter)Dim br As New SolidBrush(TextColor)g.DrawString(Text, fnt, br, boundsPixels, sf)br.Dispose()fnt.Dispose()sf.Dispose()End IfEndDraw(g, gState)End IfEnd SubShared Function FixRectangle(ByVal rc As RectangleF) As RectangleFIf (rc.Left > rc.Right OrElse rc.Top > rc.Bottom) ThenReturn RectangleF.FromLTRB( _Math.Min(rc.Left, rc.Right), _Math.Min(rc.Top, rc.Bottom), _Math.Max(rc.Left, rc.Right), _Math.Max(rc.Top, rc.Bottom))ElseReturn rcEnd IfEnd FunctionShared Function IsNonEmptyRectangle(ByVal rc As RectangleF) As BooleanReturn rc.Width <> 0 AndAlso rc.Height <> 0End FunctionEnd Class
using System;using System.Drawing;using System.Drawing.Drawing2D;using System.Windows.Forms;using System.Runtime.Serialization;using Leadtools.Annotations;namespace MyNamespace{//// CheckBox annotation object class// our class will use the ControlPaint class to draw a check box along with text.// our class will not support a pen, but a brush and a text//[Serializable] // our class must be serializable to play well with the annotation load/save and undo/redo featurespublic class MyCheckBoxObject :AnnTextObject // We will derive from AnnTextObject so we already have the text{//// our private variables//// the checkbox state[NonSerialized()] private bool _checked;//// constructor//public MyCheckBoxObject() :base(false, // no, we do not require a pentrue, // we require a brushtrue) // we require a font{// initialize the state_checked = false;}//// ISerializable implementation//protected MyCheckBoxObject(SerializationInfo info, StreamingContext context) :base(info, context) // do not forget to call the base class version{// we need to deserialize our private variables here_checked = info.GetBoolean("Checked");}public override void GetObjectData(SerializationInfo info, StreamingContext context){// we need to serialize our private variables here// call the base class versionbase.GetObjectData(info, context);// serialize the stateinfo.AddValue("Checked", _checked);}//// accessors//public bool Checked{get{return _checked;}set{_checked = value;}}//// AnnObject overrides//protected override AnnObject Create(){// must return a new instance of our classreturn new MyCheckBoxObject();}public override object Clone(){// override the clone method// first call the base implementationMyCheckBoxObject obj = base.Clone() as MyCheckBoxObject;return obj;}// we will not override GetGraphicsPath, the base class version is// suitable for hittesting and finding the object bounding rectangle.// however, we will override DrawObject to manually paint the objectprotected override void DrawObject(Graphics g){// begin the drawingGraphicsState gState = BeginDraw(g);if(gState != null){// get the bounding rectangle in pixelsRectangleF boundsPixels = Bounds.ConvertTo(UnitConverter, AnnUnit.Pixel).ToRectangleF();// first, see if we have a brush, if so, fill this rectangleif(HasBrush){using(Brush br = Brush.Create(UnitConverter, Bounds))g.FillRectangle(br, boundsPixels);}if(IsNonEmptyRectangle(boundsPixels)){// use the ControlPaint class to paint the check box on the left of the objectRectangle rc = Rectangle.Round(boundsPixels);if(IsNonEmptyRectangle(rc)){Rectangle checkBoxRectangle = new Rectangle(rc.Left, rc.Top, rc.Height, rc.Height);ControlPaint.DrawCheckBox(g, checkBoxRectangle, Checked ? ButtonState.Checked: ButtonState.Normal);// update the bounds rectangleboundsPixels = RectangleF.FromLTRB(checkBoxRectangle.Right, checkBoxRectangle.Top, boundsPixels.Right, boundsPixels.Bottom);}}// get the edge margin in pixelsfloat edgeMarginPixels = EdgeMargin.Converted(UnitConverter, AnnUnit.Pixel);boundsPixels.Inflate(-edgeMarginPixels, -edgeMarginPixels);// finally draw the textif(HasFont && Text != null && Text != string.Empty && Text.Length > 0 && IsNonEmptyRectangle(boundsPixels)){using(StringFormat sf = StringFormat.GenericDefault.Clone() as StringFormat){sf.Alignment = Alignment;sf.LineAlignment = LineAlignment;using(Font font = Font.Create(g, UnitConverter)){using(Brush brush = new SolidBrush(TextColor))g.DrawString(Text, font, brush, boundsPixels, sf);}}}EndDraw(g, gState);}}static RectangleF FixRectangle(RectangleF rc){if(rc.Left > rc.Right || rc.Top > rc.Bottom)return RectangleF.FromLTRB(Math.Min(rc.Left, rc.Right),Math.Min(rc.Top, rc.Bottom),Math.Max(rc.Left, rc.Right),Math.Max(rc.Top, rc.Bottom));elsereturn rc;}static bool IsNonEmptyRectangle(RectangleF rc){return rc.Width != 0 && rc.Height != 0;}}}
We will use the AnnRectangleDrawDesigner and AnnRectangleEditDesigner to draw and edit this class. However, we will add a custom run designer to handle the runtime behavor of this class. At runtime, we want the user to be able to click the CheckBox class and change the checked state.
Add a new class to your project, name this class "MyCheckBoxRunDesigner" and add the following code:
Imports SystemImports System.DrawingImports System.Windows.FormsImports Leadtools.Annotations'' MyCheckBoxObject run designer' When the user clicks the object, flip the Checked state'Public Class MyCheckBoxObjectRunDesignerInherits AnnRunDesigner ' must derive from AnnRunDesigner or one of its derived classes'' constructor'Public Sub New()End Sub'' AnnRunDesigner overrides'Protected Overrides Sub OnRun(ByVal e As AnnRunDesignerEventArgs)' OnRun gets called while the user is running the object' check the stateIf (e.OperationStatus = AnnDesignerOperationStatus.End) Then' End is when the user clicks the object by default, flip the Checked stateDim obj As MyCheckBoxObject = DirectCast(e.Object, MyCheckBoxObject)obj.Checked = Not obj.Checked' repaint this objectOwner.Invalidate(obj.InvalidRectangle)' we chose to ignore the default run for this object' not setting e.Cancel to true will play the hyperlinke.Cancel = TrueEnd IfMyBase.OnRun(e)End SubEnd Class
using System;using System.Drawing;using System.Windows.Forms;using Leadtools.Annotations;namespace MyNamespace{//// MyCheckBoxObject run designer// When the user clicks the object, flip the Checked state//public class MyCheckBoxObjectRunDesigner :AnnRunDesigner // must derive from AnnRunDesigner or one of its derived classes{//// constructor//public MyCheckBoxObjectRunDesigner(){}//// AnnRunDesigner overrides//protected override void OnRun(AnnRunDesignerEventArgs e){// OnRun gets called while the user is running the object// check the stateif(e.OperationStatus == AnnDesignerOperationStatus.End){// End is when the user clicks the object by default, flip the Checked stateMyCheckBoxObject obj = e.Object as MyCheckBoxObject;obj.Checked = !obj.Checked;// repaint this objectOwner.Invalidate(obj.InvalidRectangle);// we chose to ignore the default run for this object// not setting e.Cancel to true will play the hyperlinke.Cancel = true;}base.OnRun(e);}}}
Now let us test these new objects. Add a new RasterImageViewer control to your main form along with four buttons (Text = "Save", "Load", "Clear" and "Run/Design".
At the top of the Form1 class, add the following:
Imports System.IOImports LeadtoolsImports Leadtools.CodecsImports Leadtools.WinFormsImports Leadtools.Annotations
using System.IO;using Leadtools;using Leadtools.Codecs;using Leadtools.WinForms;using Leadtools.Annotations;using MyNamespace;
Add a new handler for Form1 Load event and add the following code:
/// Private theManager As AnnAutomationManagerPrivate Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load' load an image into the viewerDim codecs As New RasterCodecsRasterImageViewer1.Image = codecs.Load("C:\Users\Public\Documents\LEADTOOLS Images\Sample1.cmp")' create and setup the automation managertheManager = New AnnAutomationManager' Create the default (all) automation objects.theManager.CreateDefaultObjects()' add our own Triangle and CheckBox as automation objectsAddMyTriangleAutomationObject(theManager)AddMyCheckBoxAutomationObject(theManager)' create the toolbar and add it to the formtheManager.CreateToolBar()Controls.Add(theManager.ToolBar)theManager.ToolBar.BringToFront()' setup the automation (will create the container as well)Dim automation As New AnnAutomation(theManager, RasterImageViewer1)' setup this automation as the active oneautomation.Active = TrueEnd SubPrivate Sub AddMyTriangleAutomationObject(ByVal manager As AnnAutomationManager)Dim autObj As New AnnAutomationObject' setup the IDautObj.Id = AnnAutomationManager.UserObjectId' give it a friendly nameautObj.Name = "Triangle"' setup the object template' this the object that will be used when drawing new MyTriangleObject. so, setup the pen and brush as desiredDim obj As New MyTriangleObjectobj.Pen = New AnnPen(Color.Blue, New AnnLength(2, AnnUnit.Pixel))obj.Brush = New AnnSolidBrush(Color.Yellow)autObj.Object = obj' setup the draw, edit and run designers typeautObj.DrawDesignerType = GetType(MyTriangleObjectDrawDesigner)autObj.EditDesignerType = GetType(MyTriangleObjectEditDesigner)' we will use the default run designer for this objectautObj.RunDesignerType = GetType(AnnRunDesigner)' we need a toolbar image, so create oneDim btmp As New Bitmap(16, 16)Dim g As Graphics = Graphics.FromImage(btmp)g.FillRectangle(Brushes.Magenta, 0, 0, 16, 16)g.DrawLine(Pens.Black, 8, 2, 14, 14)g.DrawLine(Pens.Black, 14, 14, 2, 14)g.DrawLine(Pens.Black, 2, 14, 8, 2)g.Dispose()autObj.ToolBarImage = btmp' setup the toolbar button tooltip textautObj.ToolBarToolTipText = "Triangle"' setup the cursor used when drawing this objectautObj.DrawCursor = Cursors.Cross' setup the context menu for this object' we will use the default context menuautObj.ContextMenu = AnnAutomationManager.CreateDefaultObjectContextMenu(autObj.Id)' finally add this object to the automation objects of the managermanager.Objects.Add(autObj)End SubPrivate Sub AddMyCheckBoxAutomationObject(ByVal manager As AnnAutomationManager)Dim autObj As New AnnAutomationObject' setup the IDautObj.Id = AnnAutomationManager.UserObjectId + 1' give it a friendly nameautObj.Name = "CheckBox"' setup the object template' this the object that will be used when drawing new MyCheckBoxObject. so, setup the brush, font and textDim obj As New MyCheckBoxObjectobj.Checked = Falseobj.Brush = New AnnSolidBrush(SystemColors.Control)obj.Text = "CheckBox"obj.TextColor = SystemColors.ControlTextobj.Font = New AnnFont("Arial", New AnnLength(8, AnnUnit.Point), FontStyle.Regular)obj.Alignment = StringAlignment.Nearobj.LineAlignment = StringAlignment.CenterautObj.Object = obj' setup the draw, edit and run designers type' note, MyCheckBoxObject will use the default rectangle draw and edit designersautObj.DrawDesignerType = GetType(AnnRectangleDrawDesigner)autObj.EditDesignerType = GetType(AnnRectangleEditDesigner)' use our run designerautObj.RunDesignerType = GetType(MyCheckBoxObjectRunDesigner)' we need a toolbar image, so create oneDim btmp As New Bitmap(16, 16)Dim g As Graphics = Graphics.FromImage(btmp)g.FillRectangle(Brushes.Magenta, 0, 0, 16, 16)ControlPaint.DrawCheckBox(g, 0, 0, 16, 16, ButtonState.Flat Or ButtonState.Checked)g.Dispose()autObj.ToolBarImage = btmp' setup the toolbar button tooltip textautObj.ToolBarToolTipText = "CheckBox"' setup the cursor used when drawing this objectautObj.DrawCursor = Cursors.Cross' setup the context menu for this object' we will insert a new menu item for "Checked" before the properties menu itemDim cm As ContextMenu = AnnAutomationManager.CreateDefaultObjectContextMenu(autObj.Id)' add a handler for the Popup event so we can check/uncheck our new menu itemAddHandler cm.Popup, AddressOf checkedMenuItem_Popup' first a new separator 2 items from the endcm.MenuItems.Add(cm.MenuItems.Count - 2, New MenuItem("-"))' and our menu itemDim checkedMenuItem As New MenuItem("Checked")AddHandler checkedMenuItem.Click, AddressOf checkedMenuItem_Clickcm.MenuItems.Add(cm.MenuItems.Count - 2, checkedMenuItem)autObj.ContextMenu = cm' finally add this object to the automation objects of the managermanager.Objects.Add(autObj)End SubPrivate Sub checkedMenuItem_Popup(ByVal sender As Object, ByVal e As EventArgs)' check/uncheck the Checked menu item depending on the object stateDim cm As ContextMenu = DirectCast(sender, ContextMenu)Dim automation As AnnAutomation = theManager.Automations(0)Dim obj As MyCheckBoxObject = DirectCast(automation.CurrentEditObject, MyCheckBoxObject)cm.MenuItems(cm.MenuItems.Count - 3).Checked = obj.CheckedEnd SubPrivate Sub checkedMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs)' check/uncheck the object' do not forget to add an undo nodeDim automation As AnnAutomation = theManager.Automations(0)Dim obj As MyCheckBoxObject = DirectCast(automation.CurrentEditObject, MyCheckBoxObject)automation.BeginUndo()obj.Checked = Not obj.Checkedautomation.EndUndo()automation.Viewer.Invalidate(obj.InvalidRectangle)End Sub
private AnnAutomationManager theManager;private void Form1_Load(object sender, System.EventArgs e){// load an image into the viewerRasterCodecs codecs = new RasterCodecs();rasterImageViewer1.Image = codecs.Load(@"C:\Users\Public\Documents\LEADTOOLS Images\Sample1.cmp");// create and setup the automation managertheManager = new AnnAutomationManager();// Create the default (all) automation objects.theManager.CreateDefaultObjects();// add our own Triangle and CheckBox as automation objectsAddMyTriangleAutomationObject(theManager);AddMyCheckBoxAutomationObject(theManager);// create the toolbar and add it to the formtheManager.CreateToolBar();Controls.Add(theManager.ToolBar);theManager.ToolBar.BringToFront();// setup the automation (will create the container as well)AnnAutomation automation = new AnnAutomation(theManager, rasterImageViewer1);// setup this automation as the active oneautomation.Active = true;}private void AddMyTriangleAutomationObject(AnnAutomationManager manager){AnnAutomationObject autObj = new AnnAutomationObject();// setup the IDautObj.Id = AnnAutomationManager.UserObjectId;// give it a friendly nameautObj.Name = "Triangle";// setup the object template// this the object that will be used when drawing new MyTriangleObject. so, setup the pen and brush as desiredMyTriangleObject obj = new MyTriangleObject();obj.Pen = new AnnPen(Color.Blue, new AnnLength(2, AnnUnit.Pixel));obj.Brush = new AnnSolidBrush(Color.Yellow);autObj.Object = obj;// setup the draw, edit and run designers typeautObj.DrawDesignerType = typeof(MyTriangleObjectDrawDesigner);autObj.EditDesignerType = typeof(MyTriangleObjectEditDesigner);// we will use the default run designer for this objectautObj.RunDesignerType = typeof(AnnRunDesigner);// we need a toolbar image, so create oneBitmap btmp = new Bitmap(16, 16);using(Graphics g = Graphics.FromImage(btmp)){g.FillRectangle(Brushes.Magenta, 0, 0, 16, 16);g.DrawLine(Pens.Black, 8, 2, 14, 14);g.DrawLine(Pens.Black, 14, 14, 2, 14);g.DrawLine(Pens.Black, 2, 14, 8, 2);}autObj.ToolBarImage = btmp;// setup the toolbar button tooltip textautObj.ToolBarToolTipText = "Triangle";// setup the cursor used when drawing this objectautObj.DrawCursor = Cursors.Cross;// setup the context menu for this object// we will use the default context menuautObj.ContextMenu = AnnAutomationManager.CreateDefaultObjectContextMenu(autObj.Id);// finally add this object to the automation objects of the managermanager.Objects.Add(autObj);}private void AddMyCheckBoxAutomationObject(AnnAutomationManager manager){AnnAutomationObject autObj = new AnnAutomationObject();// setup the IDautObj.Id = AnnAutomationManager.UserObjectId + 1;// give it a friendly nameautObj.Name = "CheckBox";// setup the object template// this the object that will be used when drawing new MyCheckBoxObject. so, setup the brush, font and textMyCheckBoxObject obj = new MyCheckBoxObject();obj.Checked = false;obj.Brush = new AnnSolidBrush(SystemColors.Control);obj.Text = "CheckBox";obj.TextColor = SystemColors.ControlText;obj.Font = new AnnFont("Arial", new AnnLength(8, AnnUnit.Point), FontStyle.Regular);obj.Alignment = StringAlignment.Near;obj.LineAlignment = StringAlignment.Center;autObj.Object = obj;// setup the draw, edit and run designers type// note, MyCheckBoxObject will use the default rectangle draw and edit designersautObj.DrawDesignerType = typeof(AnnRectangleDrawDesigner);autObj.EditDesignerType = typeof(AnnRectangleEditDesigner);// use our run designerautObj.RunDesignerType = typeof(MyCheckBoxObjectRunDesigner);// we need a toolbar image, so create oneBitmap btmp = new Bitmap(16, 16);using(Graphics g = Graphics.FromImage(btmp)){g.FillRectangle(Brushes.Magenta, 0, 0, 16, 16);ControlPaint.DrawCheckBox(g, 0, 0, 16, 16, ButtonState.Flat | ButtonState.Checked);}autObj.ToolBarImage = btmp;// setup the toolbar button tooltip textautObj.ToolBarToolTipText = "CheckBox";// setup the cursor used when drawing this objectautObj.DrawCursor = Cursors.Cross;// setup the context menu for this object// we will insert a new menu item for "Checked" before the properties menu itemContextMenu cm = AnnAutomationManager.CreateDefaultObjectContextMenu(autObj.Id);// add a handler for the Popup event so we can check/uncheck our new menu itemcm.Popup += new EventHandler(checkedMenuItem_Popup);// first a new seperator 2 items from the endcm.MenuItems.Add(cm.MenuItems.Count - 2, new MenuItem("-"));// and our menu itemMenuItem checkedMenuItem = new MenuItem("Checked");checkedMenuItem.Click += new EventHandler(checkedMenuItem_Click);cm.MenuItems.Add(cm.MenuItems.Count - 2, checkedMenuItem);autObj.ContextMenu = cm;// finally add this object to the automation objects of the managermanager.Objects.Add(autObj);}private void checkedMenuItem_Popup(object sender, EventArgs e){// check/uncheck the Checked menu item depending on the object stateContextMenu cm = sender as ContextMenu;AnnAutomation automation = theManager.Automations[0];MyCheckBoxObject obj = automation.CurrentEditObject as MyCheckBoxObject;cm.MenuItems[cm.MenuItems.Count - 3].Checked = obj.Checked;}private void checkedMenuItem_Click(object sender, EventArgs e){// check/uncheck the object// do not forget to add an undo nodeAnnAutomation automation = theManager.Automations[0];MyCheckBoxObject obj = automation.CurrentEditObject as MyCheckBoxObject;automation.BeginUndo();obj.Checked = !obj.Checked;automation.EndUndo();automation.Viewer.Invalidate(obj.InvalidRectangle);}
Add event handlers for Button1, Button2, Button3 and Button3 and add the following code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click' Save button clickedDim codecs As New AnnCodecscodecs.Save("C:\Test.ann", theManager.Automations(0).Container, AnnCodecsFormat.Serialize, 1, AnnCodecsSavePageMode.Overwrite)RasterImageViewer1.Invalidate()End SubPrivate Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click' Load button clickedDim codecs As New AnnCodecstheManager.Automations(0).Container.Objects.Clear()codecs.Load("C:\Test.ann", theManager.Automations(0).Container, 1)RasterImageViewer1.Invalidate()End SubPrivate Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click' Clear button clickedtheManager.Automations(0).Container.Objects.Clear()RasterImageViewer1.Invalidate()Dim editDesigner As AnnEditDesigner = TryCast(theManager.Automations(0).CurrentDesigner, AnnEditDesigner)If Not editDesigner Is Nothing TheneditDesigner.End()End IfEnd SubPrivate Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click' Run/Design button clickedIf (theManager.UserMode = AnnUserMode.Design) ThentheManager.UserMode = AnnUserMode.RunElsetheManager.UserMode = AnnUserMode.DesignEnd IfEnd Sub
private void button1_Click(object sender, System.EventArgs e){// Save button clickedAnnCodecs codecs = new AnnCodecs();codecs.Save(@"C:\Test.ann", theManager.Automations[0].Container, AnnCodecsFormat.Serialize, 1, AnnCodecsSavePageMode.Overwrite);rasterImageViewer1.Invalidate();}private void button2_Click(object sender, System.EventArgs e){// Load button clickedAnnCodecs codecs = new AnnCodecs();theManager.Automations[0].Container.Objects.Clear();codecs.Load(@"C:\Test.ann", theManager.Automations[0].Container, 1);rasterImageViewer1.Invalidate();}private void button3_Click(object sender, System.EventArgs e){// Clear button clickedtheManager.Automations[0].Container.Objects.Clear();rasterImageViewer1.Invalidate();AnnEditDesigner editDesigner = theManager.Automations[0].CurrentDesigner as AnnEditDesigner;if(editDesigner != null){editDesigner.End();}}private void button4_Click(object sender, System.EventArgs e){// Run/Design button clickedtheManager.UserMode = theManager.UserMode == AnnUserMode.Design ? AnnUserMode.Run : AnnUserMode.Design;}
Build and run the application to test it.
|
Products |
Support |
Feedback: Implementing User Defined Objects With LEADTOOLS Annotations (Deprecated) |
Introduction |
Help Version 19.0.2017.3.3
|

Raster .NET | C API | C++ Class Library | JavaScript HTML5
Document .NET | C API | C++ Class Library | JavaScript HTML5
Medical .NET | C API | C++ Class Library | JavaScript HTML5
Medical Web Viewer .NET
Your email has been sent to support! Someone should be in touch! If your matter is urgent please come back into chat.
Chat Hours:
Monday - Friday, 8:30am to 6pm ET
Thank you for your feedback!
Please fill out the form again to start a new chat.
All agents are currently offline.
Chat Hours:
Monday - Friday
8:30AM - 6PM EST
To contact us please fill out this form and we will contact you via email.