Winform开发框架之读卡器和条码扫描枪的数据接收处理

在我们开发一些项目的时候,一般需要一些外围的设备进行数据处理,如ID/IC读卡器获取卡号、激光条码扫描枪、USB摄像头、USB方式的小票据打印机(POS打印机)、USB来电录音盒、普通打印机等一系列附属设备。借助这些设备,可以使我们的业务流程更严谨,输入数据更方便,或者能够一些特殊的数据等功能。本文主要介绍其中的ID读卡器(IC读卡器)快速读取卡号,以及实用激光条码枪的条码扫描录入功能,后面的一些硬件设备的处理,后续文章在继续介绍。

1、设备介绍

前面介绍的设备,在很多场合上都可能用到,如我的会员管理系统里面,就需要用到下面的设备处理。



本文主要针对性了解ID读卡器和条码枪的设备数据处理,这两种设备虽然不同,但是它们相似的地方就是都支持在光标处录入数据的,就有点类似我们的键盘快速录入一样,当然激光条码枪也支持很多种方式的事件处理操作,这是后话。

2、ID读卡器数据读取界面和条码扫描枪读取界面的分析介绍

在我的会员管理系统里面,录入卡号一般是通过ID读卡器获取的,在界面上设置一个可以弹出录入的文本框,也方便手工录入卡号,如下面的界面功能所示。



当然,有时候,我们可能不需要提供手工录入,那么就不能通过光标录入方式获取扫描的内容,因为我们把输入框设置为只读的了,所以这种情况,就就应该通过事件来获取设备的输入内容。



在条码枪处理读取条形码或者二维码的时候,我们一般都是和商品相关的地方使用条形码,二维码也可以使用,条形码可能一般带有数据供阅读,二维码则没有,但是都可以通过设备读取出来到文本框里面,一般如果录入,就停放光标在文本框就可以了,如商品的信息的录入。在我们需要输入条码的地方点一下,然后操作条码枪录入条码即可,这种不需要额外的代码处理。

但是对于一些我们需要快速录入商品信息的界面,如客户消费界面,那么就需要对条码的事件进行处理了。
例如下面的界面,在消费确认前的产品录入,我们都是通过条码枪的快速扫描产品进行录入的,这时候条码枪就代替了手工的录入,我们可以每次扫描一次,就在列表里面自动增加一个对应商品的记录,非常方便的了。


3、通用的读卡操作和条码扫描枪操作实现

在前面小节介绍了一些利用ID读卡器录入数据和使用条码枪的场景,对于如果是在可输入文本框里面获得内容,不用任何编码,如果是在只读界面或者窗体上获得设备的数据,那么就可以通过事件进行处理了,那么读卡器和扫描枪的事件应该如何处理的呢。
我的做法,是统一在我的Winform开发框架的界面层基类模块里面,增加一些硬件相关的处理类和界面,这样在各个框架派生出来的项目就可以很方便使用了。



其中Device里面的CardReader就是IC、ID读卡器获取操作的处理,一般来说,这些卡都是以00开始的,所以我们的处理类,通过一个Time来控制连续获取数据的处理就可以了,主要就是监听KeyUp事件。

以CardReader为例,它的完整代码如下所示。

/// <summary>
/// 读卡器封装类
/// </summary>
public class CardReader
{
    private Control _hostCtrl;
    private string _cardCode;
    private Timer _timer;
    private const int CARD_CODE_LEN = 10;
    private const string CARD_CODE_START = "00";

    /// <summary>
    /// 读卡器读到一张卡的事件
    /// </summary>
    public event CardReadEventHandler CardRead;

    /// <summary>
    /// 默认读卡器(挂在主窗体上,会被主窗体初始化,在模块里用肯定是安全的)
    /// </summary>
    public static CardReader Default { get; set; }

    /// <summary>
    /// 构造器
    /// </summary>
    /// <param name="hostCtrl">接受键盘事件的宿主控件</param>
    public CardReader(Control hostCtrl)
    {
        _hostCtrl = hostCtrl;
        if (_hostCtrl is Form)
        {
            (_hostCtrl as Form).KeyPreview = true;
        }
        _hostCtrl.KeyUp += new KeyEventHandler(hostCtrl_KeyUp);
        _cardCode = "";
        _timer = new Timer();
        _timer.Interval = 20;
        _timer.Tick += new EventHandler(timer_Tick);
        _timer.Start();
    }

