Partilhar via


Exemplo de área de transferência de tinta

Este programa demonstra como copiar e colar tinta em outro aplicativo. Ele também permite que o usuário copie uma seleção de traçados e cole o resultado no objeto de tinta existente.

Há os seguintes modos de área de transferência disponíveis:

  • Formato serializado de tinta (ISF)
  • Metaficheiro
  • Enhanced Metafile (EMF)
  • Bitmap
  • Tinta de texto
  • Tinta de esboço

Tinta de texto e tinta de esboço são dois tipos de controles de tinta usados como texto ou desenho, respectivamente. É possível colar ISF, tinta de texto e tinta de esboço em tinta existente.

Além da área de transferência, este exemplo também ilustra como selecionar traçados com a ferramenta de laço. O usuário pode mover traçados selecionados e modificar seus atributos de desenho. Essa funcionalidade é um subconjunto da funcionalidade de seleção já fornecida pelo controle de sobreposição de tinta; é aqui implementado para fins ilustrativos.

Os seguintes recursos são usados neste exemplo:

  • O objeto InkCollector.
  • Suporte para área de transferência de tinta.
  • O uso do laço com o método Microsoft.Ink.Ink.HitTest.

Este exemplo demonstra a renderização de tinta, copiando essa tinta e, em seguida, colando a tinta em outro aplicativo, como o Microsoft Paint.

Coletando tinta e configurando o formulário

Primeiro, consulte as interfaces de automação do Tablet PC, que são instaladas com o Microsoft Windows<entity type="reg"/> XP Tablet PC Edition Software Development Kit (SDK).

using Microsoft.Ink;

Em seguida, o formulário declara algumas constantes e campos que são anotados posteriormente neste exemplo.

// Declare constant for the size of the border around selected strokes
private const int SelectedInkWidthIncrease = 105;

// Declare constant for the size of a lasso point
private const int DotSize = 6;

// Declare constant for the spacing between lasso points
private const int DotSpacing = 7;

// Declare constant for the selection rectangle padding
private const int SelectionRectBuffer = 8;

// Declare constant for the lasso hit test percent (specifies how much
// of the stoke must fall within the lasso in order to be selected).
private const float LassoPercent = 50;
...
// Declare the InkCollector object
private InkCollector myInkCollector = null;

// The points in the selection lasso
private ArrayList lassoPoints = null;

// The array of rectangle selection handles
private PictureBox[] selectionHandles;

// The rectangle that bounds the selected strokes
private Rectangle selectionRect = Rectangle.Empty;

// The strokes that have been selected by the lasso
private Strokes selectedStrokes = null;
...
// Declare the colors used in the selection lasso
private Color dotEdgeColor = Color.White;
private Color dotColor = SystemColors.Highlight;
private Color connectorColor = Color.Black;

// Declare the pens used to draw the selection lasso
private Pen connectorPen = null;
private Pen dotEdgePen = null;
private Pen dotPen = null;

Finalmente, no manipulador de eventos Load do formulário, o formulário é inicializado, um objeto InkCollector para o formulário é criado e o coletor de tinta é habilitado.

// Create an ink collector and assign it to this form's window
myInkCollector = new InkCollector(this.Handle);

// Turn the ink collector on
myInkCollector.Enabled = true;

Manipulando eventos do menu

Os manipuladores de eventos de item de menu atualizam principalmente o estado do formulário.

O comando Limpar remove o retângulo de seleção e exclui os traçados do objeto Ink do coletor de tinta.

O comando Exit desativa o coletor de tinta antes de sair do aplicativo.

O menu Editar ativa os comandos Recortar e Copiar conforme o estado de seleção do formulário e ativa o comando Colar com base no conteúdo da área de transferência, determinado pelo uso do método CanPaste do objeto Ink.

Os comandos Recortar e Copiar usam um método auxiliar para copiar tinta para a área de transferência. O comando Recortar usa um método auxiliar para excluir os traçados selecionados.

O comando Colar primeiro verifica o método CanPaste do objeto Ink para ver se o objeto na área de transferência pode ser colado. Em seguida, o comando Colar calcula o canto superior esquerdo da região de colagem, converte as coordenadas de pixels para o plano de tinta e cola os traços da área de transferência no coletor de tinta. Finalmente, a caixa de seleção é atualizada.

if (myInkCollector.Ink.CanPaste())
{
   // Compute the location where the ink should be pasted;
    // this location should be shifted from the origin
    // to account for the width of the selection rectangle's handle.
    Point offset = new Point(leftTopHandle.Width+1,leftTopHandle.Height+1);
    using (Graphics g = CreateGraphics())
    {
        myInkCollector.Renderer.PixelToInkSpace(g, ref offset);
    }
    // Use Ink API to paste the clipboard data into the Ink
    Strokes pastedStrokes = myInkCollector.Ink.ClipboardPaste(offset);

    // If the contents of the clipboard were a valid format 
    // (Ink Serialized Format or Embeddable OLE Object) and at
    // least one stroke was pasted into the ink, use a helper 
    // method to update the stroke selection.  Otherwise,
    // the result is null and this paste becomes a no-op.  
    if (null != pastedStrokes)
    {
        SetSelection(pastedStrokes);
    }
}

