VintaSoft Imaging .NET SDK 15.1: Документация для .NET разработчика
В этом разделе
Как создать пользовательскую аннотацию метку?
В этом разделе
VintaSoft Annotation .NET Plug-in позволяет создавать пользовательские аннотации с нуля, т.е.: Также плагин позволяет использовать любую стандартную аннотацию в качестве базового класса для пользовательских аннотаций, поскольку архитектура SDK является открытой.


В этой статье описывается, как создать аннотацию-метку с нуля. Для выполнения задачи необходимо создать 1 перечисление и 4 класса:

Перечисление MarkAnnotationType

Перечисление MarkAnnotationType определяет возможные типы меток: прямоугольник, галочка, звездочка или крест.

Вот C#/VB.NET код, который демонстрирует, как реализовать перечисление MarkAnnotationType:
namespace CommonCode.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 CommonCode.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

Класс MarkAnnotationData:
Вот C#/VB.NET код, который демонстрирует, как реализовать класс MarkAnnotationData:
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 CommonCode.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 CommonCode.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

Класс MarkAnnotationBuilder выполняет визуальное построение аннотации.
Класс MarkAnnotationBuilder:
Вот C#/VB.NET код, который демонстрирует, как реализовать класс MarkAnnotationBuilder:
using System.Drawing;

using Vintasoft.Imaging.UI.VisualTools.UserInteraction;

namespace CommonCode.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 CommonCode.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

