2019 5 13 WPF 從觸控訊息轉觸控事件

2022-01-12 22:27:55 字數 4176 閱讀 4035

title

author

date

createtime

categories

wpf 從觸控訊息轉觸控事件

lindexi

2019-05-13 09:43:48 +0800

2019-05-12 15:12:31 +0800

wpf在 wpf 程式可能因為一些坑讓程式觸控失效,如果此時還可以收到系統的觸控訊息,那麼可以通過從觸控訊息轉觸控事件解決程式觸控失效但不適合所有觸控失效程式

在 wpf 的觸控**寫的不是很清真,特別是觸控到事件可能出現一些坑,如wpf 在觸控執行緒等待主線程視窗關閉會讓主線程和觸控執行緒相互等待 和 wpf 插拔觸控裝置觸控失效 等,有時候在開機的過程,如果啟動快了,觸控裝置還沒準備好,剛好在 wpf 初始化的過程 usb 觸控裝置才準備好,此時 wpf 也會觸控失效

在希沃的裝置通過判斷使用者的開機啟動時間,如果啟動時間過短,那麼就需要多判斷是不是 usb 裝置還沒準備好,如果 usb 還沒準備好,那麼通過一些黑科技告訴使用者重新啟動。因為在希沃的裝置上主要是觸控螢幕,使用者不會有滑鼠,如果出現了初始化的過程剛好就是 usb 準備好,那麼這個程式將收不到任何觸控事件

在程式啟動的時候,可以通過獲得觸控精度和觸控點判斷當前是否存在觸控裝置,如果不存在觸控裝置同時判斷是在希沃的裝置上執行,那麼就是觸控失效了。但是還可以收到系統的觸控訊息,可以通過本文的黑科技收到觸控

在 wpf 的框架,觸控是從 penimc 裡面獲取的,如果通過自己建立乙個模擬的觸控裝置,請看 wpf 模擬觸控裝置 也可以做到模擬乙個觸控。在預設的 wpf 程式是收不到系統的觸控訊息,需要禁用實時觸控才可以收到觸控訊息,在 win7 和之後都可以從系統收到wm_touch訊息,通過這個訊息可以解析當前的觸控點和觸控面積,通過這兩個值可以用來模擬觸控走原有的 wpf 觸控

在使用wm_touch訊息需要用到一些本地的方法,先定義乙個 nativemethods 類,用來放本地方法

internal

static

class

nativemethods

在開啟觸控訊息之前需要在 window 的 sourceinitialized 事件觸發之後才能呼叫

建立 messagetouchdevice 繼承 touchdevice 從 wpf 模擬觸控裝置 可以知道這個類可以用來模擬觸控,在這個類新增乙個靜態的方法 usemessagetouch 用它傳入視窗

public

mainwindow()

private

void

mainwindow_sourceinitialized(object

sender, eventargs

e)

在 usemessagetouch 方法需要先通過禁用實時觸控然後使用鉤子拿到訊息

///

>

/// 使用訊息觸控

/// 注意 開啟了訊息觸控之後,原有的 wpf 觸控將會無法再次使用

///summary

>

public

static

void

usemessagetouch(window

window)

);}

定義 wndproc 靜態方法用來收到訊息,通過訊息 msg 可以判斷當前是否觸控訊息,然後通過 wparam 計算出當前的觸控收集到的次數

因為 windows 訊息觸發比較慢,也就是沒有 penimc 拿到觸控點那麼快,在一次觸發的時候可以拿到多個觸控輸入

private

static

void

wndproc(window

window, int

msg, intptr

wparam, intptr

lparam, ref

bool

handled)

}

通過下面**,可以找到當前的訊息有多少次輸入

var

inputcount

=wparam.toint32() &

0xffff;

然後建立乙個陣列,從 gettouchinputinfo 獲取所有的輸入

var

inputs

=new

nativemethods.touchinput[inputcount];

nativemethods.gettouchinputinfo(lparam, inputcount, inputs, nativemethods.touchinputsize);

如果可以拿到輸入,那麼 gettouchinputinfo 將會返回 true 通過這個判斷

然後遍歷 inputs 輸入進行轉換事件,從 wpf 模擬觸控裝置 找到通過封裝的 down 等方法可以轉換為事件,請看**

在 gettouchinputinfo 方法拿到的輸入的類包含了當前觸控的螢幕座標和觸控的面積,拿到的資料其實是原有是的百分之一也就是需要除以100才是畫素

[structlayout(layoutkind.sequential)]

internal

struct

touchinput

通過下面**可以將 touchinput 轉換為螢幕座標和觸控面積,注意這裡沒有處理任何 dpi 相關,也就是我認為當前的螢幕是 96 的 dpi 的時候下面的轉換的就是相對螢幕的座標

var

position

=new

point(input.x

/100.0, input.y

/100.0);

varsize

=new

size(input.cxcontact

/100.0, input.cycontact

/100.0);

在一次觸控的過程,需要使用相同的 touchdevice 於是在按下和移動等需要有乙個相同的例項,通過建立乙個靜態的字典按照觸控的 id 存放

private

static

readonly

dictionary

<

int, messagetouchdevice

>

_devices

=new

dictionary

();

在判斷沒有存在裝置的時候建立

if (!

_devices.trygetvalue(input.dwid, out

vardevice))

在判斷是按下的時候觸發按下

if (!

device.isactive

&&input.dwflags.hasflag(nativemethods.toucheventf.toucheventf_down))

其他事件也差不多,另外在 gettouchpoint 方法需要做一點修改,新增屬性 position 和 size 在獲取的時候返回

///

>

/// 觸控點

///summary

>

private

point

position

///>

/// 觸控大小

///summary

>

private

size

size

public

override

touchpoint

gettouchpoint(iinputelement

relativeto)

上面**沒有按照約定,返回輸入元素相對的座標,而是返回螢幕的座標,所以請小夥伴自己修改**才能在專案使用,同時因為使用的是螢幕的座標,所以在主視窗觸控的時候,如果判斷當前的觸控點在螢幕之外,那麼就不會觸發主視窗的觸控。因為主視窗期望的是返回的輸入的點是相對的主視窗的座標而不是相對於螢幕的座標

所有**放在 github 歡迎小夥伴幫忙修改

除了通過 touch 訊息之外,在 win7 以上的系統,如 window 10 系統支援 pointer 訊息,可以通過 把觸控提公升 pointer 訊息 將觸控訊息轉 pointer 訊息進行模擬

2019 11 29 WPF 從觸控訊息轉觸控事件

title author date createtime categories wpf 從觸控訊息轉觸控事件 lindexi 2019 11 29 08 47 55 0800 2019 05 12 15 12 31 0800 wpf在 wpf 程式可能因為一些坑讓程式觸控失效,如果此時還可以收到系統...

WPF 觸控失效 試試重啟觸控

在使用一些詭異的系統以及詭異的觸控框的時候,也許會出現 wpf 程式觸控失效,失效的本質原因是 win32 層應用觸控失效。也許出現的問題是某個視窗設定 topmost 然後插拔一些觸控裝置等,這些行為,如果觸控裝置太過詭異,也許就會讓 win32 視窗觸控失效。剛好 wpf 也是乙個 win32 ...

2018 5 4 WPF 獲得觸控精度和觸控點

title author date createtime categories wpf 獲得觸控精度和觸控點 lindexi 2018 05 04 21 11 51 0800 2018 5 4 21 8 4 0800 wpf 觸控 需要通過反射的方法才可以拿到觸控的精度。使用 tablet.tabl...