Os comandos Selecionar e Tinta atualizam o modo de aplicativo e os atributos de desenho padrão, limpam a seleção atual, atualizam o estado do menu e atualizam o formulário. Outros manipuladores confiam no estado da aplicação para executar a função correta, seja a capturar ou a aplicar tinta. Além disso, o comando Select adiciona os manipuladores de eventos NewPackets e Stroke ao coletor de tinta e o comando Ink remove esses manipuladores de eventos do coletor de tinta.

Os formatos disponíveis na Área de Transferência quando os traçados são copiados estão listados no menu Formatar, e o utilizador seleciona o formato para copiar a tinta a partir dessa lista. Os tipos de formatos disponíveis incluem ISF (Ink Serialized Format), metarquivo, metarquivo aprimorado e bitmap. Os formatos de tinta de esboço e tinta de texto são mutuamente exclusivos e dependem da tinta que está sendo copiada para a área de transferência como um objeto OLE.

O menu Estilo permite que o usuário altere as propriedades de cor e largura da caneta e quaisquer traços selecionados.

Por exemplo, o comando Red define a propriedade Color da propriedade DefaultDrawingAttributes do coletor de tinta como a cor vermelha. Como a propriedade DrawingAttributes do objeto Cursor não foi definida, qualquer nova tinta desenhada no coletor de tinta herda a cor de desenho padrão. Além disso, se algum traçado estiver selecionado no momento, a propriedade Color dos atributos de desenho de cada traçado também será atualizada.

private void SetColor(Color newColor)
{
    myInkCollector.DefaultDrawingAttributes.Color = newColor;

    // In addition to updating the ink collector, also update
    // the drawing attributes of all selected strokes.
    if (HasSelection())
    {
        foreach (Stroke s in selectedStrokes)
        {
            s.DrawingAttributes.Color = newColor;
        }
    }

    Refresh();
}

Manipulando eventos do mouse

O manipulador de eventos MouseMove verifica o modo do aplicativo. Se o modo for MoveInk e um botão do mouse estiver inativo, o manipulador moverá os traçados usando o método Move da coleção Strokes e atualizará a caixa de seleção. Caso contrário, o manipulador verifica se o retângulo de seleção contém o cursor, habilita a recolha de tinta e ajusta o cursor em conformidade.

O manipulador de eventos MouseDown verifica a configuração do cursor. Se o cursor estiver definido como SizeAll, o manipulador define o modo de aplicativo como MoveInk e registra o local do cursor. Caso contrário, se houver uma seleção atual, elimine-a.

O manipulador de eventos MouseUp verifica o modo do aplicativo. Se o modo for MoveInk, o manipulador definirá o modo de aplicativo com base no estado verificado do comando Selecionar.

O evento NewPackets é gerado no modo de seleção quando o coletor de tinta recebe novos dados de pacote. Se o aplicativo estiver no modo de seleção, é necessário intercetar os novos pacotes e usá-los para desenhar o laço de seleção.

A coordenada de cada pacote é convertida em pixels, restrita à área de desenho e adicionada à coleção de pontos do laço. Um método auxiliar é então chamado para desenhar o laço no formulário.

Lidando com um novo AVC

O evento Stroke é gerado no modo de seleção quando um novo traçado é desenhado. Se o aplicativo estiver no modo de seleção, esse traçado corresponde ao laço e é necessário atualizar as informações dos traços selecionados.

O manipulador cancela o evento Stroke, verifica se existem mais de dois pontos de lasso, copia a coleção Points para uma matriz de objetos Point e converte as coordenadas dos pontos na matriz de pixels para o espaço de tinta. Em seguida, o manipulador usa o método HitTest do objeto Ink para obter os traçados selecionados pelos pontos de laço e atualiza o estado de seleção do formulário. Finalmente, o traçado que gerou o evento é removido da coleção de traçados selecionados, a coleção de Pontos de laço é esvaziada e um método auxiliar desenha o retângulo de seleção.

// This stroke corresponds to the lasso - 
// cancel it so that it is not added into the ink
e.Cancel = true;  

Strokes hitStrokes = null;

// If there are enough lasso points, perform a hit test
// to determine which strokes were selected. 
if (lassoPoints.Count > 2)
{

    // Convert the lasso points from pixels to ink space
    Point[] inkLassoPoints = (Point[])lassoPoints.ToArray(typeof(Point));
    using (Graphics g = CreateGraphics())
    {
        myInkCollector.Renderer.PixelToInkSpace(g, ref inkLassoPoints);
    }
    // Perform a hit test on this ink collector's ink to
    // determine which points were selected by the lasso stroke.
    //
    // Note that there is a slight inefficiency here since the
    // lasso stroke is part of the ink and, therefore, part of the
    // hit test - even though we don't need it.   It would have 
    // been more efficient to remove the stroke from the ink before 
    // calling HitTest.  However, it is not good practice to modify 
    // the stroke inside of its own event handler.
    hitStrokes = myInkCollector.Ink.HitTest(inkLassoPoints, LassoPercent);
    hitStrokes.Remove(e.Stroke);
}

