此應用程式示範如何使用 RealTimeStylus 類別。 如需 StylusInput API 的詳細概觀,包括 RealTimeStylus 類別,請參閱 存取及操作手寫筆輸入。 如需同步和異步外掛程式的相關信息,請參閱 外掛程式和 RealTimeStylus 類別。
範例概觀
實作 IStylusSyncPlugin 或 IStylusAsyncPlugin 介面的外掛程式物件可以新增至 RealTimeStylus 物件。 這個範例應用程式使用數種類型的外掛程式:
- 封包篩選外掛程式:修改封包。 此範例中的封包篩選外掛程式會透過限制所有 (x, y) 封包數據在一個矩形區域內來修改封包資訊。
- 自定義動態轉譯器外掛程式:修改動態轉譯品質。 此範例中的自訂動態渲染外掛程式會透過在筆劃上的每個(x, y)點周圍繪製小圓圈,來修改墨跡的顯示方式。
- 動態轉譯器外掛程式:修改動態轉譯品質。 此範例示範如何使用 DynamicRenderer 對象作為外掛程式來處理筆跡的動態轉譯。
- 手勢辨識器外掛程式:辨識應用程式手勢。 此範例示範如何使用 GestureRecognizer 對象作為外掛程式來辨識應用程式手勢(在Microsoft筆勢辨識器存在的系統上執行時)。
此外,此範例提供使用者介面,可讓使用者新增、移除和變更集合中每個外掛程式的順序。 此範例解決方案包含兩個專案:RealTimeStylusPluginApp 和 RealTimeStylusPlugins。 RealTimeStylusPluginApp 包含範例的使用者介面。 RealTimeStylusPlugins 包含外掛程式的實作。RealTimeStylusPlugins 專案會定義 RealTimeStylusPlugins 命名空間,其中包含封包篩選和自定義動態轉譯器外掛程式。這個命名空間是由 RealTimeStylusPluginApp 項目所參考。 RealTimeStylusPlugins 專案使用 Microsoft.Ink、Microsoft.StylusInput和 Microsoft.StylusInput.PluginData 命名空間。
如需 Microsoft.StylusInput 和 Microsoft.StylusInput.PluginData 命名空間的概觀,請參閱 stylusInput API 架構。
封包篩選外掛程式
封包篩選外掛程式是示範封包修改的同步外掛程式。 具體而言,它會在表單上定義一個矩形。 在區域外部繪製的任何封包,會在區域內轉譯。 外掛程式類別 PacketFilterPlugin
註冊了 StylusDown
、StylusUp
和 Packets
的手寫筆輸入事件通知。 類別會實作 StylusDown、StylusUp和 PacketsIStylusSyncPlugin 類別上定義的方法。
PacketFilterPlugin
的公用建構函式需要 矩形 結構。 此矩形在筆跡空間座標中定義了一個矩形區域(1 HIMETRIC 單位 = 0.01 毫米),封包將包含在此區域內。 矩形會被存放在私有欄位 rectangle
中。
public class PacketFilterPlugin:IStylusSyncPlugin
{
private System.Drawing.Rectangle rectangle = System.Drawing.Rectangle.Empty;
public PacketFilterPlugin(Rectangle r)
{
rectangle = r;
}
// ...
PacketFilterPlugin
類別會實作 DataInterest 屬性的 get 存取子,以註冊事件通知。 在此情況下,外掛程式有興趣回應 StylusDown
、Packets
、StylusUp
和 Error
通知。 此範例會依據 DataInterestMask 列舉中所定義的內容傳回這些值。 當筆尖接觸數位板表面時,會呼叫 StylusDown 方法。 當筆尖離開數位板表面時,會呼叫 StylusUp 方法。 當 RealTimeStylus 物件接收封包時,會呼叫 Packets 方法。 當目前的外掛程式或先前的外掛程式擲回例外狀況時,會呼叫 Error 方法。
public DataInterestMask DataInterest
{
get
{
return DataInterestMask.StylusDown |
DataInterestMask.Packets |
DataInterestMask.StylusUp |
DataInterestMask.Error;
}
}
//...
PacketFilterPlugin
類別會在輔助方法 ModifyPacketData
中處理大部分的通知。
ModifyPacketData
方法會從 PacketsData 類別取得每個新封包的 x 和 y 值。 如果任一個值在矩形之外,方法會將值取代為仍落在矩形內的最接近點。 這是一個範例,說明插件如何在接收到畫筆輸入數據流中的封包數據時進行取代。
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;
}
}
}
自訂動態渲染器外掛程式
CustomDynamicRenderer
類別也會實作 IStylusSyncPlugin 類別,以接收手寫筆輸入通知。 然後,它會處理 Packets
通知,以在每個新的封包點周圍繪製一個小圓圈。
類別包含 Graphics 變數,該變數會保存傳遞至類別建構函式之圖形對象的參考。 這是用於動態轉譯的圖形物件。
private Graphics myGraphics;
public CustomDynamicRendererPlugin(Graphics g)
{
myGraphics = g;
}
//...
當自定義動態轉譯器外掛程式收到 Packets 通知時,它會擷取 (x,y) 數據,並在點周圍繪製一個小綠色圓圈。 這是以畫筆輸入數據流為基礎的自定義轉譯範例。
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);
}
}
RealTimeStylusPluginApp 專案
RealTimeStylusPluginApp 專案示範先前所述的外掛程式,以及 GestureRecognizer 和 DynamicRenderer 外掛程式。專案的使用者介面包含:
- 表單,包含用來定義筆跡輸入區域的 GroupBox 控件。
- 要列出並選取可用外掛程式的 CheckedListBox 控制項。
- 一組 Button 物件,以重新排序外掛程式。
專案會定義 結構,PlugInListItem
,讓管理專案中所使用的外掛程式變得更容易。
PlugInListItem
結構包含外掛程式和描述。
RealTimeStylusPluginApp
類別本身會實作 IStylusAsyncPlugin 類別。 這是必要的,因此 RealTimeStylusPluginApp
類別可以在 GestureRecognizer 外掛程式將手勢數據新增至輸出佇列時收到通知。 應用程式會註冊通知 CustomStylusDataAdded。 收到手勢數據時,RealTimeStylusPluginApp
將它的描述放在窗體底部的狀態列上。
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;
}
}
}
}
注意
在 CustomStylusDataAdded 的實作中,有趣的是,你可以透過 GUID 使用 GestureRecognitionDataGuid 欄位來識別輸出佇列中的自定義手勢資料,或是使用 as 語句的結果來識別其類型。 此範例會針對示範目的使用這兩種識別技術。 單靠任一種方法也是有效的。
在表單的 Load 事件處理程式中,應用程式會建立 PacketFilter
和 CustomDynamicRenderer
類別的實例,並將其新增至清單框。 然後,應用程式會嘗試建立 GestureRecognizer 類別的實例,如果成功,請將它新增至清單框。 如果系統上沒有手勢辨識器,就會失敗。 接下來,應用程式會具現化 DynamicRenderer 物件,並將其新增至清單框。 最後,應用程式會啟用每個外掛程式和 RealTimeStylus 物件本身。
另一件值得注意的範例是,在協助程式方法中,RealTimeStylus 物件會在新增或移除外掛程式之前先停用,然後在新增或移除完成後重新啟用。
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;
}
相關主題