Exemplo de plug-in RealTimeStylus
Este aplicativo demonstra como trabalhar com a classe RealTimeStylus . Para obter uma visão geral detalhada das APIs StylusInput, incluindo a classe RealTimeStylus , consulte Acessando e manipulando entrada de caneta. Para obter informações sobre plug-ins síncronos e assíncronos, consulte Plug-ins e a classe RealTimeStylus.
Visão geral do exemplo
Plug-ins, objetos que implementam a interface IStylusSyncPlugin ou IStylusAsyncPlugin podem ser adicionados a um objeto RealTimeStylus . Este aplicativo de exemplo usa vários tipos de plug-in:
- Plug-in de Filtro de Pacote: modifica pacotes. O plug-in de filtro de pacote neste exemplo modifica as informações do pacote restringindo todos os dados de pacote (x,y) dentro de uma área retangular.
- Plug-in do Renderizador Dinâmico Personalizado: modifica as qualidades de renderização dinâmica. O plug-in de renderização dinâmica personalizada neste exemplo modifica a maneira como a tinta é renderizada desenhando um pequeno círculo em torno de cada ponto (x,y) em um traço.
- Plug-in do Renderizador Dinâmico: modifica as qualidades de renderização dinâmica. Este exemplo demonstra o uso do objeto DynamicRenderer como um plug-in para lidar com a renderização dinâmica de tinta.
- Plug-in do Reconhecimento de Gestos: reconhece gestos de aplicativo. Este exemplo demonstra o uso do objeto GestureRecognizer como um plug-in para reconhecer gestos de aplicativo (ao executar em um sistema com o reconhecedor de gestos da Microsoft presente).
Além disso, este exemplo fornece uma interface do usuário que permite que o usuário adicione, remova e altere a ordem de cada plug-in na coleção. A solução de exemplo contém dois projetos, RealTimeStylusPluginApp e RealTimeStylusPlugins. RealTimeStylusPluginApp contém a interface do usuário para o exemplo. RealTimeStylusPlugins contém as implementações dos plug-ins. O projeto RealTimeStylusPlugins define o namespace RealTimeStylusPlugins, que contém o filtro de pacotes e plug-ins personalizados do renderizador dinâmico. Esse namespace é referenciado pelo projeto RealTimeStylusPluginApp. O projeto RealTimeStylusPlugins usa os namespaces Microsoft.Ink, Microsoft.StylusInput e Microsoft.StylusInput.PluginData .
Para obter uma visão geral dos namespaces Microsoft.StylusInput e Microsoft.StylusInput.PluginData , consulte Arquitetura das APIs StylusInput.
Plug-in de Filtro de Pacote
O plug-in de filtro de pacote é um plug-in síncrono que demonstra a modificação do pacote. Especificamente, ele define um retângulo no formulário. Todos os pacotes desenhados fora da região são renderizados dentro da região. A classe plug-in, PacketFilterPlugin
, registra para notificação de eventos de StylusDown
entrada de caneta , StylusUp
e Packets
. A classe implementa os métodos StylusDown, StylusUp e Packets definidos na classe IStylusSyncPlugin .
O construtor público para PacketFilterPlugin
requer uma estrutura Rectangle . Esse retângulo define a área retangular, em coordenadas de espaço à tinta (.01mm = 1 unidade HIMETRIC), na qual os pacotes serão contidos. O retângulo é mantido em um campo privado, rectangle
.
public class PacketFilterPlugin:IStylusSyncPlugin
{
private System.Drawing.Rectangle rectangle = System.Drawing.Rectangle.Empty;
public PacketFilterPlugin(Rectangle r)
{
rectangle = r;
}
// ...
A PacketFilterPlugin
classe registra para notificações de evento implementando o acessador get para a propriedade DataInterest . Nesse caso, o plug-in tem interesse em responder às StylusDown
notificações , Packets
StylusUp
, e Error
. O exemplo retorna esses valores conforme definido na enumeração DataInterestMask . O método StylusDown é chamado quando a dica de caneta entra em contato com a superfície do digitalizador. O método StylusUp é chamado quando a ponta da caneta sai da superfície do digitalizador. O método Packets é chamado quando o objeto RealTimeStylus recebe pacotes. O método Error é chamado quando o plug-in atual ou um plug-in anterior gera uma exceção.
public DataInterestMask DataInterest
{
get
{
return DataInterestMask.StylusDown |
DataInterestMask.Packets |
DataInterestMask.StylusUp |
DataInterestMask.Error;
}
}
//...
A PacketFilterPlugin
classe lida com a maioria dessas notificações em um método auxiliar, ModifyPacketData
. O ModifyPacketData
método obtém os valores x e y para cada novo pacote da classe PacketsData . Se um dos valores estiver fora do retângulo, o método substituirá o valor pelo ponto mais próximo que ainda está dentro do retângulo. Este é um exemplo de como um plug-in pode substituir os dados do pacote conforme eles são recebidos do fluxo de entrada de caneta.
private void ModifyPacketData(StylusDataBase data)
{
for (int i = 0; i < data.Count ; i += data.PacketPropertyCount)
{
// packet data always has x followed by y followed by the rest
int x = data[i];
int y = data[i+1];
// Constrain points to the input rectangle
x = Math.Max(x, rectangle.Left);
x = Math.Min(x, rectangle.Right);
y = Math.Max(y, rectangle.Top);
y = Math.Min(y, rectangle.Bottom);
// If necessary, modify the x,y packet data
if (x != data[i])
{
data[i] = x;
}
if (y != data[i+1])
{
data[i+1] = y;
}
}
}
Plug-in do Renderizador Dinâmico Personalizado
A CustomDynamicRenderer
classe também implementa a classe IStylusSyncPlugin para receber notificações de entrada por caneta. Em seguida, ele manipula a Packets
notificação para desenhar um pequeno círculo em torno de cada novo ponto de pacote.
A classe contém uma variável Graphics que contém uma referência ao objeto gráfico passado para o construtor de classe. Esse é o objeto gráfico usado para renderização dinâmica.
private Graphics myGraphics;
public CustomDynamicRendererPlugin(Graphics g)
{
myGraphics = g;
}
//...
Quando o plug-in do renderizador dinâmico personalizado recebe uma notificação de Pacotes, ele extrai os dados (x,y) e desenha um pequeno círculo verde ao redor do ponto. Este é um exemplo de renderização personalizada com base no fluxo de entrada de caneta.
public void Packets(RealTimeStylus sender, PacketsData data)
{
for (int i = 0; i < data.Count; i += data.PacketPropertyCount)
{
// Packet data always has x followed by y followed by the rest
Point point = new Point(data[i], data[i+1]);
// Since the packet data is in Ink Space coordinates, we need to convert to Pixels...
point.X = (int)Math.Round((float)point.X * (float)myGraphics.DpiX/2540.0F);
point.Y = (int)Math.Round((float)point.Y * (float)myGraphics.DpiY/2540.0F);
// Draw a circle corresponding to the packet
myGraphics.DrawEllipse(Pens.Green, point.X - 2, point.Y - 2, 4, 4);
}
}
O projeto RealTimeStylusPluginApp
O projeto RealTimeStylusPluginApp demonstra os plug-ins descritos anteriormente, bem como os plug-ins GestureRecognizer e DynamicRenderer . A interface do usuário do projeto consiste em:
- Um Formulário que contém um controle GroupBox usado para definir a área de entrada de tinta.
- Um controle CheckedListBox para listar e selecionar os plug-ins disponíveis.
- Um par de objetos Button para habilitar a reordenação dos plug-ins.
O projeto define uma estrutura, PlugInListItem
, para facilitar o gerenciamento dos plug-ins usados no projeto. A PlugInListItem
estrutura contém o plug-in e uma descrição.
A RealTimeStylusPluginApp
própria classe implementa a classe IStylusAsyncPlugin . Isso é necessário para que a RealTimeStylusPluginApp
classe possa ser notificada quando o plug-in GestureRecognizer adicionar dados de gesto à fila de saída. O aplicativo registra para notificação de CustomStylusDataAdded. Quando dados de gesto são recebidos, RealTimeStylusPluginApp
coloca uma descrição dele na barra de status na parte inferior do formulário.
public void CustomStylusDataAdded(RealTimeStylus sender, CustomStylusData data)
{
if (data.CustomDataId == GestureRecognizer.GestureRecognitionDataGuid)
{
GestureRecognitionData grd = data.Data as GestureRecognitionData;
if (grd != null)
{
if (grd.Count > 0)
{
GestureAlternate ga = grd[0];
sbGesture.Text = "Gesture=" + ga.Id + ", Confidence=" + ga.Confidence;
}
}
}
}
Observação
Na implementação CustomStylusDataAdded , é interessante que você possa identificar os dados de gesto personalizados na fila de saída por GUID (usando o campo GestureRecognitionDataGuid ) ou por tipo (usando o resultado da instrução as). O exemplo usa ambas as técnicas de identificação para fins de demonstração. Qualquer abordagem por si só também é válida.
No manipulador de eventos Load do Formulário, o aplicativo cria instâncias das classes e e CustomDynamicRenderer
as PacketFilter
adiciona à caixa de listagem. Em seguida, o aplicativo tenta criar uma instância da classe GestureRecognizer e, se tiver êxito, adiciona-a à caixa de listagem. Isso falhará se o reconhecedor de gestos não estiver presente no sistema. Em seguida, o aplicativo cria uma instância de um objeto DynamicRenderer e o adiciona à caixa de listagem. Por fim, o aplicativo habilita cada um dos plug-ins e o próprio objeto RealTimeStylus .
Outra coisa importante a observar sobre o exemplo é que, nos métodos auxiliares, o objeto RealTimeStylus é desabilitado primeiro antes que os plug-ins sejam adicionados ou removidos e, em seguida, reabilitados após a conclusão da adição ou remoção.
private void RemoveFromPluginCollection(int index)
{
IStylusSyncPlugin plugin = ((PluginListItem)chklbPlugins.Items[index]).Plugin;
bool rtsEnabled = myRealTimeStylus.Enabled;
myRealTimeStylus.Enabled = false;
myRealTimeStylus.SyncPluginCollection.Remove(plugin);
myRealTimeStylus.Enabled = rtsEnabled;
}
Tópicos relacionados