This documentation is for the old version. Go to the latest Graphics Mill docs

How to Use Vector Objects

This topic describes how to use the Vector Objects module in more complex applications that the one described in the Quick Start in Vector Objects topic. This text is based on the code of the Multilayer Image Editor sample. The intention of this sample is to provide guidelines on working with the Vector Objects module and to demonstrate how to create a simple custom v-object.

The main part of this sample is the MultiLayerViewer control itself. All objects available for user manipulation—both layers and v-objects— are hosted by this control. The control provides alsmost all features used and demonstrated in the sample. For better understanding of the control architecture, refer to the Basic Concepts of Vector Objects topic.

User Interface Overview

The user interface of this application consists of four main parts:

  • Toolbar contains buttons to add vector objects and turn on/off such v-object actions as rotate and resize.
  • Viewport area contains the image itself.
  • Layers list contains a list of layers and buttons to add, remove, rename and swap layers.
  • Objects list is located under the Layers list contains a list of vector objects of the current layer. It also has buttons to remove, rename and swap objects.

Multilayer Image Editor screenshot
Fig. 1. Multilayer Image Editor screenshot

V-objects Usage

In this sample, the following operations with v-objects are shown.

Adding and Removing V-objects

When the user clicks one of the Add buttons on the toolbar, the AddNewObject method is called. This method takes as an argument one of XXXCreateDesigner objects—depending on the type of the v-object being created. For example, if the user clicks the Add New Image button, ImageVObjectCreateDesigner will be passed to the AddNewObject method. XXXCreateDesigner objects are used in order to let the user select the part of the viewer area where a new v-object will be placed. Here is an implementation of the AddNewObject method:

Visual Basic
Private Sub AddNewObject(ByVal designer As Aurigma.GraphicsMill.WinControls.IDesigner)
    If _multiLayerViewer.CurrentLayer Is Nothing Then
        
        'Do not allow adding objects, if no layer is selected
        ClearAddObjectToggle()
        Return
    End If
    If _multiLayerViewer.Layers(_layersCheckedListBox.SelectedIndex).Visible = _
        False Then
    
        'Do not allow adding objects, if the selected layer is hidden
        ClearAddObjectToggle()
        Return
    End If
    If _multiLayerViewer.Layers.Count < 1 Then
    
        'Do not allow adding objects, if no layers exist
        ClearAddObjectToggle()
        Return
    End If

    'If the desginer needs to load a bitmap, open the file choose dialog
    If TypeOf designer Is Aurigma.GraphicsMill.WinControls.ImageVObjectCreateDesigner Then
        Dim imageDesigner As Aurigma.GraphicsMill.WinControls.ImageVObjectCreateDesigner = _
            CType(designer, Aurigma.GraphicsMill.WinControls.ImageVObjectCreateDesigner)
        If _imageOpenFileDialog.ShowDialog = DialogResult.OK Then
            Try
                imageDesigner.Bitmap = New Aurigma.GraphicsMill.Bitmap( _
                    _imageOpenFileDialog.FileName)
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Load Image Error", _
                    MessageBoxButtons.OK, MessageBoxIcon.Error)
                _addImageToolBarButton.Pushed = False
                Return
            End Try
        End If
        ClearAddObjectToggle(Nothing)
    End If

    'Set the current designer
    _multiLayerViewer.CurrentDesigner = designer
