0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# - InkRecognizerContainer - Windows FormsとWPFでの利用

Last updated at Posted at 2025-01-21

はじめに

手軽で賢い InkRecognizerContainer 手書き認識は、UWP で提供されています。
この InkRecognizerContainer を、Windows Forms / WPF - .NET Framework 4.8 から利用する手法を題材にしようと思います。

UWP - InkRecognizerContainer と WPF - InkCanvas のペアで利用したことがありますが、これは、正攻法ではないので、別記事にします。
まずは、UWP - InkRecognizerContainer と UWP - InkCanvas のペアで利用する方法を記載します。

今回利用する Windows Runtime(WinRT)組み込みサポートは .NET 5 で削除されたので、.NET Framework のみの情報とします。(.NET 5 以降は別手法です)

破壊的変更: WinRT の組み込みサポートは .NET から削除されています - .NET | Microsoft Learn

InkCanvas での入力検知(InkCanvas.InkPresenter.StrokesCollectedイベント)で、自動的に手書き認識させること、および、認識結果の複数候補を表示させることなどもできます。
簡潔なサンプルコードとするために、ボタンクリックで手書き認識して、第一候補のみ表示する形態とします。

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • WPF - .NET Framework 4.8

前準備

Microsoft.Windows.SDK.Contracts、Microsoft.Toolkit.Forms.UI.Controls、Microsoft.Toolkit.Wpf.UI.Controls の何れかを導入すると、Microsoft.VCRTForwarders.140 が、いつの間にか、参照に追加されてしまうようです。
Microsoft.VCRTForwarders.140 は、プラットフォームが「Any CPU」だと、ビルド時に警告が出力されます。

このため、ソリューション、プロジェクトを作成したら、まず、下記手順で、プラットフォームを「x86」にします。

ソリューション 構成マネージャーを開いて、プラットフォーム「新規作成」を選択します。

setting-01.png

新しいプラットフォーム「x86」を選択して作成します。

setting-02.png

次は、プラットフォーム「編集」を選択します。

setting-03.png

「Any CPU」を削除してください。

setting-04.png

手書き認識

Microsoft が Windows として提供している、手書き認識には、下記が存在します。

  • InkAnalyzer
    • SDK for Vista として提供された System.Windows.Ink.InkAnalyzer(IACore.dll, IALoader.dll, IAWinFX.dll)
  • InkRecognizerContainer
    • Windows 10 で UWP 用に提供された Windows.UI.Input.Inking.InkRecognizerContainer
    • Windows 10 以降で利用可能

InkRecognizerContainer は、InkAnalyzer と比較して、認識率が高く、使い勝手も良くなりました。
Windows 10 以降であれば、当然、InkRecognizerContainer を利用すべきです。

Windows Forms / WPF から、Windows Runtime を呼び出す手法が存在します。
今回は .NET Framework のみを対象としたので、Windows Runtime(WinRT)組み込みサポートで InkRecognizerContainer を利用します。

Windows Runtime(WinRT)組み込みサポート

Windows Forms/WPF から、Windows Runtime(WinRT)組み込みサポートを利用する場合、下記手順が必要となります。

  • Windows SDK 導入
    • Windows SDK | Microsoft Developer から、ターゲットプラットフォームに該当する Windows SDK を入手します。今回は最新のWindows SDK(10.0.26100.0)を利用
  • Windows.winmd 参照追加
    • ソリューションエクスプローラ「参照の追加」で、C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd を追加
  • NuGet パッケージマネージャー コンソールで、NuGet Gallery | Microsoft.Windows.SDK.Contracts を導入
    • PM> NuGet\Install-Package Microsoft.Windows.SDK.Contracts

手書き入力コントール

手書き入力コントールには、下記が存在します。

  • Windows Forms
    • InkEdit
    • InkEdit ととして手書き認識機能あり
  • WPF
    • System.Windows.Controls.InkCanvas
    • InkAnalyzer とペアで利用
  • UWP
    • Windows.UI.Xaml.Controls.InkCanvas
    • InkRecognizerContainer とペアで利用