// Reset the lasso points
lassoPoints.Clear();
lastDrawnLassoDot = Point.Empty;

// Use helper method to set the selection
SetSelection(hitStrokes);

Copiando tinta para a área de transferência

O método auxiliar CopyInkToClipboard cria um valor InkClipboardFormats, verifica o estado do menu Formato para atualizar os formatos a serem adicionados à área de transferência e utiliza o método ClipboardCopy do objeto Ink para copiar os traçados para a área de transferência.

// Declare the ink clipboard formats to put on the clipboard
InkClipboardFormats formats = new InkClipboardFormats();

// Use selected format menu items to set the clipboard 
// formats
...

// If at least one format was selected, invoke the Ink
// API's ClipboardCopy method.  Note that selectedStrokes
// could be null, but that this is ok - if selectedStrokes
// is null, all of the ink is copied.
if (formats != InkClipboardFormats.None)
{
    myInkCollector.Ink.ClipboardCopy(selectedStrokes,formats,clipboardModes);
}
else
{
    MessageBox.Show("No clipboard formats selected");
}

Atualizar uma seleção

O método auxiliar SetSelection atualiza os selectedStrokes arquivados e, se a coleção estiver NULL ou EMPTY, o retângulo de seleção será definido como o retângulo vazio. Se a coleção Strokes selecionada não estiver vazia, o método SetSelection executará as seguintes etapas:

  • Determina o retângulo delimitador usando o método GetBoundingBox da coleção de traçados
  • Converte as coordenadas do retângulo do espaço de tinta para pixels
  • Infla o retângulo para fornecer algum espaço visual entre ele e os traçados selecionados
  • Cria manípulos de seleção para a caixa de seleção atual

Finalmente, o método SetSelection define a visibilidade das alças de seleção e define a propriedade AutoRedraw do coletor de tinta como FALSE, se os traçados forem selecionados.

// Tracks whether the rectangle that bounds the selected
// strokes should be displayed
bool isSelectionVisible = false;

// Update the selected strokes collection
selectedStrokes = strokes;

// If no strokes are selected, set the selection rectangle
// to empty
if (!HasSelection())
{
    selectionRect = Rectangle.Empty;
}
    // Otherwise, at least one stroke is selected and it is necessary
    // to display the selection rectangle.
else
{
    isSelectionVisible = true;

    // Retrieve the bounding box of the strokes
    selectionRect = selectedStrokes.GetBoundingBox();
    using (Graphics g = CreateGraphics())
    {
        InkSpaceToPixel(g, ref selectionRect);
    }

    // Pad the selection rectangle so that the selected ink 
    // doesn't overlap with the selection rectangle's handles.
    selectionRect.Inflate(SelectionRectBuffer, SelectionRectBuffer);

    // compute the center of the rectangle that bounds the 
    // selected strokes
    int xAvg = (selectionRect.Right+selectionRect.Left)/2;
    int yAvg = (selectionRect.Top+selectionRect.Bottom)/2;

    // Draw the resize handles
    // top left
    SetLocation(selectionHandles[0],selectionRect.Left, selectionRect.Top);
    // top
    SetLocation(selectionHandles[1],xAvg, selectionRect.Top);
    // top right 
    SetLocation(selectionHandles[2],selectionRect.Right, selectionRect.Top);

    // left 
    SetLocation(selectionHandles[3],selectionRect.Left, yAvg);
    // right
    SetLocation(selectionHandles[4],selectionRect.Right, yAvg);

    // bottom left
    SetLocation(selectionHandles[5],selectionRect.Left, selectionRect.Bottom);
    // bottom
    SetLocation(selectionHandles[6],xAvg, selectionRect.Bottom);
    // bottom right
    SetLocation(selectionHandles[7],selectionRect.Right, selectionRect.Bottom);
}

// Set the visibility of each selection handle in the 
// selection rectangle.  If there is no selection, all 
// handles should be hidden.  Otherwise, all handles should
// be visible.
foreach(PictureBox pb in selectionHandles)
{
    pb.Visible = isSelectionVisible;
}

// Turn off autoredrawing if there is a selection - otherwise,
// the selected ink is not displayed as selected.
myInkCollector.AutoRedraw = !isSelectionVisible;

// Since the selection has changed, repaint the screen.
Refresh();

Desenhando o Laço

O laço é desenhado como uma série de pontos vazios que seguem o caminho do traço do laço, e uma linha tracejada conecta as duas extremidades. O evento NewPackets é gerado enquanto o laço está a ser desenhado, e o manipulador de evento passa as informações de traçado para o método DrawLasso.

O método auxiliar DrawLasso primeiro remove a linha de ligação antiga e, em seguida, itera sobre os pontos do traçado. Em seguida, DrawLasso calcula onde colocar os pontos ao longo do traçado e os desenha. Finalmente, desenha uma nova linha de conectores.

Fechando o formulário

O método Dispose do formulário elimina o objeto InkCollector myInkCollector.