    /// <summary>
    /// 判断是否卡号
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    public static bool IsCardCode(string code)
    {
        return code.Length == CARD_CODE_LEN && code.StartsWith(CARD_CODE_START);
    }

    /// <summary>
    /// 定时器到期的事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void timer_Tick(object sender, EventArgs e)
    {
        //达到一定的位数才开始判断
        if (_cardCode.Length >= CARD_CODE_LEN)
        {
            _cardCode = _cardCode.Trim((char)13);
            if (IsCardCode(_cardCode))
            {
                _timer.Stop();
                OnCardRead(_cardCode);
            }
        }
        _cardCode = "";
        _timer.Start();
    }

    /// <summary>
    /// 监听按键弹起的事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void hostCtrl_KeyUp(object sender, KeyEventArgs e)
    {
        _timer.Stop();
        _cardCode = _cardCode + (char)e.KeyValue;
        _timer.Start();
    }

    private void OnCardRead(string scanCode)
    {
        if (CardRead != null)
        {
            CardRead(scanCode);
        }
    }

}

/// <summary>
/// 读卡器读到一张卡的事件处理委托
/// </summary>
/// <param name="cardCode"></param>
public delegate void CardReadEventHandler(string cardCode);

CardReader封装类, 它的使用操作如下所示。我们通过事件就可以获取到完整的输入内容,然后进行数据的绑定或处理即可,代码如下所示。

public partial class FrmProcessConsumption : BaseDock
{
    /// <summary>
    /// 会员信息
    /// </summary>
    private MemberInfo memberInfo { get; set; }

    /// <summary>
    /// 读卡器接口
    /// </summary>
    private CardReader cardReader;

    public FrmProcessConsumption()
    {
        InitializeComponent();

         ................................

        cardReader = new CardReader(this);
        cardReader.CardRead += new CardReadEventHandler(cardReader_CardRead);   
    }

    void cardReader_CardRead(string cardCode)
    {
        this.txtMember_CardNo.Text = cardCode;

        BindMemberData();
    }

然后我们为了方便使用,还可以定义一个统一的处理读卡器和扫描枪的接收数据的小窗口。



这个弹出的小窗口用来处理读卡器,扫描枪等信息的录入就可以了,当然上述的如CardReader/USBScanner还是可以独立使用,如我们在一个只读控件或者窗口里面,一样可以监听到对应的设备数据读取操作,但设备有数据读取完成过后,就会触发相应的事件了。
下面代码就是上面设备信息读取的代码

/// <summary>
/// 读卡器、USB条码扫描器、串口条码扫描器数据读取及显示窗体
/// </summary>
public partial class DeviceReaderDialog : BaseForm
{
    private CardReader _cardReader;
    private USBScanner _usbScanner;public DeviceReaderDialog(DeviceType type = DeviceType.Card)
    {
        InitializeComponent();
        //能手填
        this.Readonly = false;

        if (type == DeviceType.Card)
        {
            this._cardReader = new CardReader(this);
            this._cardReader.CardRead += new CardReadEventHandler(_cardReader_CardRead);
        }
        else if (type == DeviceType.UsbScanner)
        {
            this._usbScanner = new USBScanner(this);
            this._usbScanner.ScannerRead += new ScannerReadEventHandler(Scanner_ScannerRead);
        }
    }

    void Scanner_ScannerRead(string scanCode)
    {
        this.txtCode.Text = scanCode;
        DialogResult = DialogResult.OK;
    }

    void _cardReader_CardRead(string cardCode)
    {
        this.txtCode.Text = cardCode;
        DialogResult = DialogResult.OK;
    }

    public string Code
    {
        get { return txtCode.Text; }
    }

    public bool Readonly
    {
        get { return txtCode.Properties.ReadOnly; }
        set
        {
            txtCode.Properties.ReadOnly = value;
            this.btnOK.Enabled = !value;
            this.btnOK.Visible = !value;
        }
    }

    private void DeviceReaderDialog_Load(object sender, EventArgs e)
    {
        if (!this.Readonly)
        {
            this.KeyDown += new KeyEventHandler(DeviceReaderDialog_KeyDown);
        }
    }

    void DeviceReaderDialog_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
        {
            this.DialogResult = DialogResult.OK;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容