Compartilhar via


Exemplo da área de transferência de impressão

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ços e cole o resultado no objeto de tinta existente.

Os seguintes modos de área de transferência estão disponíveis:

  • Formato serializado à tinta (ISF)
  • Metarquivo
  • EMF (Metarquivo Avançado)
  • 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ços com a ferramenta laço. O usuário pode mover traços 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; ele é implementado aqui para fins ilustrativos.

Os seguintes recursos são usados neste exemplo:

Este exemplo demonstra a renderização da tinta, a cópia dessa tinta e, em seguida, a colagem da tinta em outro aplicativo, como Microsoft Paint.

Coletando tinta e configurando o formulário

Primeiro, faça referência às 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 observados 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;

Por fim, 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 está 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 de menu

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

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

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

O menu Editar habilita os comandos Recortar e Copiar com base no estado de seleção do formulário e habilita o comando Colar com base no conteúdo da área de transferência, determinado usando o 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 Cut usa um método auxiliar para excluir os traços 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 em espaço à tinta e cola os traços da área de transferência para o coletor de tinta. Por fim, 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 dependem do estado do aplicativo para executar a função correta, seja laço ou colocação de 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ços são copiados são listados no menu Formatar e o usuário seleciona o formato para copiar a tinta desta lista. Os tipos de formatos disponíveis incluem ISF (Formato Serializado à Tinta), metarquivo, metafile aprimorado e bitmap. Os formatos de tinta de esboço e tinta de texto são mutuamente exclusivos e dependem da tinta ser 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 Vermelho 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 para o coletor de tinta herda a cor de desenho padrão. Além disso, se algum traço estiver selecionado no momento, a propriedade Color de atributos de desenho de cada traço 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 de aplicativo. Se o modo for MoveInk e um botão do mouse estiver inativo, o manipulador moverá os traços 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 coleção de tinta adequadamente e também define o cursor adequadamente.

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

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

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, será necessário interceptar 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. Em seguida, um método auxiliar é chamado para desenhar o laço no formulário.

Manipulando um novo traço

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

O manipulador cancela o evento Stroke , verifica mais de dois pontos de laço, copia a coleção Points para uma matriz de objetos Point e converte as coordenadas dos pontos na matriz de pixels para espaço à tinta. Em seguida, o manipulador usa o método HitTest do objeto Ink para obter os traços selecionados pelos pontos de laço e atualiza o estado de seleção do formulário. Por fim, o traço que gerou o evento é removido da coleção de traços 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 Formatar para atualizar os formatos a serem colocados na área de transferência e usa o método ClipboardCopy do objeto Ink para copiar os traços 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");
}

Atualizando uma seleção

O método auxiliar SetSelection atualiza os selectedStrokes arquivados e, se a coleção for 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 strokes
  • Converte as coordenadas do retângulo do espaço à tinta em pixels
  • Infla o retângulo para fornecer algum espaço visual entre ele e os traços selecionados
  • Cria identificadores de seleção para a caixa de seleção atual

Por fim, o método SetSelection define a visibilidade dos identificadores de seleção e define a propriedade AutoRedraw do coletor de tinta como FALSE, se os traços 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();

Desenho do Laço

O laço é desenhado como uma série de ponto abertos que seguem o caminho do traço de laço e uma linha de conector tracejada entre as duas extremidades. O evento NewPackets é gerado à medida que o laço está sendo desenhado e o manipulador de eventos passa as informações de traço para o método DrawLasso.

O método auxiliar DrawLasso primeiro remove a linha do conector antigo e itera sobre os pontos no traço. Em seguida, DrawLasso calcula onde colocar os ponto ao longo do traço e os desenha. Por fim, ele desenha uma nova linha de conector.

Fechando o formulário

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