WPF实现无线扫码枪无焦点自动获取数据并逻辑处理
USB接口的扫码枪基本就相当于一个电脑外设,在WINDOWS系统中,甚至可以简单到不做任何处理,就能在焦点位置获取扫码枪返回的数据。但是作为一个程序员,总是会遇到客户各种神奇的需求,比如一个比较常见的功能:客户手拿扫码枪不管扫什么,软件都能自动辨别是什么类型的条码,叭叭叭扫好几种条码的时候,软件能够区分出来各种条码,并且根据条码调出相关数据。
呐,这个需求的话,就直接把手动更改焦点位置来扫码的可能性pass了,客户没有那么智能,并且客户很懒。这样的话,就需要根据条码的内容来确定是什么条码,然后再和后台数据一起显示到软件界面上。
这个时候就确定了软件有关扫码枪方面需要实现的功能:无视焦点,后台获取扫码枪的数据,然后就是业务逻辑了。这篇文章就是介绍WPF如何实现无焦点获取扫码枪数据。
这里需要感谢一下简书上的一位大佬,他的一篇文章基本解决我的需求,但是其中很多地方讲解的不太详细,我在这里再画蛇添足一下,dalao勿怪。
现在放出几个关键点的代码,加以说明,全部代码在最后放出链接:
1.扫描监听器BarcodeScannerListener
使用WindowInteropHelper获取传入窗体的句柄,并且绑定ThreadFilterMessage事件,达到从而可以触发ProcessRawInputMessage方法
/// <summary>
/// 将监听器附着到窗体上
/// </summary>
/// <param name="form">需要附着的窗体(WPF)</param>
public void Attach(Window form)
{
var helper = new WindowInteropHelper(form);
IntPtr hwnd = helper.Handle;
form.KeyDown += (sender, args) =>
{
if (_ControlHandled)
{
args.Handled = true;
_ControlHandled = false;
}
};
DoAttach(hwnd);
}
/// <summary>
/// 监听绑定
/// </summary>
/// <param name="hwnd">设备指针</param>
private void DoAttach(IntPtr hwnd)
{
this.keystrokeBuffer = new StringBuilder();
this.InitializeBarcodeScannerDeviceHandles();
this.interopHelper.HookRawInput(hwnd);
//this.HookHandleEvents(form);
//this.AssignHandle(ptr);
this.filter = new BarcodeScannerKeyDownMessageFilter();
ComponentDispatcher.ThreadFilterMessage -= ComponentDispatcher_ThreadFilterMessage;
ComponentDispatcher.ThreadFilterMessage += ComponentDispatcher_ThreadFilterMessage;
//Application.AddMessageFilter(this.filter);
}
ProcessRawInputMessage方法中,判断传入的字符串是否是扫码枪设置的结束字符(扫码的字符串是一个一个传入的),如果不是,就加入到Buffer中,如果是,则触发FireBarcodeScanned方法
/// <summary>
/// 处理WM_INPUT消息
/// </summary>
/// <param name="rawInputHeader">rawInputHeader的指针</param>
/// <returns>按键是否被处理</returns>
private bool ProcessRawInputMessage(IntPtr rawInputHeader)
{
BarcodeScannerDeviceInfo deviceInfo;
bool handled;
bool keystroke;
string localBuffer;
IntPtr rawInputDeviceHandle;
handled = false;
keystroke = false;
localBuffer = string.Empty;
rawInputDeviceHandle = IntPtr.Zero;
this.interopHelper.GetRawInputInfo(
rawInputHeader,
ref rawInputDeviceHandle,
ref keystroke,
ref localBuffer);
if (this.devices.TryGetValue(rawInputDeviceHandle, out deviceInfo) && keystroke)
{
handled = true;
// 这里判断的是Tab按键,可以更换为其他按键
if (localBuffer.Length == 1 && (localBuffer[0] == 0x09 || localBuffer[0] == '\t'))
{
this.FireBarcodeScanned(deviceInfo);
}
else
{
this.keystrokeBuffer.Append(localBuffer);
}
}
return handled;
}
FireBarcodeScanned方法中,则会调用界面初始化时,绑定的事件,传入扫码的字符串
/// <summary>
/// 触发扫码事件
/// </summary>
/// <param name="deviceInfo">扫码设备信息</param>
private void FireBarcodeScanned(BarcodeScannerDeviceInfo deviceInfo)
{
string barcode;
EventHandler handler;
barcode = this.keystrokeBuffer.ToString();
if (barcode.Length > 0)
{
handler = this.BarcodeScanned;
this.keystrokeBuffer = new StringBuilder();
if (handler != null)
{
handler(this, new BarcodeScannedEventArgs(barcode, deviceInfo));
}
}
}
2.页面调用
这里我使用的MVVM模式,所以在ViewModel层调用,但是只要能拿到View的对象,在那一层都没有关系
BarcodeScannerListener = new BarcodeScannerListener();
BarcodeScannerListener.Attach((Window)GetView());
BarcodeScannerListener.BarcodeScanned += OnBarcodeScanned;
在传入的事件中,获取Barcode属性即可得到扫描的值
private void OnBarcodeScanned(object sender, EventArgs e)
{
string barcode = ((BarcodeScannedEventArgs)e).Barcode;
DoBarcodeScanned(barcode);
}
3.配置条码枪的硬件ID
需要在windows设备管理器中,找到扫码枪的设备ID
就是在这里,原文作者没有讲清楚。
第一步:寻找USB设备的ID;Win10系统在设备管理->人体学输入设备->找到USB选项的设备ID
第二步:在程序中确认设备ID写入,并且测试SDK
这其中涉及到App.config的编程和设备ID的解析,还有一个&符号的转义,这里我就不赘述了。
完整Demo