Класс MarkAnnotationView:
Вот C#/VB.NET код, который демонстрирует, как реализовать класс MarkAnnotationView:
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 CommonCode.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>
        /// <param name="ignoreContainmentCheckDistance">A value indicating whether value of <see cref="AnnotationView.ContainmentCheckDistance"/> property must be ignored.</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, bool ignoreContainmentCheckDistance)
        {
            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 CommonCode.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>
        ''' <param name="ignoreContainmentCheckDistance">A value indicating whether value of <see cref="AnnotationView.ContainmentCheckDistance"/> property must be ignored.</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, ignoreContainmentCheckDistance As Boolean) 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



Регистрация связи между классами MarkAnnotationData и MarkAnnotationView

Связь между MarkAnnotationData и Классы MarkAnnotationView должны быть зарегистрированы до создания любого экземпляра класса MarkAnnotationView. Связь между классами MarkAnnotationData и MarkAnnotationView можно зарегистрировать с помощью метода AnnotationViewFactory.RegisterViewForAnnotationData.

Класс MarkAnnotationRenderer

Класс MarkAnnotationRenderer:
Вот код C#/VB.NET, демонстрирующий реализацию класса MarkAnnotationRenderer:
using System.Drawing;

using Vintasoft.Imaging;

using Vintasoft.Imaging.Annotation;
using Vintasoft.Imaging.Annotation.Rendering;
using Vintasoft.Imaging.Drawing;

namespace CommonCode.Annotation
{
    /// <summary>
    /// Determines how to render the mark annotation.
    /// </summary>
    public class MarkAnnotationRenderer : AnnotationRenderer
    {

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MarkAnnotationRenderer"/> class.
        /// </summary>
        /// <param name="annotationData">Object that stores the annotation data.</param>
        public MarkAnnotationRenderer(MarkAnnotationData annotationData)
            : base(annotationData)
        {
        }

        #endregion



        #region Properties

        /// <summary>
        /// Gets an annotation data.
        /// </summary>
        MarkAnnotationData MarkAnnoData
        {
            get
            {
                return (MarkAnnotationData)Data;
            }
        }

        #endregion



        #region Methods

        /// <summary>
        /// Returns a drawing box of annotation, in the image space.
        /// </summary>
        /// <param name="drawingSurface">The object that provides information about drawing surface.</param>
        /// <returns>Drawing box of annotation, in the image space.</returns>
        public override RectangleF GetDrawingBox(DrawingSurface drawingSurface)
        {
            using (IGraphicsPath path = GetAsGraphicsPath(DrawingFactory.Default))
            {
                using (IDrawingPen pen = DrawingFactory.Default.CreatePen(Data.Outline))
                {
                    // create transformation that allows to get correct bounding box
                    AffineMatrix transform = AffineMatrix.CreateRotation(MarkAnnoData.Rotation);
                    transform.Translate(MarkAnnoData.Location.X, MarkAnnoData.Location.Y);

                    return path.GetBounds(pen, transform);
                }
            }
        }

        /// <summary>
        /// Returns a mark annotation as <see cref="IGraphicsPath"/> in content space.
        /// </summary>
        public virtual IGraphicsPath GetAsGraphicsPath(DrawingFactory drawingFactory)
        {
            IGraphicsPath path = drawingFactory.CreateGraphicsPath();

            PointF[] referencePoints = MarkAnnoData.GetReferencePointsInContentSpace();

            switch (MarkAnnoData.MarkType)
            {
                case MarkAnnotationType.Tick:
                    path.AddCurve(referencePoints);
                    break;
                default:
                    path.AddPolygon(referencePoints);
                    break;
            }

            return path;
        }

        /// <summary>
        /// Renders the annotation on the <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" />
        /// in the coordinate space of annotation.
        /// </summary>
        /// <param name="drawingEngine">The <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" /> to render on.</param>
        /// <param name="drawingSurface">The object that provides information about drawing surface.</param>
        protected override void RenderInContentSpace(DrawingEngine drawingEngine, DrawingSurface drawingSurface)
        {
            using (IGraphicsPath path = GetAsGraphicsPath(drawingEngine.DrawingFactory))
            {
                if (Data.FillBrush != null)
                {
                    using (IDrawingBrush brush = drawingEngine.DrawingFactory.CreateBrush(Data.FillBrush))
                        drawingEngine.FillPath(brush, path);
                }
                if (Data.Border)
                {
                    using (IDrawingPen pen = drawingEngine.DrawingFactory.CreatePen(Data.Outline))
                        drawingEngine.DrawPath(pen, path);
                }
            }
        }

        #endregion

    }
}

Imports System.Drawing

Imports Vintasoft.Imaging

Imports Vintasoft.Imaging.Annotation
Imports Vintasoft.Imaging.Annotation.Rendering
Imports Vintasoft.Imaging.Drawing

Namespace CommonCode.Annotation
    ''' <summary>
    ''' Determines how to render the mark annotation.
    ''' </summary>
    Public Class MarkAnnotationRenderer
        Inherits AnnotationRenderer

        #Region "Constructors"

        ''' <summary>
        ''' Initializes a new instance of the <see cref="MarkAnnotationRenderer"/> class.
        ''' </summary>
        ''' <param name="annotationData">Object that stores the annotation data.</param>
        Public Sub New(annotationData As MarkAnnotationData)
            MyBase.New(annotationData)
        End Sub

        #End Region



        #Region "Properties"

        ''' <summary>
        ''' Gets an annotation data.
        ''' </summary>
        Private ReadOnly Property MarkAnnoData() As MarkAnnotationData
            Get
                Return DirectCast(Data, MarkAnnotationData)
            End Get
        End Property

        #End Region



        #Region "Methods"

        ''' <summary>
        ''' Returns a drawing box of annotation, in the image space.
        ''' </summary>
        ''' <param name="drawingSurface">The object that provides information about drawing surface.</param>
        ''' <returns>Drawing box of annotation, in the image space.</returns>
        Public Overrides Function GetDrawingBox(drawingSurface As DrawingSurface) As RectangleF
            Using path As IGraphicsPath = GetAsGraphicsPath(DrawingFactory.[Default])
                Using pen As IDrawingPen = DrawingFactory.[Default].CreatePen(Data.Outline)
                    ' create transformation that allows to get correct bounding box
                    Dim transform As AffineMatrix = AffineMatrix.CreateRotation(MarkAnnoData.Rotation)
                    transform.Translate(MarkAnnoData.Location.X, MarkAnnoData.Location.Y)

                    Return path.GetBounds(pen, transform)
                End Using
            End Using
        End Function

        ''' <summary>
        ''' Returns a mark annotation as <see cref="IGraphicsPath"/> in content space.
        ''' </summary>
        Public Overridable Function GetAsGraphicsPath(drawingFactory As DrawingFactory) As IGraphicsPath
            Dim path As IGraphicsPath = drawingFactory.CreateGraphicsPath()

            Dim referencePoints As PointF() = MarkAnnoData.GetReferencePointsInContentSpace()

            Select Case MarkAnnoData.MarkType
                Case MarkAnnotationType.Tick
                    path.AddCurve(referencePoints)
                    Exit Select
                Case Else
                    path.AddPolygon(referencePoints)
                    Exit Select
            End Select

            Return path
        End Function

        ''' <summary>
        ''' Renders the annotation on the <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" />
        ''' in the coordinate space of annotation.
        ''' </summary>
        ''' <param name="drawingEngine">The <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" /> to render on.</param>
        ''' <param name="drawingSurface">The object that provides information about drawing surface.</param>
        Protected Overrides Sub RenderInContentSpace(drawingEngine As DrawingEngine, drawingSurface As DrawingSurface)
            Using path As IGraphicsPath = GetAsGraphicsPath(drawingEngine.DrawingFactory)
                If Data.FillBrush IsNot Nothing Then
                    Using brush As IDrawingBrush = drawingEngine.DrawingFactory.CreateBrush(Data.FillBrush)
                        drawingEngine.FillPath(brush, path)
                    End Using
                End If
                If Data.Border Then
                    Using pen As IDrawingPen = drawingEngine.DrawingFactory.CreatePen(Data.Outline)
                        drawingEngine.DrawPath(pen, path)
                    End Using
                End If
            End Using
        End Sub

        #End Region

    End Class
End Namespace






Примеры

Реализация аннотации метки в C# и VB.NET является частью кода проекта AnnotationDemo и WpfAnnotationDemo.