這一篇介紹 WPF 上的手寫辨識實作,為了符合大家開發的習慣,範例會採用 MVVM 的方式完成。
準備工作
這個實作會用辨識引擎的部分,通常會在 \Program Files\Common Files\Microsoft Shared\ink\ Microsoft.Ink.,請記得將這個組件加入參考。
畫面安排
範例的畫面將 Grid 切割成三個 rows,最頂端放置辨識與清除按鈕、中間安排 ItemsControl 來展示候選字、下方則是使用 InkCanvas 當作手寫板。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="72"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" >
<StackPanel.Resources >
<Style TargetType="Button" >
<Setter Property="Margin" Value="6,12"/>
</Style>
</StackPanel.Resources>
<Button Content="Recognize" Command="{Binding Recognize}" />
<Button Content="Clear" Command="{Binding Clear}" />
</StackPanel>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Candidates}" Background="YellowGreen" >
<ItemsControl.ItemsPanel >
<ItemsPanelTemplate >
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Margin="6" Padding="4" Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<InkCanvas Grid.Row="2"
Background="White"
Strokes="{Binding Strokes}"
UseCustomCursor="True" Cursor="Pen"
DefaultDrawingAttributes="{Binding DrawingAttributes}">
</InkCanvas>
</Grid>
資料繫結
我們會需要繫結到幾個資料,一個是 InkCanvas 的 Strokes (筆觸)、一個是 InkCanvas 的 DefaultDrawingAttributes、最後則是 ItemsControl 所要展示個候選字集合。
private StrokeCollection _strokes;
public StrokeCollection Strokes
{
get => _strokes;
set => SetProperty(ref _strokes, value);
}
private ObservableCollection<string> _candidates;
public ObservableCollection<string> Candidates
{
get => _candidates;
set => SetProperty(ref _candidates, value);
}
private System.Windows.Ink.DrawingAttributes _drawingAttributes;
public System.Windows.Ink.DrawingAttributes DrawingAttributes
{
get => _drawingAttributes;
set => SetProperty(ref _drawingAttributes, value);
}
在 View Model 的建構式先初始化這三個屬性的相關欄位資料。
public MainViewModel()
{
_candidates = new ObservableCollection<string>();
_strokes = new StrokeCollection();
_drawingAttributes = new System.Windows.Ink.DrawingAttributes
{
Color = Colors.DarkBlue,
FitToCurve = true,
IgnorePressure = false,
IsHighlighter = false,
StylusTip = StylusTip.Ellipse,
Width = 18,
Height = 18,
};
}
命令繫結
分為兩個部分,一個是辨識,另一個是清除。清除的程式碼很簡單就是把筆觸和候選字集合清除而已。
public ICommand Clear
{
get
{
return new RelayCommand((x) =>
{
Strokes.Clear();
Candidates.Clear();
});
}
}
辨識的部分就會用到開頭所說明的函式庫,透過 memory stream 將 InkCanvas 的筆觸轉換為 Microsoft.Ink 的筆觸,接著使用 RecognizerContext.Recognize 方法辨識筆觸的內容,最後利用 RecognitionResult.GetAlternatesFromSelection 方法取得候選字就完成了。
public ICommand Recognize
{
get
{
return new RelayCommand((x) =>
{
Candidates.Clear();
if (Strokes.Count == 0) return;
using (var stream = new MemoryStream())
{
Strokes.Save(stream);
var ink = new Ink();
ink.Load(stream.ToArray());
using (var context = new RecognizerContext())
{
context.Strokes = ink.Strokes;
var result = context.Recognize(out RecognitionStatus status);
if (status == RecognitionStatus.NoError)
{
foreach (var candidate in result.GetAlternatesFromSelection())
{
Candidates.Add(candidate.ToString());
}
}
}
}
});
}
}
非常容易就能完成手寫辨識的功能,範例程式在此。