VintaSoft Imaging .NET SDK 12.3: Documentation for .NET developer
In This Topic
    How to create custom mark annotation?
    In This Topic
    VintaSoft Annotation .NET Plug-in allows to create custom annotation from scratch, i.e.:
    Also Plug-in allows to use any standard annotation as a base class for custom annotation because SDK provides open architecture.


    This article describes how to create mark annotation from scratch. For doing the task is necessary to create 1 enumeration and 3 classes:


    MarkAnnotationType enumeration

    MarkAnnotationType enumeration defines possible mark type: rectangle, tick, star or cross.

    Here is an example that shows implementation of MarkAnnotationType enumeration:
    namespace DemosCommonCode.Annotation
    {
        /// <summary>
        /// Specifies the available types of mark annotation.
        /// </summary>
        public enum MarkAnnotationType : int
        {
            /// <summary>
            /// The rectangle.
            /// </summary>
            Rectangle = 0,
    
            /// <summary>
            /// The tick.
            /// </summary>
            Tick = 1,
    
            /// <summary>
            /// The star.
            /// </summary>
            Star = 2,
    
            /// <summary>
            /// The cross.
            /// </summary>
            Cross = 3,
        }
    }
    
    
    Namespace DemosCommonCode.Annotation
        ''' <summary>
        ''' Specifies the available types of mark annotation.
        ''' </summary>
        Public Enum MarkAnnotationType As Integer
            ''' <summary>
            ''' The rectangle.
            ''' </summary>
            Rectangle = 0
    
            ''' <summary>
            ''' The tick.
            ''' </summary>
            Tick = 1
    
            ''' <summary>
            ''' The star.
            ''' </summary>
            Star = 2
    
            ''' <summary>
            ''' The cross.
            ''' </summary>
            Cross = 3
        End Enum
    End Namespace
    
    


    MarkAnnotationData class

    MarkAnnotationData class:

    Here is an example that shows implementation of MarkAnnotationData class:
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    
    using Vintasoft.Imaging;
    using Vintasoft.Imaging.Annotation;
    using Vintasoft.Imaging.Annotation.Rendering;
    
    namespace DemosCommonCode.Annotation
    {
        /// <summary>
        /// Class that holds information about the annotation that displays a mark.
        /// </summary>
        [Serializable]
        public class MarkAnnotationData : AnnotationData
        {
    
            #region Constructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="MarkAnnotationData"/> class.
            /// </summary>
            public MarkAnnotationData()
                : base()
            {
                FillBrush = new AnnotationSolidBrush(Color.Black);
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="AnnotationData"/> class.
            /// </summary>
            /// <param name="info">The SerializationInfo to populate with data.</param>
            /// <param name="context">The destination for this serialization.</param>
            public MarkAnnotationData(SerializationInfo info, StreamingContext context)
                : base(info, context)
            {
                _markType = (MarkAnnotationType)info.GetValue("MarkType", typeof(int));
            }
    
    
            /// <summary>
            /// Initializes the <see cref="MarkAnnotationData"/> class.
            /// </summary>
            static MarkAnnotationData()
            {
                // register renderer form this annotation
                AnnotationRendererFactory.RegisterRendererForAnnotationData(typeof(MarkAnnotationData), typeof(MarkAnnotationRenderer));
            }
    
            #endregion
    
    
    
            #region Properties
    
            MarkAnnotationType _markType = MarkAnnotationType.Tick;
            /// <summary>
            /// Gets or sets a mark type.
            /// </summary>
            [Description("The mark type.")]
            [DefaultValue(MarkAnnotationType.Tick)]
            public MarkAnnotationType MarkType
            {
                get
                {
                    return _markType;
                }
                set
                {
                    if (_markType != value)
                    {
                        ObjectPropertyChangingEventArgs changingArgs =
                            new ObjectPropertyChangingEventArgs("MarkType", _markType, value);
                        if (OnPropertyChanging(changingArgs))
                        {
                            _markType = (MarkAnnotationType)changingArgs.NewValue;
                            OnPropertyChanged(changingArgs.ToChangedEventArgs());
                        }
                    }
                }
            }
    
            #endregion
    
    
    
            #region Methods
    
            /// <summary>
            /// Returns the bounding box of annotation if annotation will have specified location,
            /// size and rotation.
            /// </summary>
            /// <param name="location">Location, in device-independent pixels (1/96th inch),
            /// of annotation.</param>
            /// <param name="size">Size, in device-independent pixels (1/96th inch),
            /// of annotation</param>
            /// <param name="rotation">Rotation, in degrees, of annotation.</param>
            /// <returns>Bounding box of annotation.</returns>
            public override RectangleF GetBoundingBox(PointF location, SizeF size, float rotation)
            {
                PointF[] points = GetReferencePointsInContentSpace();
    
                // rotate
                AnnotationsMath.RotatePointsAt(points, PointF.Empty, Rotation);
                // scale
                AnnotationsMath.ScalePoints(points, size.Width / this.Size.Width, size.Height / this.Size.Height);
                // translate
                AnnotationsMath.TranslatePoints(points, location.X, location.Y);
    
                return AnnotationsMath.GetBoundingBox(points);
            }
    
    
            /// <summary>
            /// Gets an array that contains reference points in content space of this annotation.
            /// </summary>
            /// <returns></returns>
            public virtual PointF[] GetReferencePointsInContentSpace()
            {
                float width = Size.Width;
                float height = Size.Height;
                float w = Math.Min(width / 10, height / 10);
    
                PointF[] points;
                switch (MarkType)
                {
                    case MarkAnnotationType.Rectangle:
                        points = new PointF[]{
                            new PointF(-width / 2, -height/2),
                            new PointF(width / 2, -height/2),
                            new PointF(width / 2, height/2),
                            new PointF(-width / 2, height/2)};
                        break;
    
                    case MarkAnnotationType.Tick:
                        points = new PointF[]{
                            new PointF(-width / 2, 0),
                            new PointF(0, height / 4),
                            new PointF(width / 2, -height / 2),
                            new PointF(0, height / 2),
                            new PointF(-width / 2, 0)};
                        break;
    
                    case MarkAnnotationType.Cross:
                        points = new PointF[]{
                            new PointF(-width / 2, -w),
                            new PointF(-w, -w),
                            new PointF(-w, -height/2),
                            new PointF(w, -height/2),
                            new PointF(w, -w),
                            new PointF(width/2, -w),
                            new PointF(width/2, w),
                            new PointF(w, w),
                            new PointF(w, height/2),
                            new PointF(-w, height/2),
                            new PointF(-w, w),
                            new PointF(-width/2, w)};
                        break;
    
                    case MarkAnnotationType.Star:
                        points = new PointF[]{
                            new PointF(-width / 2, 0),
                            new PointF(-w, -w),
                            new PointF(0, -height/2),
                            new PointF(w, -w),
                            new PointF(width/2, 0),
                            new PointF(w, w),
                            new PointF(0, height/2),
                            new PointF(-w, w),
                            new PointF(-width/2, 0)};
                        break;
    
                    default:
                        throw new NotImplementedException();
                }
    
                AffineMatrix matrix = new AffineMatrix();
                if (HorizontalMirrored)
                    matrix.ScalePrepend(-1, 1);
                if (VerticalMirrored)
                    matrix.ScalePrepend(1, -1);
    
                PointFAffineTransform.TransformPoints(matrix, points);
    
                return points;
            }
    
    
            /// <summary>
            /// Populates a SerializationInfo with the data needed to serialize the target object.
            /// </summary>
            /// <param name="info">The SerializationInfo to populate with data.</param>
            /// <param name="context">The destination for this serialization.</param>
            [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
            public override void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                base.GetObjectData(info, context);
                info.AddValue("MarkType", (int)MarkType);
            }
    
            
            /// <summary>
            /// Creates a new object that is a copy of the current instance.
            /// </summary>
            /// <returns>A new object that is a copy of this instance.</returns>
            public override object Clone()
            {
                MarkAnnotationData data = new MarkAnnotationData();
                CopyTo(data);
                return data;
            }
    
            /// <summary>
            /// Copies the state of the current object to the target object.
            /// </summary>
            /// <param name="target">Object to copy the state of the current object to.</param>
            public override void CopyTo(AnnotationData target)
            {
                base.CopyTo(target);
    
                if (target is MarkAnnotationData)
                {
                    ((MarkAnnotationData)target).MarkType = MarkType;
                }
            }
    
            #endregion
    
        }
    }
    
    
    Imports System.ComponentModel
    Imports System.Drawing
    Imports System.Drawing.Drawing2D
    Imports System.Runtime.Serialization
    Imports System.Security.Permissions
    
    Imports Vintasoft.Imaging
    Imports Vintasoft.Imaging.Annotation
    Imports Vintasoft.Imaging.Annotation.Rendering
    
    Namespace DemosCommonCode.Annotation
        ''' <summary>
        ''' Class that holds information about the annotation that displays a mark.
        ''' </summary>
        <Serializable> _
        Public Class MarkAnnotationData
            Inherits AnnotationData
    
            #Region "Constructors"
    
            ''' <summary>
            ''' Initializes a new instance of the <see cref="MarkAnnotationData"/> class.
            ''' </summary>
            Public Sub New()
                MyBase.New()
                FillBrush = New AnnotationSolidBrush(Color.Black)
            End Sub
    
            ''' <summary>
            ''' Initializes a new instance of the <see cref="AnnotationData"/> class.
            ''' </summary>
            ''' <param name="info">The SerializationInfo to populate with data.</param>
            ''' <param name="context">The destination for this serialization.</param>
            Public Sub New(info As SerializationInfo, context As StreamingContext)
                MyBase.New(info, context)
                _markType = CType(info.GetValue("MarkType", GetType(Integer)), MarkAnnotationType)
            End Sub
    
    
            ''' <summary>
            ''' Initializes the <see cref="MarkAnnotationData"/> class.
            ''' </summary>
            Shared Sub New()
                ' register renderer form this annotation
                AnnotationRendererFactory.RegisterRendererForAnnotationData(GetType(MarkAnnotationData), GetType(MarkAnnotationRenderer))
            End Sub
    
            #End Region
    
    
    
            #Region "Properties"
    
            Private _markType As MarkAnnotationType = MarkAnnotationType.Tick
            ''' <summary>
            ''' Gets or sets a mark type.
            ''' </summary>
            <Description("The mark type.")> _
            <DefaultValue(MarkAnnotationType.Tick)> _
            Public Property MarkType() As MarkAnnotationType
                Get
                    Return _markType
                End Get
                Set
                    If _markType <> value Then
                        Dim changingArgs As New ObjectPropertyChangingEventArgs("MarkType", _markType, value)
                        If OnPropertyChanging(changingArgs) Then
                            _markType = CType(changingArgs.NewValue, MarkAnnotationType)
                            OnPropertyChanged(changingArgs.ToChangedEventArgs())
                        End If
                    End If
                End Set
            End Property
    
            #End Region
    
    
    
            #Region "Methods"
    
            ''' <summary>
            ''' Returns the bounding box of annotation if annotation will have specified location,
            ''' size and rotation.
            ''' </summary>
            ''' <param name="location">Location, in device-independent pixels (1/96th inch),
            ''' of annotation.</param>
            ''' <param name="size">Size, in device-independent pixels (1/96th inch),
            ''' of annotation</param>
            ''' <param name="rotation">Rotation, in degrees, of annotation.</param>
            ''' <returns>Bounding box of annotation.</returns>
            Public Overrides Function GetBoundingBox(location As PointF, size As SizeF, rotation__1 As Single) As RectangleF
                Dim points As PointF() = GetReferencePointsInContentSpace()
    
                ' rotate
                AnnotationsMath.RotatePointsAt(points, PointF.Empty, Rotation)
                ' scale
                AnnotationsMath.ScalePoints(points, size.Width / Me.Size.Width, size.Height / Me.Size.Height)
                ' translate
                AnnotationsMath.TranslatePoints(points, location.X, location.Y)
    
                Return AnnotationsMath.GetBoundingBox(points)
            End Function
    
    
            ''' <summary>
            ''' Gets an array that contains reference points in content space of this annotation.
            ''' </summary>
            ''' <returns></returns>
            Public Overridable Function GetReferencePointsInContentSpace() As PointF()
                Dim width As Single = Size.Width
                Dim height As Single = Size.Height
                Dim w As Single = Math.Min(width / 10, height / 10)
    
                Dim points As PointF()
                Select Case MarkType
                    Case MarkAnnotationType.Rectangle
                        points = New PointF() {New PointF(-width / 2, -height / 2), New PointF(width / 2, -height / 2), New PointF(width / 2, height / 2), New PointF(-width / 2, height / 2)}
                        Exit Select
    
                    Case MarkAnnotationType.Tick
                        points = New PointF() {New PointF(-width / 2, 0), New PointF(0, height / 4), New PointF(width / 2, -height / 2), New PointF(0, height / 2), New PointF(-width / 2, 0)}
                        Exit Select
    
                    Case MarkAnnotationType.Cross
                        points = New PointF() {New PointF(-width / 2, -w), New PointF(-w, -w), New PointF(-w, -height / 2), New PointF(w, -height / 2), New PointF(w, -w), New PointF(width / 2, -w), _
                            New PointF(width / 2, w), New PointF(w, w), New PointF(w, height / 2), New PointF(-w, height / 2), New PointF(-w, w), New PointF(-width / 2, w)}
                        Exit Select
    
                    Case MarkAnnotationType.Star
                        points = New PointF() {New PointF(-width / 2, 0), New PointF(-w, -w), New PointF(0, -height / 2), New PointF(w, -w), New PointF(width / 2, 0), New PointF(w, w), _
                            New PointF(0, height / 2), New PointF(-w, w), New PointF(-width / 2, 0)}
                        Exit Select
                    Case Else
    
                        Throw New NotImplementedException()
                End Select
    
                Dim matrix As New AffineMatrix()
                If HorizontalMirrored Then
                    matrix.ScalePrepend(-1, 1)
                End If
                If VerticalMirrored Then
                    matrix.ScalePrepend(1, -1)
                End If
    
                PointFAffineTransform.TransformPoints(matrix, points)
    
                Return points
            End Function
    
    
            ''' <summary>
            ''' Populates a SerializationInfo with the data needed to serialize the target object.
            ''' </summary>
            ''' <param name="info">The SerializationInfo to populate with data.</param>
            ''' <param name="context">The destination for this serialization.</param>
            <SecurityPermission(SecurityAction.LinkDemand, Flags := SecurityPermissionFlag.SerializationFormatter)> _
            Public Overrides Sub GetObjectData(info As SerializationInfo, context As StreamingContext)
                MyBase.GetObjectData(info, context)
                info.AddValue("MarkType", CInt(MarkType))
            End Sub
    
    
            ''' <summary>
            ''' Creates a new object that is a copy of the current instance.
            ''' </summary>
            ''' <returns>A new object that is a copy of this instance.</returns>
            Public Overrides Function Clone() As Object
                Dim data As New MarkAnnotationData()
                CopyTo(data)
                Return data
            End Function
    
            ''' <summary>
            ''' Copies the state of the current object to the target object.
            ''' </summary>
            ''' <param name="target">Object to copy the state of the current object to.</param>
            Public Overrides Sub CopyTo(target As AnnotationData)
                MyBase.CopyTo(target)
    
                If TypeOf target Is MarkAnnotationData Then
                    DirectCast(target, MarkAnnotationData).MarkType = MarkType
                End If
            End Sub
    
            #End Region
    
        End Class
    End Namespace
    
    


    MarkAnnotationBuilder class

    MarkAnnotationBuilder class performs visual building of annotation.
    MarkAnnotationBuilder class:

    Here is an example that shows implementation of MarkAnnotationBuilder class:
    using System.Drawing;
    
    using Vintasoft.Imaging.UI.VisualTools.UserInteraction;
    
    namespace DemosCommonCode.Annotation
    {
        /// <summary>
        /// Interaction controller that builds a mark annotation.
        /// </summary>
        public class MarkAnnotationBuilder : InteractionControllerBase<IRectangularInteractiveObject>
        {
    
            #region Contructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="MarkAnnotationBuilder"/> class.
            /// </summary>
            /// <param name="view">The mark annotation.</param>
            public MarkAnnotationBuilder(MarkAnnotationView view)
                : base(view)
            {
                // create an interaction area that can be moved, hovered and clicked
                ImageViewerArea buildArea = new ImageViewerArea(
                    InteractionAreaType.Hover | InteractionAreaType.Movable | InteractionAreaType.Clickable);
                // mark that any mouse button can interact with interaction area
                buildArea.AnyActionMouseButton = true;
                // add the interacton area to a list of interaction areas of the interaction controller
                InteractionAreaList.Add(buildArea);
    
                // set initial size of annotation
                _initialSize = view.Size;
            }
    
            #endregion
    
    
    
            #region Properties
            
            SizeF _initialSize;
            /// <summary>
            /// Gets or sets an annotation initial size.
            /// </summary>
            public SizeF InitialSize
            {
                get
                {
                    return _initialSize;
                }
                set
                {
                    _initialSize = value;
                }
            }
    
            #endregion
    
    
    
            #region Methods
    
            /// <summary>
            /// Performs an interaction between user and interaction area.
            /// </summary>
            /// <param name="args">An interaction event args.</param>
            protected override void PerformInteraction(InteractionEventArgs args)
            {
                // set rectangle of annotation
                InteractiveObject.SetRectangle(
                            args.Location.X - _initialSize.Width / 2, args.Location.Y - _initialSize.Height / 2,
                            args.Location.X + _initialSize.Width / 2, args.Location.Y + _initialSize.Height / 2);
    
                // mark that the user interacted with annotation
                args.InteractionOccured(true);
    
                // if any mouse button is clicked
                if (args.Action == InteractionAreaAction.Click)
                    // finish building of annotation
                    args.InteractionFinished = true;
            }
    
            #endregion
    
        }
    }
    
    
    Imports System.Drawing
    
    Imports Vintasoft.Imaging.UI.VisualTools.UserInteraction
    
    Namespace DemosCommonCode.Annotation
        ''' <summary>
        ''' Interaction controller that builds a mark annotation.
        ''' </summary>
        Public Class MarkAnnotationBuilder
            Inherits InteractionControllerBase(Of IRectangularInteractiveObject)
    
            #Region "Contructors"
    
            ''' <summary>
            ''' Initializes a new instance of the <see cref="MarkAnnotationBuilder"/> class.
            ''' </summary>
            ''' <param name="view">The mark annotation.</param>
            Public Sub New(view As MarkAnnotationView)
                MyBase.New(view)
                ' create an interaction area that can be moved, hovered and clicked
                Dim buildArea As New ImageViewerArea(InteractionAreaType.Hover Or InteractionAreaType.Movable Or InteractionAreaType.Clickable)
                ' mark that any mouse button can interact with interaction area
                buildArea.AnyActionMouseButton = True
                ' add the interacton area to a list of interaction areas of the interaction controller
                InteractionAreaList.Add(buildArea)
    
                ' set initial size of annotation
                _initialSize = view.Size
            End Sub
    
            #End Region
    
    
    
            #Region "Properties"
    
            Private _initialSize As SizeF
            ''' <summary>
            ''' Gets or sets an annotation initial size.
            ''' </summary>
            Public Property InitialSize() As SizeF
                Get
                    Return _initialSize
                End Get
                Set
                    _initialSize = value
                End Set
            End Property
    
            #End Region
    
    
    
            #Region "Methods"
    
            ''' <summary>
            ''' Performs an interaction between user and interaction area.
            ''' </summary>
            ''' <param name="args">An interaction event args.</param>
            Protected Overrides Sub PerformInteraction(args As InteractionEventArgs)
                ' set rectangle of annotation
                InteractiveObject.SetRectangle(args.Location.X - _initialSize.Width / 2, args.Location.Y - _initialSize.Height / 2, args.Location.X + _initialSize.Width / 2, args.Location.Y + _initialSize.Height / 2)
    
                ' mark that the user interacted with annotation
                args.InteractionOccured(True)
    
                ' if any mouse button is clicked
                If args.Action = InteractionAreaAction.Click Then
                    ' finish building of annotation
                    args.InteractionFinished = True
                End If
            End Sub
    
            #End Region
    
        End Class
    End Namespace
    
    


    MarkAnnotationView class

    MarkAnnotationView class:

    Here is an example that shows implementation of MarkAnnotationView class:
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    
    using Vintasoft.Imaging;
    using Vintasoft.Imaging.Annotation;
    using Vintasoft.Imaging.Annotation.UI;
    using Vintasoft.Imaging.Annotation.UI.VisualTools.UserInteraction;
    using Vintasoft.Imaging.Drawing;
    using Vintasoft.Imaging.UI.VisualTools.UserInteraction;
    
    namespace DemosCommonCode.Annotation
    {
        /// <summary>
        /// Class that determines how to display the annotation that displays a mark
        /// and how user can interact with annotation.
        /// </summary>
        public class MarkAnnotationView : AnnotationView, IRectangularInteractiveObject
        {
    
            #region Constructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="MarkAnnotationView"/> class.
            /// </summary>
            /// <param name="annotationData">Object that stores the annotation data.</param>
            public MarkAnnotationView(MarkAnnotationData annotationData)
                : base(annotationData)
            {
                SizeF initialSize = Size;
                if (initialSize.IsEmpty)
                {
                    initialSize = new Size(64, 64);
                    Size = initialSize;
                }
                Builder = new MarkAnnotationBuilder(this);
    
                RectangularAnnotationTransformer transformer = new RectangularAnnotationTransformer(this);
                transformer.HideInteractionPointsWhenMoving = true;
                foreach (InteractionPoint point in transformer.ResizePoints)
                    point.FillColor = Color.FromArgb(100, Color.Red);
                Transformer = transformer;
            }
    
            #endregion
    
    
    
            #region Properties
    
            /// <summary>
            /// Gets or sets a mark type.
            /// </summary>
            [Description("The mark type.")]
            [DefaultValue(MarkAnnotationType.Tick)]
            public MarkAnnotationType MarkType
            {
                get
                { 
                    return MarkAnnoData.MarkType;
                }
                set 
                { 
                    MarkAnnoData.MarkType = value;
                }
            }
    
            /// <summary>
            /// Gets an annotation data.
            /// </summary>
            MarkAnnotationData MarkAnnoData
            {
                get 
                { 
                    return (MarkAnnotationData)Data;
                }
            }
    
            /// <summary>
            /// Gets or sets the rotation angle of interactive object.
            /// </summary>
            double IRectangularInteractiveObject.RotationAngle
            {
                get 
                { 
                    return Rotation; 
                }
                set 
                { 
                    Rotation = (float)value;
                }
            }
    
            #endregion
    
    
    
            #region Methods
    
            #region PUBLIC
    
            /// <summary>
            /// Indicates whether the specified point is contained within the annotation.
            /// </summary>
            /// <param name="point">Point in image space.</param>
            /// <returns><b>true</b> if the specified point is contained within the annotation;
            /// otherwise, <b>false</b>.</returns>
            public override bool IsPointOnFigure(PointF point)
            {
                using (IGraphicsPath path = ((MarkAnnotationRenderer)Renderer).GetAsGraphicsPath(DrawingFactory.Default))
                {
                    path.Transform(GetTransformFromContentToImageSpace());
                    using (IDrawingPen pen = DrawingFactory.Default.CreatePen(Outline))
                    {
                        return path.Contains(point) || path.OutlineContains(point, pen);
                    }
                }
            }
    
            /// <summary>
            /// Creates a new object that is a copy of the current
            /// <see cref="MarkAnnotationView"/> instance.
            /// </summary>
            /// <returns>A new object that is a copy of this <see cref="MarkAnnotationView"/>
            /// instance.</returns>
            public override object Clone()
            {
                return new MarkAnnotationView((MarkAnnotationData)this.Data.Clone());
            }
    
            /// <summary>
            /// Returns an annotation selection as <see cref="GraphicsPath"/> in annotation content space.
            /// </summary>
            public override GraphicsPath GetSelectionAsGraphicsPath()
            {
                GraphicsPath path = new GraphicsPath();
                SizeF size = Size;
                path.AddRectangle(new RectangleF(-size.Width / 2, -size.Height / 2, size.Width, size.Height));
                using (Matrix transform = GdiConverter.Convert(GetTransformFromContentToImageSpace()))
                    path.Transform(transform);
                return path;
            }
    
            #endregion
    
    
            #region PROTECTED
    
            /// <summary>
            /// Sets the properties of interaction controller according to the properties of annotation.
            /// </summary>
            /// <param name="controller">The interaction controller.</param>
            protected override void SetInteractionControllerProperties(IInteractionController controller)
            {
                base.SetInteractionControllerProperties(controller);
    
                RectangularObjectTransformer rectangularTransformer = controller as RectangularObjectTransformer;
                if (rectangularTransformer != null)
                {
                    rectangularTransformer.CanMove = Data.CanMove;
                    rectangularTransformer.CanResize = Data.CanResize;
                    rectangularTransformer.CanRotate = Data.CanRotate;
                    return;
                }
            }
    
            /// <summary>
            /// Raises the <see cref="AnnotationView.StateChanged" /> event.
            /// Invoked when the property of annotation is changed.
            /// </summary>
            /// <param name="e">An <see cref="ObjectPropertyChangedEventArgs" />
            /// that contains the event data.</param>
            protected override void OnDataPropertyChanged(ObjectPropertyChangedEventArgs e)
            {
                base.OnDataPropertyChanged(e);
    
                if (e.PropertyName == "Size")
                {
                    if (Builder is MarkAnnotationBuilder)
                        ((MarkAnnotationBuilder)Builder).InitialSize = (SizeF)e.NewValue;
                }
            }
    
            #endregion
    
    
            #region IRectangularInteractiveObject
    
            /// <summary>
            /// Returns a rectangle of interactive object.
            /// </summary>
            /// <param name="x0">Left-top X coordinate of rectangle.</param>
            /// <param name="y0">Left-top Y coordinate of rectangle.</param>
            /// <param name="x1">Right-bottom X coordinate of rectangle.</param>
            /// <param name="y1">Right-bottom Y coordinate of rectangle.</param>
            void IRectangularInteractiveObject.GetRectangle(
                out double x0,
                out double y0,
                out double x1,
                out double y1)
            {
                PointF location = Location;
                SizeF size = Size;
                x0 = location.X - size.Width / 2;
                y0 = location.Y - size.Height / 2;
                x1 = location.X + size.Width / 2;
                y1 = location.Y + size.Height / 2;
                if (Data.HorizontalMirrored)
                {
                    double tmp = x0;
                    x0 = x1;
                    x1 = tmp;
                }
                if (Data.VerticalMirrored)
                {
                    double tmp = y0;
                    y0 = y1;
                    y1 = tmp;
                }
            }
    
            /// <summary>
            /// Sets a rectangle of interactive object.
            /// </summary>
            /// <param name="x0">Left-top X coordinate of rectangle.</param>
            /// <param name="y0">Left-top Y coordinate of rectangle.</param>
            /// <param name="x1">Right-bottom X coordinate of rectangle.</param>
            /// <param name="y1">Right-bottom Y coordinate of rectangle.</param>
            void IRectangularInteractiveObject.SetRectangle(double x0, double y0, double x1, double y1)
            {
                Size = new SizeF((float)Math.Abs(x0 - x1), (float)Math.Abs(y0 - y1));
                Location = new PointF((float)(x0 + x1) / 2, (float)(y0 + y1) / 2);
    
                HorizontalMirrored = x0 > x1;
                VerticalMirrored = y0 > y1;
    
                if (Data.IsInitializing)
                    OnStateChanged();
            }
    
            #endregion
    
            #endregion
    
        }
    }
    
    
    Imports System.ComponentModel
    Imports System.Drawing
    Imports System.Drawing.Drawing2D
    
    Imports Vintasoft.Imaging
    Imports Vintasoft.Imaging.Annotation
    Imports Vintasoft.Imaging.Annotation.UI
    Imports Vintasoft.Imaging.Annotation.UI.VisualTools.UserInteraction
    Imports Vintasoft.Imaging.Drawing
    Imports Vintasoft.Imaging.UI.VisualTools.UserInteraction
    
    Namespace DemosCommonCode.Annotation
        ''' <summary>
        ''' Class that determines how to display the annotation that displays a mark
        ''' and how user can interact with annotation.
        ''' </summary>
        Public Class MarkAnnotationView
            Inherits AnnotationView
            Implements IRectangularInteractiveObject
    
            #Region "Constructors"
    
            ''' <summary>
            ''' Initializes a new instance of the <see cref="MarkAnnotationView"/> class.
            ''' </summary>
            ''' <param name="annotationData">Object that stores the annotation data.</param>
            Public Sub New(annotationData As MarkAnnotationData)
                MyBase.New(annotationData)
                Dim initialSize As SizeF = Size
                If initialSize.IsEmpty Then
                    initialSize = New Size(64, 64)
                    Size = initialSize
                End If
                Builder = New MarkAnnotationBuilder(Me)
    
                Dim transformer__1 As New RectangularAnnotationTransformer(Me)
                transformer__1.HideInteractionPointsWhenMoving = True
                For Each point As InteractionPoint In transformer__1.ResizePoints
                    point.FillColor = Color.FromArgb(100, Color.Red)
                Next
                Transformer = transformer__1
            End Sub
    
            #End Region
    
    
    
            #Region "Properties"
    
            ''' <summary>
            ''' Gets or sets a mark type.
            ''' </summary>
            <Description("The mark type.")> _
            <DefaultValue(MarkAnnotationType.Tick)> _
            Public Property MarkType() As MarkAnnotationType
                Get
                    Return MarkAnnoData.MarkType
                End Get
                Set
                    MarkAnnoData.MarkType = value
                End Set
            End Property
    
            ''' <summary>
            ''' Gets an annotation data.
            ''' </summary>
            Private ReadOnly Property MarkAnnoData() As MarkAnnotationData
                Get
                    Return DirectCast(Data, MarkAnnotationData)
                End Get
            End Property
    
            ''' <summary>
            ''' Gets or sets the rotation angle of interactive object.
            ''' </summary>
            Private Property IRectangularInteractiveObject_RotationAngle() As Double Implements IRectangularInteractiveObject.RotationAngle
                Get
                    Return Rotation
                End Get
                Set
                    Rotation = CSng(value)
                End Set
            End Property
    
            #End Region
    
    
    
            #Region "Methods"
    
            #Region "PUBLIC"
    
            ''' <summary>
            ''' Indicates whether the specified point is contained within the annotation.
            ''' </summary>
            ''' <param name="point">Point in image space.</param>
            ''' <returns><b>true</b> if the specified point is contained within the annotation;
            ''' otherwise, <b>false</b>.</returns>
            Public Overrides Function IsPointOnFigure(point As PointF) As Boolean
                Using path As IGraphicsPath = DirectCast(Renderer, MarkAnnotationRenderer).GetAsGraphicsPath(DrawingFactory.[Default])
                    path.Transform(GetTransformFromContentToImageSpace())
                    Using pen As IDrawingPen = DrawingFactory.[Default].CreatePen(Outline)
                        Return path.Contains(point) OrElse path.OutlineContains(point, pen)
                    End Using
                End Using
            End Function
    
            ''' <summary>
            ''' Creates a new object that is a copy of the current
            ''' <see cref="MarkAnnotationView"/> instance.
            ''' </summary>
            ''' <returns>A new object that is a copy of this <see cref="MarkAnnotationView"/>
            ''' instance.</returns>
            Public Overrides Function Clone() As Object
                Return New MarkAnnotationView(DirectCast(Me.Data.Clone(), MarkAnnotationData))
            End Function
    
            ''' <summary>
            ''' Returns an annotation selection as <see cref="GraphicsPath"/> in annotation content space.
            ''' </summary>
            Public Overrides Function GetSelectionAsGraphicsPath() As GraphicsPath
                Dim path As New GraphicsPath()
                Dim size__1 As SizeF = Size
                path.AddRectangle(New RectangleF(-size__1.Width / 2, -size__1.Height / 2, size__1.Width, size__1.Height))
                Using transform As Matrix = GdiConverter.Convert(GetTransformFromContentToImageSpace())
                    path.Transform(transform)
                End Using
                Return path
            End Function
    
            #End Region
    
    
            #Region "PROTECTED"
    
            ''' <summary>
            ''' Sets the properties of interaction controller according to the properties of annotation.
            ''' </summary>
            ''' <param name="controller">The interaction controller.</param>
            Protected Overrides Sub SetInteractionControllerProperties(controller As IInteractionController)
                MyBase.SetInteractionControllerProperties(controller)
    
                Dim rectangularTransformer As RectangularObjectTransformer = TryCast(controller, RectangularObjectTransformer)
                If rectangularTransformer IsNot Nothing Then
                    rectangularTransformer.CanMove = Data.CanMove
                    rectangularTransformer.CanResize = Data.CanResize
                    rectangularTransformer.CanRotate = Data.CanRotate
                    Return
                End If
            End Sub
    
            ''' <summary>
            ''' Raises the <see cref="AnnotationView.StateChanged" /> event.
            ''' Invoked when the property of annotation is changed.
            ''' </summary>
            ''' <param name="e">An <see cref="ObjectPropertyChangedEventArgs" />
            ''' that contains the event data.</param>
            Protected Overrides Sub OnDataPropertyChanged(e As ObjectPropertyChangedEventArgs)
                MyBase.OnDataPropertyChanged(e)
    
                If e.PropertyName = "Size" Then
                    If TypeOf Builder Is MarkAnnotationBuilder Then
                        DirectCast(Builder, MarkAnnotationBuilder).InitialSize = CType(e.NewValue, SizeF)
                    End If
                End If
            End Sub
    
            #End Region
    
    
            #Region "IRectangularInteractiveObject"
    
            ''' <summary>
            ''' Returns a rectangle of interactive object.
            ''' </summary>
            ''' <param name="x0">Left-top X coordinate of rectangle.</param>
            ''' <param name="y0">Left-top Y coordinate of rectangle.</param>
            ''' <param name="x1">Right-bottom X coordinate of rectangle.</param>
            ''' <param name="y1">Right-bottom Y coordinate of rectangle.</param>
            Private Sub IRectangularInteractiveObject_GetRectangle(ByRef x0 As Double, ByRef y0 As Double, ByRef x1 As Double, ByRef y1 As Double) Implements IRectangularInteractiveObject.GetRectangle
                Dim location__1 As PointF = Location
                Dim size__2 As SizeF = Size
                x0 = location__1.X - size__2.Width / 2
                y0 = location__1.Y - size__2.Height / 2
                x1 = location__1.X + size__2.Width / 2
                y1 = location__1.Y + size__2.Height / 2
                If Data.HorizontalMirrored Then
                    Dim tmp As Double = x0
                    x0 = x1
                    x1 = tmp
                End If
                If Data.VerticalMirrored Then
                    Dim tmp As Double = y0
                    y0 = y1
                    y1 = tmp
                End If
            End Sub
    
            ''' <summary>
            ''' Sets a rectangle of interactive object.
            ''' </summary>
            ''' <param name="x0">Left-top X coordinate of rectangle.</param>
            ''' <param name="y0">Left-top Y coordinate of rectangle.</param>
            ''' <param name="x1">Right-bottom X coordinate of rectangle.</param>
            ''' <param name="y1">Right-bottom Y coordinate of rectangle.</param>
            Private Sub IRectangularInteractiveObject_SetRectangle(x0 As Double, y0 As Double, x1 As Double, y1 As Double) Implements IRectangularInteractiveObject.SetRectangle
                Size = New SizeF(CSng(Math.Abs(x0 - x1)), CSng(Math.Abs(y0 - y1)))
                Location = New PointF(CSng(x0 + x1) / 2, CSng(y0 + y1) / 2)
    
                HorizontalMirrored = x0 > x1
                VerticalMirrored = y0 > y1
    
                If Data.IsInitializing Then
                    OnStateChanged()
                End If
            End Sub
    
            #End Region
    
            #End Region
    
        End Class
    End Namespace
    
    


    Registering link between MarkAnnotationData and MarkAnnotationView classes

    Link between MarkAnnotationData and MarkAnnotationView classes should be registered before any instance of MarkAnnotationView class will be created. Link between MarkAnnotationData and MarkAnnotationView classes can be registered using the AnnotationViewFactory.RegisterViewForAnnotationData method.


    Examples

    The implementation of mark annotation in C# and VB.NET is a part of AnnotationDemo and WpfAnnotationDemo project code.