前にWindowsXPでWinTab.NETを使うサンプルを書きました。
今回はそれと同じことをwpfでやってみました。 後者の「ペンタブを縦置き...」と同等のコードです。 環境は、
- Windows7 Home Premium 64bit
- Visual C# 2010 Express
- WinTab.NET 1.6.1
xamlのコードは、
<Window x:Class="WinTabTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="640" Width="360" ResizeMode="NoResize" SourceInitialized="OnSourceInitialized" Activated="OnActivated" Deactivated="OnDeactivated"> <Canvas Name="mainCanvas"> <Ellipse Name="penCursor" Width="16" Height="16" StrokeThickness="2" Stroke="Black"/> </Canvas> </Window>
csのコードは、
// MainWindow.xaml.cs using System; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using WinTabDotnet; namespace WinTabTest { public partial class MainWindow : Window { private const int WINTAB_RANGE = 65536; private WinTabMessenger _wtMessenger; private WinTabContext _wtContext; private int _cx; private int _cy; private int _tx; private int _ty; private int _pressure; private System.Windows.Forms.Message _wndProcMessage; public MainWindow() { InitializeComponent(); _wndProcMessage = new System.Windows.Forms.Message(); } private void OnSourceInitialized(object sender, EventArgs e) { if (!WinTab.LoadWinTab()) { MessageBox.Show("ペンタブレットが見つかりません(WinTab32.dllが見つかりません)。", "WinTabTest"); throw new WinTabException("WinTab.NETの初期化に失敗しました。"); } _wtMessenger = new WinTabMessenger(); _wtContext = new WinTabContext(); _wtMessenger.CursorMove += OnTabletCursorMove; _wtMessenger.NPressureChange += OnTabletNPressureChange; HwndSource source = (HwndSource)HwndSource.FromVisual(this); _wtContext.Open( source.Handle, true, 0, 0, WINTAB_RANGE, WINTAB_RANGE, ContextOption.OFFMODE | ContextOption.SYSTEM, RelativeField.None); source.AddHook(new HwndSourceHook(WndProc)); } private void OnTabletCursorMove(PacketEventArgs e) { _tx = e.pkts.pkX; _ty = e.pkts.pkY; _cx = TabY2ClientX(_ty); _cy = TabX2ClientY(_tx); UpdateTitle(); Canvas.SetLeft(penCursor, _cx - penCursor.Width / 2); Canvas.SetTop(penCursor, _cy - penCursor.Height / 2); } private void OnTabletNPressureChange(PacketEventArgs e) { _pressure = e.pkts.pkNormalPressure; UpdateTitle(); penCursor.Width = 16 + _pressure / 8; penCursor.Height = 16 + _pressure / 8; } private void OnActivated(object sender, System.EventArgs e) { _wtContext.Overlap(true); } private void OnDeactivated(object sender, System.EventArgs e) { _wtContext.Overlap(false); } private int TabX2ClientY(int x) { return (int)((WINTAB_RANGE - x) * mainCanvas.ActualHeight / WINTAB_RANGE); } private int TabY2ClientX(int y) { return (int)(y * mainCanvas.ActualWidth / WINTAB_RANGE); } private void UpdateTitle() { this.Title = "(" + _tx + ", " + _ty + ") → " + "(" + _cx + ", " + _cy + ") " + _pressure; } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { _wndProcMessage.HWnd = hwnd; _wndProcMessage.Msg = msg; _wndProcMessage.WParam = wParam; _wndProcMessage.LParam = lParam; _wndProcMessage.Result = IntPtr.Zero; _wtMessenger.WndProc(ref _wndProcMessage); handled = false; return IntPtr.Zero; } } }
変わったところは、まずはWinTabの初期化のタイミングですね。 wpfではhwndをWindowクラスのコンストラクタで取得できません。 なので、WinTabの初期化はWindow.SourceInitializedイベントで行っています。 hwndを得るには、コードの通りHwndSourceを使います。
WndProcの扱いも変わっています。 overrideではなく、HwndSource.AddHookで登録しなければなりません。 base.WndProcにあたるものは呼ばなくてもいいようです。 handled = falseを設定するのはその代わりなのかな?
WinTabMessenger.WndProcは引数にSystem.Windows.Forms.Message構造体をとります。 これは参照の追加をしなければ使用できません。 参照の追加ダイアログ → .NETタグでSystem.Windows.Formsのコンポーネントを追加しましょう。 ただし、このコンポーネントにはwpfとクラス名がかぶっているものが多いです。 Message構造体以外に使う要素もないでしょうし、usingを書かずにフルパスで構造体を指定しましょう。
このサンプルではコンストラクタでMessage構造体をnewして使いまわしています。 「イベントのたびにnewすると重いかも?」と思ってのコーディングですけど、微々たる重さなので分かりやすいコードにした方がよかったかも?
あと、カーソルをxamlのEllipseに変更しています。 筆圧で大きさが変わるようにもしてるんですが、ちょっとチラつきますね。 ちゃんとしたアプリケーションではこういう方法はダメっぽいです。
ペンタブのカーソル座標はmainCanvasの幅と高さを元に計算しています。 しかし、mainCanvasのサイズはxamlで明記していません。 こういう場合、ActualWidthとActualHeightでサイズを調べます。