Windows Forms / WPF から、UWP コントロールを利用する手法として、XAML Islands があります。
InkRecognizerContainer を選択したので、XAML Islands で、UWP - InkCanvas を利用します。

XAML Islands で UWP InkCanvas 利用

Windows Forms と WPF で手順が異なるので、それぞれの手順を以下に記載します。

Windows Forms

NuGet パッケージマネージャー コンソールで、NuGet Gallery | Microsoft.Toolkit.Forms.UI.Controls を導入します。

現時点での最新 Microsoft.Toolkit.Forms.UI.Controls 6.1.2 は、
System.Net.Http >= 4.0.0
という依存関係があります。

System.Net.Http が存在しない場合、System.Net.Http 4.0.0 を取り込みますが、4.0.0 は脆弱性が検知されています。
このため、最新の System.Net.Http を先に導入します。

PM> NuGet\Install-Package System.Net.Http
PM> NuGet\Install-Package Microsoft.Toolkit.Forms.UI.Controls

WPF

NuGet パッケージマネージャー コンソールで、NuGet Gallery | Microsoft.Toolkit.Wpf.UI.Controls を導入します。

PM> NuGet\Install-Package Microsoft.Toolkit.Wpf.UI.Controls

app.manifest

ターゲットを、app.manifest で明示します。

ソリューションエクスプローラ「追加」「新しい項目」で「アプリケーションマニフェストファイル(Windowsのみ)」を選択します。
追加した app.manifest の Windows 10 定義部分のコメントを外して、maxversiontested で Windows SDK バージョンを記載します。

app.manifest
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    <!-- Windows 10 -->
    <maxversiontested Id="10.0.26100.0"/>
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  </application>
</compatibility>

サンプルコード

前述、「Windows runtime (WinRT) 組み込みサポート」「XAML Islands で UWP InkCanvas 利用」「app.manifest」に記載した手順を実施した上でのサンプルコードを記載します。

Windows Forms - .NET Framework 4.8

using Windows.UI.Input.Inking;
using Microsoft.Toolkit.Forms.UI.Controls;
Form1.cs
public partial class Form1 : Form
{
  // 動的配置コントール
  private InkCanvas inkCanvas = null;
  private TextBox txtResult = null;

  public Form1()
  {
    InitializeComponent();

    // フォームサイズ
    this.Width = 440;
    this.Height = 290;

    // 手書きコントロール配置
    inkCanvas = new InkCanvas
      { Width = 400, Height = 200, Top = 10, Left = 10, Name = "inkCanvas" };
    this.Controls.Add(inkCanvas);
    // マウス・ペン・タッチ入力
    inkCanvas.InkPresenter.InputDeviceTypes = 
      Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.CoreInputDeviceTypes.Mouse |
      Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.CoreInputDeviceTypes.Pen |
      Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.CoreInputDeviceTypes.Touch;

    // 認識結果テキストボックス
    txtResult = new TextBox
      { Width = 300, Height = 20, Top = 220, Left = 110, Name = "txtResult" };
    this.Controls.Add(txtResult);

    // 認識ボタン
    var btnAction = new Button
      { Width = 50, Height = 20, Top = 220, Left = 10, Text = "認識", 
        Name = "btnAction" };
    this.Controls.Add(btnAction);
    btnAction.Click += btnAction_Click;

    // 消去ボタン
    var btnClear = new Button
      { Width = 50, Height = 20, Top = 220, Left = 60, Text = "消去", 
        Name = "btnAction" };
    this.Controls.Add(btnClear);
    btnClear.Click += (sender, e) => {
      inkCanvas.InkPresenter.StrokeContainer.Clear();
      txtResult.Text = string.Empty;
    };
  }

