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:
- O objeto InkCollector .
- Suporte à á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 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.