End Sub
C#
private void AddNewObject(Aurigma.GraphicsMill.WinControls.IDesigner designer)
{
    if (_multiLayerViewer.CurrentLayer == null)
    {
        //Do not allow adding objects, if no layer is selected
        ClearAddObjectToggle();
        return;
    }
    if (_multiLayerViewer.Layers[_layersCheckedListBox.SelectedIndex].Visible ==
        false)
    {
        //Do not allow adding objects, if the selected layer is hidden
        ClearAddObjectToggle();
        return;
    }
    if (_multiLayerViewer.Layers.Count < 1)
    {
        //Do not allow adding objects, if no layers exist
        ClearAddObjectToggle();
        return;
    }
    
    //If the desginer needs to load a bitmap, open the file choose dialog
    if (designer is Aurigma.GraphicsMill.WinControls.ImageVObjectCreateDesigner)
    {
        Aurigma.GraphicsMill.WinControls.ImageVObjectCreateDesigner imageDesigner =
            (Aurigma.GraphicsMill.WinControls.ImageVObjectCreateDesigner) designer;
        if (_imageOpenFileDialog.ShowDialog() == DialogResult.OK)
        {
            try
            {
                imageDesigner.Bitmap = new Aurigma.GraphicsMill.Bitmap(
                    _imageOpenFileDialog.FileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Load Image Error",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
                _addImageToolBarButton.Pushed = false;
                return;
            }
        }
        ClearAddObjectToggle(null);
    }
    
    //Set the current designer
    _multiLayerViewer.CurrentDesigner = designer;
}

When the user selects objects to delete in the Objects list and the clicks the Remove Objects button, the RemoveObject method is called for each selected object. This method, in turn, simply calls the VObjectCollection.Remove method:

Visual Basic
Private Sub RemoveObject(ByVal obj As Aurigma.GraphicsMill.WinControls.IVObject)
    _multiLayerViewer.CurrentLayer.VObjects.Remove(obj)
End Sub
C#
private void RemoveObject(Aurigma.GraphicsMill.WinControls.IVObject obj)
{
    _multiLayerViewer.CurrentLayer.VObjects.Remove(obj);
}

Changing V-object Properties

In this demo application the user can change settings (if available) of the selected v-object, for example: select a pen to outline the object and a brush to fill it. Changing these settings itself is not really interesting as you simply need to assign required values to the Pen and Brush properties of the selected v-object. The interesting part is that the dialog from which the user chooses required colors is opened through a context menu of the v-object.

This context menu is a standard Windows Forms control. Work with it as with any other context menu. The only particular thing you need to do is to attach the menu to the current designer as this designer represents the currently selected object. Below is a method that sets the appropriate menu based on the type of the selected object. This method should be called when the current designer is somehow changed:

Visual Basic
Private Sub UpdateVObjectContextMenu(ByVal obj As Aurigma.GraphicsMill.WinControls.VObject)
    Dim designer As Aurigma.GraphicsMill.WinControls.GenericVObjectEditDesigner = _
    CType(obj.Designer, Aurigma.GraphicsMill.WinControls.GenericVObjectEditDesigner)

    If TypeOf obj Is Aurigma.GraphicsMill.WinControls.RectangleVObject Then
        designer.ContextMenu = _penBrushSettingsContextMenu
    ElseIf TypeOf obj Is Aurigma.GraphicsMill.WinControls.EllipseVObject Then
        designer.ContextMenu = _penBrushSettingsContextMenu
    ElseIf TypeOf obj Is Aurigma.GraphicsMill.WinControls.LineVObject Then
        designer.ContextMenu = _penSettingsContextMenu
    ElseIf TypeOf obj Is Aurigma.GraphicsMill.WinControls.PolylineVObject Then
        designer.ContextMenu = _penBrushSettingsContextMenu
    ElseIf TypeOf obj Is Aurigma.GraphicsMill.WinControls.TextVObject Then
        designer.ContextMenu = _textSettingsContextMenu
    End If
End Sub
C#
private void UpdateVObjectContextMenu(Aurigma.GraphicsMill.WinControls.VObject obj)
{
    Aurigma.GraphicsMill.WinControls.GenericVObjectEditDesigner designer =
        (Aurigma.GraphicsMill.WinControls.GenericVObjectEditDesigner)
        obj.Designer;
    
    if (obj is Aurigma.GraphicsMill.WinControls.RectangleVObject)
    {
        designer.ContextMenu = _penBrushSettingsContextMenu;
    }
    else if (obj is Aurigma.GraphicsMill.WinControls.EllipseVObject)
    {
        designer.ContextMenu = _penBrushSettingsContextMenu;
    }
    else if (obj is Aurigma.GraphicsMill.WinControls.LineVObject)
    {
        designer.ContextMenu = _penSettingsContextMenu;
    }
    else if (obj is Aurigma.GraphicsMill.WinControls.PolylineVObject)
    {
        designer.ContextMenu = _penBrushSettingsContextMenu;
    }
    else if (obj is Aurigma.GraphicsMill.WinControls.TextVObject)
    {
        designer.ContextMenu = _textSettingsContextMenu;
    }
}

Actions

The user can also transform objects—rotate, skew and resize—by dragging the corresponding control points. In terms of Vector Objects such transformations made by the user are called actions. In this demo the user can also turn these actions on and off by clicking buttons on the toolbar. When the user clicks a toggle button that enables or disables some action, action grips (they are the corresponding control points) either become visible or hidden.

Different v-objects can support different actions. If you need to know which actions are supported by a particular v-object, check its VObject.SupportedActions collection. Each action in this collection is represented by a constant value. Using this value you can create a required action object, a descendant of the VObjectAction class. This newly created object will have the Enabled property. When this property is set to false object designers will not allow applying the corresponding action.

In Multilayer Image Editor, actions are enabled of disabled depending on the state of toggle buttons:

Visual Basic
If Not _multiLayerViewer.CurrentDesigner Is Nothing AndAlso _
    _multiLayerViewer.CurrentDesigner.VObjects.Length > 0 Then
    For Each obj As Aurigma.GraphicsMill.WinControls.IControlPointsProvider In _
        _multiLayerViewer.CurrentDesigner.VObjects

        If obj.SupportedActions.Contains(Aurigma.GraphicsMill.WinControls.VObjectAction.Resize) Then
        
            'Enable or disable resize
            obj.SupportedActions(Aurigma.GraphicsMill.WinControls.VObjectAction.Resize).Enabled = _
                _resizeGripsToolBarButton.Pushed
        End If

        If obj.SupportedActions.Contains(Aurigma.GraphicsMill.WinControls.VObjectAction.Rotate) Then
            
            'Enable or disable rotate
            obj.SupportedActions(Aurigma.GraphicsMill.WinControls.VObjectAction.Rotate).Enabled = _
                _rotateGripsToolBarButton.Pushed
        End If

        If obj.SupportedActions.Contains(Aurigma.GraphicsMill.WinControls.VObjectAction.Skew) Then
            
            'Enable or disable skew
            obj.SupportedActions(Aurigma.GraphicsMill.WinControls.VObjectAction.Skew).Enabled = _
                _skewGripsToolBarButton.Pushed
        End If
    Next
End If
C#
if (_multiLayerViewer.CurrentDesigner != null &&
    _multiLayerViewer.CurrentDesigner.VObjects.Length > 0)
{
    foreach (Aurigma.GraphicsMill.WinControls.IControlPointsProvider obj in
        _multiLayerViewer.CurrentDesigner.VObjects)
    {
        if (obj.SupportedActions.Contains(
            Aurigma.GraphicsMill.WinControls.VObjectAction.Resize))
        {
            //Enable or disable resize
            obj.SupportedActions[Aurigma.GraphicsMill.WinControls.VObjectAction.Resize].Enabled =
                _resizeGripsToolBarButton.Pushed;
        }
        
        if (obj.SupportedActions.Contains(
        Aurigma.GraphicsMill.WinControls.VObjectAction.Rotate))
        {
            //Enable or disable rotate
            obj.SupportedActions[Aurigma.GraphicsMill.WinControls.VObjectAction.Rotate].Enabled =
                _rotateGripsToolBarButton.Pushed;
        }
        
        if (obj.SupportedActions.Contains(Aurigma.GraphicsMill.WinControls.VObjectAction.Skew))
        {
            //Enable or disable skew
            obj.SupportedActions[Aurigma.GraphicsMill.WinControls.VObjectAction.Skew].Enabled =
                _skewGripsToolBarButton.Pushed;
        }
    }
}

Layers Usage

Another feature of the MultiLayerViewer control is work with layers. That is probably one of the simplest things. As described in the Basic Concepts of Vector Objects topic, layers are organized in an array associated with the control. This array can be accessed using the IVObjectHost.Layers property. So, if you want to add, remove or reorganize layers, just add, remove or reorganize the items of this array. Here is how you can delete a layer:

Visual Basic
Private Sub RemoveLayer(ByVal layer As Aurigma.GraphicsMill.WinControls.Layer)
    _multiLayerViewer.Layers.Remove(layer)
    If _layersCheckedListBox.Items.Count < 1 Then
        _layerRemoveToolBarButton.Enabled = False
        _layerRenameToolBarButton.Enabled = False
        _layerRemoveMenuItem.Enabled = False
        _layerRenameMenuItem.Enabled = False
    End If
End Sub
C#
private void RemoveLayer(Aurigma.GraphicsMill.WinControls.Layer layer)
{
    _multiLayerViewer.Layers.Remove(layer);
    if (_layersCheckedListBox.Items.Count < 1)
    {
        _layerRemoveToolBarButton.Enabled = false;
        _layerRenameToolBarButton.Enabled = false;
        _layerRemoveMenuItem.Enabled = false;
        _layerRenameMenuItem.Enabled = false;
    }
}

If you want to lock, rename the layer or toggle its visibility, set the needed value of the Locked, Visible or Name property of the selected layer, as in the following example:

Visual Basic
Private Sub RenameLayer(ByVal layer As Aurigma.GraphicsMill.WinControls.Layer)
    _changeNameDialog.Value = layer.Name
    If _changeNameDialog.ShowDialog = DialogResult.OK Then
        layer.Name = _changeNameDialog.Value
        UpdateLayersListBox()
    End If
End Sub
C#
private void RenameLayer(Aurigma.GraphicsMill.WinControls.Layer layer)
{
    _changeNameDialog.Value = layer.Name;
    if (_changeNameDialog.ShowDialog() == DialogResult.OK)
    {
        layer.Name = _changeNameDialog.Value;
        UpdateLayersListBox();
    }
}

Undo and Redo

One more convenient and easy-to-use feature of the MultiLayerViewer control is support for undoing and redoing changes. So, what should happen when the user clicks Undo or Redo in the Edit menu? Well, you should simply call the MultiLayerViewer.Undo or MultiLayerViewer.Redo method and update the user interface:

Visual Basic
Private Sub Undo()
    _multiLayerViewer.Undo()
    If Not _multiLayerViewer.CanUndo Then
        _undoToolBarButton.Enabled = False
        _undoMenuItem.Enabled = False
    End If
    _redoToolBarButton.Enabled = True
    _redoMenuItem.Enabled = True
End Sub

Private Sub Redo()
    _multiLayerViewer.Redo()
    If Not _multiLayerViewer.CanRedo Then
        _redoToolBarButton.Enabled = False
        _redoMenuItem.Enabled = False
    End If
    _undoToolBarButton.Enabled = True
    _undoMenuItem.Enabled = True
End Sub
C#
private void Undo()
{
    _multiLayerViewer.Undo();
    if (! _multiLayerViewer.CanUndo)
    {
        _undoToolBarButton.Enabled = false;
        _undoMenuItem.Enabled = false;
    }
    _redoToolBarButton.Enabled = true;
    _redoMenuItem.Enabled = true;
}

private void Redo()
{
    _multiLayerViewer.Redo();
    if (! _multiLayerViewer.CanRedo)
    {
        _redoToolBarButton.Enabled = false;
        _redoMenuItem.Enabled = false;
    }
    _undoToolBarButton.Enabled = true;
    _undoMenuItem.Enabled = true;
}

All the rest is already done for you.

Opening and Saving Files

Saving and Opening Layered Images

This application demonstrates how one can save a layered drawing in a file and later open it for additional editing.

When the user clicks Save in the File menu, they can save the their drawing in a data file (DAT). When the image is saved in a DAT file, it is actually serialized. That is, its data are written in some format that can be read later. To save the image data, you need to serialize the MultiLayerViewer properties and then all the layers associated with it. When the layers are saved this way, it also serializes all contained objects. Here is how you can save a layered drawing to a DAT file:

Visual Basic
Private Sub SaveTemplateToFile(ByVal fileName As String)
    Dim file As New System.IO.FileStream(fileName, System.IO.FileMode.Create, _
        System.IO.FileAccess.Write)
    Dim formatter As New _
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    formatter.Serialize(file, _multiLayerViewer.WorkspaceWidth)
    formatter.Serialize(file, _multiLayerViewer.WorkspaceHeight)
    _multiLayerViewer.Layers.Serialize(file)
    file.Close()
End Sub
C#
private void SaveTemplateToFile(string fileName)
{
    System.IO.FileStream file = new System.IO.FileStream(fileName,
        System.IO.FileMode.Create, System.IO.FileAccess.Write);
    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
        new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    formatter.Serialize(file, _multiLayerViewer.WorkspaceWidth);
    formatter.Serialize(file, _multiLayerViewer.WorkspaceHeight);
    
    _multiLayerViewer.Layers.Serialize(file);
    file.Close();
}
Important

Remember that binary format used for serialization is highly dependent on the build version, as the API may change from version to version, and properties may be added or removed.

If you need to later open a DAT file with the image, deserialize the MultiLayerViewer properties and the layers:

Visual Basic
Private Sub OpenTemplateFromFile(ByVal fileName As String)
    Dim file As System.IO.FileStream = Nothing
    Try
        file = New System.IO.FileStream(fileName, System.IO.FileMode.Open, _
            System.IO.FileAccess.Read)

        Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
        _multiLayerViewer.WorkspaceWidth = CType(formatter.Deserialize(file), _
            System.Int32)
        _multiLayerViewer.WorkspaceHeight = CType(formatter.Deserialize(file), _
            System.Int32)

        _multiLayerViewer.Layers.Deserialize(file)
        _multiLayerViewer.ClearHistory()
        _undoMenuItem.Enabled = False
        _redoMenuItem.Enabled = False
        _undoToolBarButton.Enabled = False
        _redoToolBarButton.Enabled = False
    Catch ex As Exception
        System.Windows.Forms.MessageBox.Show("Cannot load specified file.", _
            "Opening file error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    Finally
        If Not file Is Nothing Then file.Close()
    End Try

    '...Update the rest of GUI...
End Sub
C#
private void OpenTemplateFromFile(string fileName)
{
    System.IO.FileStream file = null;
    try
    {
        file = new System.IO.FileStream(fileName, System.IO.FileMode.Open,
            System.IO.FileAccess.Read);
        
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
            new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        _multiLayerViewer.WorkspaceWidth = (int)formatter.Deserialize(file);
        _multiLayerViewer.WorkspaceHeight = (int)formatter.Deserialize(file);
        
        _multiLayerViewer.Layers.Deserialize(file);
        _multiLayerViewer.ClearHistory();
        _undoMenuItem.Enabled = false;
        _redoMenuItem.Enabled = false;
        _undoToolBarButton.Enabled = false;
        _redoToolBarButton.Enabled = false;
    }
    catch (Exception)
    {
        System.Windows.Forms.MessageBox.Show("Cannot load specified file.",
            "Opening file error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    finally
    {
        if (file != null)
        {
            file.Close();
        }
    }
    
    //...Update the rest of GUI...
}

Rendering a Bitmap

The user can also save the image as a bitmap in one of the graphic formats supported by Graphics Mill for .NET. To implement this feature, you will only need to call the MultiLayerViewer.RenderWorkspace method and save the image to a bitmap:

Visual Basic
Private Sub Render()
    If _resultSaveFileDialog.ShowDialog = DialogResult.OK Then
        _renderForm.WidthValue = _multiLayerViewer.WorkspaceWidth
        If _renderForm.ShowDialog = DialogResult.OK Then
            Try
                Dim bitmap As Aurigma.GraphicsMill.Bitmap = _
                    _multiLayerViewer.RenderWorkspace()
                bitmap.Save(_resultSaveFileDialog.FileName)
                bitmap.Dispose()
            Catch ex As ArgumentOutOfRangeException
                MessageBox.Show("Width and height of the image should be above zero.", _
                    "Image Save Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Image Save Error", _
                    MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End If
    End If
End Sub
C#
private void Render()
{
    if (_resultSaveFileDialog.ShowDialog() == DialogResult.OK)
    {
        _renderForm.WidthValue = _multiLayerViewer.WorkspaceWidth;
        if (_renderForm.ShowDialog() == DialogResult.OK)
        {
            try
            {
                Aurigma.GraphicsMill.Bitmap bitmap =
                    _multiLayerViewer.RenderWorkspace();
                bitmap.Save(_resultSaveFileDialog.FileName);
                bitmap.Dispose();
            }
            catch (ArgumentOutOfRangeException)
            {
                MessageBox.Show("Width and height of the image should be above zero.",
                    "Image Save Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Image Save Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

Creating a Custom V-object

As an example of a custom v-object implementation, this demo contains an object that fills the background of the layer with some color. Each custom object should implement the IVObject interface that provides basic required members. If you want to let the user transform your v-object, you should also implement the IControlPointsProvider interface.The easiest way to meet these conditions is to inherit from the VObject class and to override the members that need modifications.

The sample custom v-object, SolidBackgroundVObject, overrides only several methods from the IVObject interface, as rotate, skew or resize (for which the IControlPointsProvider is intended) are not applicable for this kind of objects. This custom v-object also implements the ISerialization interface which allows saving this object along with other layer objects. A full listing of this v-object implementation can be found in the SolidBackgroundVObject.cs file of the Multi-layer image editor demo.

Also, an example implementation of the corresponding designer, SolidBackgroundVObjectCreateDesigner, is provided in the SolidBackgroundVObjectCreateDesigner.cs file of the same demo. This designer is used to make a new v-object, that fills the whole viewer area, and to put it behind all other objects in a layer.

See Also

Reference

Manual