  private async void btnAction_Click(object sender, EventArgs e)
  {
    // 手書き認識結果クリア   
    txtResult.Text = string.Empty;

    // 手書きストローク確認
    var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
    if (strokes != null && strokes.Count > 0)
    {
      var irc = new InkRecognizerContainer();
      // 手書き認識 日本語をデフォルトに設定
      var reco = irc.GetRecognizers().FirstOrDefault(r => r.Name.Contains("日本語"));
      if (reco != null)
      {
        irc.SetDefaultRecognizer(reco);
      }

      // 手書き認識結果取得
      IReadOnlyList<InkRecognitionResult> results =
        await irc.RecognizeAsync(
          inkCanvas.InkPresenter.StrokeContainer,
          InkRecognitionTarget.All);
      if (results != null && results.Count > 0)
      {
        // 第一候補のみ取得
        IReadOnlyList<string> candidates = results[0].GetTextCandidates();
        if (candidates != null && candidates.Count > 0)
        {
          txtResult.Text = candidates[0];
        }
      }
    }
  }
}

InkRecognizerContainer.png

WPF - .NET Framework 4.8

xmlns:toolkit として、Microsoft.Toolkit.Wpf.UI.Controls を定義して、Grid で各コントロールをレイアウトします。

MainWindow.xaml
<Window x:Class="MyApp.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:toolkit="clr-namespace:Microsoft.Toolkit.Wpf.UI.Controls;assembly=Microsoft.Toolkit.Wpf.UI.Controls"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:MyApp"
  mc:Ignorable="d"
  Title="MainWindow" Height="280" Width="420">
  <Grid Margin="10,10,0,0">
    <Grid.RowDefinitions>
      <RowDefinition Height="200"/>
      <RowDefinition Height="20"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="50"/>
      <ColumnDefinition Width="50"/>
      <ColumnDefinition Width="300"/>
    </Grid.ColumnDefinitions>
    <toolkit:InkCanvas x:Name="inkCanvas" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"/>
    <Button x:Name="btnAction" Grid.Row="1" Grid.Column="0" Content="認識" Click="btnAction_Click" />
    <Button x:Name="btnClear" Grid.Row="1" Grid.Column="1" Content="消去" Click="btnClear_Click" />
    <TextBlock x:Name="txtResult" Grid.Row="1" Grid.Column="2" />
  </Grid>
</Window>
using Windows.UI.Input.Inking;
using Microsoft.Toolkit.Wpf.UI.Controls;
MainWindow.xaml.cs
public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();

    // マウス・ペン・タッチ入力
    inkCanvas.InkPresenter.InputDeviceTypes =
      Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.CoreInputDeviceTypes.Mouse |
      Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.CoreInputDeviceTypes.Pen |
      Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.CoreInputDeviceTypes.Touch;
  }

  // 手書き認識
  private async void btnAction_Click(object sender, RoutedEventArgs e)
  {
    // 手書き認識結果クリア 
    txtResult.Text = string.Empty;

    // 手書きストローク確認
    var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
    if (strokes != null && strokes.Count > 0)
    {
      var irc = new InkRecognizerContainer();
      // 手書き認識 日本語をデフォルトに設定
      var reco = irc.GetRecognizers().FirstOrDefault(r => r.Name.Contains("日本語"));
      if (reco != null)
      {
        irc.SetDefaultRecognizer(reco);
      }

      // 手書き認識結果取得
      IReadOnlyList<InkRecognitionResult> results =
        await irc.RecognizeAsync(
          inkCanvas.InkPresenter.StrokeContainer,
          InkRecognitionTarget.All);
      if (results != null && results.Count > 0)
      {
        // 第一候補のみ取得
        IReadOnlyList<string> candidates = results[0].GetTextCandidates();
        if (candidates != null && candidates.Count > 0)
        {
          txtResult.Text = candidates[0];
        }
      }
    }
 }

  // クリア
  private void btnClear_Click(object sender, RoutedEventArgs e)
  {
    inkCanvas.InkPresenter.StrokeContainer.Clear();
    txtResult.Text = string.Empty;
  }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?