资讯动态

WPF二维码生成与识别实战:基于zxing的扫码Demo

发布时间:2026/10/24 5:46:17 来源:尧图企业网站定制
简介这份资源是面向C#与WPF开发者的二维码生成与识别实战示例聚焦在桌面应用中集成ZXing.Net完成QR Code的编码与解码适合已具备一定WPF基础、希望快速上手条码功能的开发者参考。压缩包共148个文件、约22.48MB以dll动态库、pdb调试符号、xml文档注释和cs源码为主另含xaml界面文件、sln解决方案、csproj工程配置及少量缓存与资源文件构成可直接运行的完整工程结构。已有1002人学习下载说明其在同类示例中具备一定参考价值。读者可从中获得二维码生成的BarcodeWriter配置思路、位图与Image控件绑定的界面呈现方式以及借助多媒体库从摄像头或图片读取二维码的整合方向并了解项目引用组织与依赖管理细节便于迁移到自己的WPF项目中。1. 一个 WPF 扫码 Demo 到底能省掉多少重复劳动去年帮一个做仓储管理系统的朋友救火他们的入库工位需要扫二维码录入批次号原本用的是一个第三方收费控件授权到期后弹窗锁死整个工位停摆。我翻出自己压箱底的一个 C# WPF 二维码生成和识别 Demo改了两小时就顶上了。这件事让我意识到很多做桌面端的同行其实并不缺业务逻辑缺的是一个能直接跑起来、生成和识别两头都通的二维码最小闭环。这个 Demo 就是干这个的基于 WPF 界面用 zxing 相关库完成二维码的编码生成与摄像头/图片解码识别适合需要在自己桌面系统里嵌入扫码能力的开发者也适合刚接触 WPF 图像处理、想找一个完整可运行例子的新手。它不解决高并发扫码也不做工业级纠错调优但把「生成一张能扫的码」和「从一张图里把码读出来」这两件事的骨架搭清楚了剩下的就是往里填业务。2. 拆开这个 Demozxing 在 WPF 里到底怎么落地2.1 为什么是 zxing而不是自己写编码二维码看着简单真要从零实现编码光是 Reed-Solomon 纠错码的生成多项式就够喝一壶。zxing 这套库把编码、掩码选择、纠错等级、版本选择全封装好了调用方只需要给内容和参数。这个 Demo 选它核心原因是跨语言生态成熟C# 侧有对应的移植版本API 形态和 Java 原版基本一致遇到问题搜到的资料大多能对上号。另一个现实原因是它同时覆盖生成和解码不用为了生成引入一个库、为了识别再引入另一个库依赖面收窄部署时少一堆 DLL 冲突的玄学问题。需要说清楚的是zxing 的解码对图像质量有要求。它内部走的是二值化加定位图案搜索的流程先找三个角上的定位方块再根据版本信息确定网格最后逐格采样解码。这意味着输入图像如果模糊、反光、倾斜角度过大识别率会明显下降。Demo 里对摄像头帧做了灰度化和适度缩放就是在给这条流程喂更干净的输入。理解这一点后面调参数时才知道该动哪里。2.2 生成端编码参数怎么设才不翻车生成二维码的代码不长但参数选错会导致扫不出来或者容量不够。下面这段是 Demo 里生成逻辑的核心我按自己的习惯加了注释using ZXing; using ZXing.Common; using ZXing.QrCode; public BitmapSource GenerateQr(string content, int pixelSize 300) { // 编码器指定二维码格式纠错等级设为 M var writer new BarcodeWriter { Format BarcodeFormat.QR_CODE, Options new QrCodeEncodingOptions { // 纠错等级L(7%) M(15%) Q(25%) H(30%) ErrorCorrection ZXing.QrCode.Internal.ErrorCorrectionLevel.M, // 字符集中文内容必须显式指定否则可能乱码 CharacterSet UTF-8, // 输出图像边长像素不是模块数 Width pixelSize, Height pixelSize, // 静区边距单位是模块数太小会导致定位失败 Margin 2 } }; // 返回 WPF 可直接绑定的 BitmapSource return writer.Write(content); }逻辑上分三步构造 writer、配置编码选项、写出位图。参数里最容易被忽视的是CharacterSet默认值在某些版本下不是 UTF-8中文内容生成出来扫出来是乱码这个坑我踩过。Margin是静区二维码四周必须留白设成 0 或者 1 时部分扫码枪会因为找不到边界而失败2 是比较稳的下限。ErrorCorrection选 M 是折中内容不长时用 Q 更抗污损但容量会缩水如果业务里二维码要印在小标签上被磨损建议直接上 Q 或 H。Width/Height控制的是最终像素尺寸不是模块数量模块数由内容和纠错等级自动决定所以内容越长同样像素下每个模块越小打印出来越难扫这是容量和可扫性的天然矛盾。2.3 识别端从图片和摄像头两条路走通识别分两个入口一个是选本地图片文件一个是开摄像头实时取帧。图片识别路径简单读成位图后交给BarcodeReaderusing ZXing; using System.Drawing; public string DecodeFromFile(string filePath) { var reader new BarcodeReader { Options new DecodingOptions { // 只尝试二维码减少误判和耗时 PossibleFormats new[] { BarcodeFormat.QR_CODE }, // 允许尝试更复杂的二值化策略 TryHarder true, // 指定字符集和生成端保持一致 CharacterSet UTF-8 } }; using (var bmp new Bitmap(filePath)) { var result reader.Decode(bmp); return result?.Text; // 识别失败返回 null调用方要判空 } }TryHarder打开后会尝试多种二值化阈值代价是耗时增加图片识别场景可以开实时视频流里建议关掉否则帧率掉得厉害。PossibleFormats限定成二维码能避免把条形码误识别成二维码的尴尬。摄像头路径则是用CaptureElement或者定时器抓帧把每一帧转成WriteableBitmap再送进同一个BarcodeReaderDemo 里做了个简单的节流每 200 毫秒解一帧避免 UI 线程被解码拖死。这里有个细节解码要放到后台线程解出来的结果再通过Dispatcher回主线程更新界面直接在 UI 线程里跑解码摄像头预览会卡成幻灯片。2.4 界面与线程WPF 里最容易忽略的一环WPF 的线程模型决定了任何耗时操作都不能碰 UI 线程。Demo 里生成操作因为通常很快同步执行问题不大但识别尤其是摄像头连续识别必须异步。常见做法是用Task.Run包住解码逻辑结果通过await回到 UI 上下文。另一个容易翻车的是图像格式转换Bitmap和BitmapSource之间来回转如果没释放非托管资源跑久了内存会一路涨。Demo 里对每帧的Bitmap都用了using这个习惯值得保留。界面布局上预览区和结果区分离识别成功时把文本追加到结果列表而不是覆盖方便连续扫码时核对这个小设计在实际工位场景里很实用。3. 从零跑起来环境、依赖与第一次生成识别3.1 环境准备与依赖引入这个 Demo 是标准的 WPF 项目目标框架建议 .NET Framework 4.7.2 以上或者 .NET 6/8 的 Windows 桌面目标两者都能跑但依赖引入方式不同。老项目走 NuGet 装 zxing 的 C# 移植包新项目同样走 NuGet注意包名在不同版本下可能有差异装的时候看清楚是核心库还是带 System.Drawing 依赖的版本。WPF 本身不直接依赖 System.Drawing但解码位图时用到了Bitmap所以需要额外引用System.Drawing.Common在 .NET 6 以上这个包是独立的不装会编译报错。# 以 .NET CLI 为例在项目目录下执行 dotnet add package ZXing.Net dotnet add package System.Drawing.Common装完检查一下项目的目标框架和包的兼容性System.Drawing.Common在非 Windows 平台上有额外限制但这个 Demo 本来就是 Windows 桌面场景不受影响。如果用的是 Visual Studio 的包管理器搜索同样的包名安装即可装完重新生成一次确认没有版本冲突警告再往下走。3.2 生成一张码并验证可扫性跑通生成最简单的方式是界面上放一个按钮点击后调用 2.2 里的方法把返回的BitmapSource赋给Image控件。验证可扫性别只用眼睛看拿手机扫一下扫不出来先查三件事内容里有没有中文而字符集没设对、静区是不是被裁掉了、像素尺寸是不是太小。我一般会把生成的码存成 PNG 再扫排除界面缩放带来的干扰。// 按钮点击事件里 private void OnGenerateClick(object sender, RoutedEventArgs e) { var content ContentTextBox.Text; if (string.IsNullOrWhiteSpace(content)) { MessageBox.Show(内容不能为空); return; } // 生成并直接绑定到界面 QrImage.Source GenerateQr(content, 400); }参数 400 是像素边长内容短的时候够用内容长到接近容量上限时建议提到 600 以上否则模块太密手机对焦都费劲。生成后如果要做打印注意 DPI 设置WPF 的BitmapSource默认 96 DPI直接打印会偏小需要在打印时按物理尺寸换算。3.3 图片识别与摄像头识别的差异处理图片识别按 2.3 的代码走选文件、解码、显示结果流程直白。摄像头识别要多考虑几件事摄像头分辨率、帧率、对焦。Demo 里默认取 640x480这个分辨率对二维码识别够用太高反而增加解码耗时。对焦方面固定焦距的摄像头在近距离扫码时容易糊如果工位场景是固定距离建议选带自动对焦的模组或者手动调好焦距后锁死。// 定时器里抓帧解码的简化逻辑 private async void OnTimerTick(object sender, EventArgs e) { if (_isDecoding) return; // 防止重入 _isDecoding true; try { var frame CaptureFrame(); // 从摄像头取一帧 Bitmap var text await Task.Run(() DecodeBitmap(frame)); if (!string.IsNullOrEmpty(text)) { ResultList.Items.Add(text); } } finally { _isDecoding false; } }_isDecoding这个标志位是血泪经验不加的话上一帧还没解完下一帧又进来线程池被占满界面直接假死。Task.Run把解码挪到后台await回来后更新 UI这个模式在 WPF 里是标准解法。识别结果追加到列表而不是覆盖连续扫码时不会丢数据。4. 避坑与排查那些让 Demo 跑不起来的细节4.1 中文内容扫出来是乱码现象是生成端显示正常手机扫出来一串问号或者方块。原因基本锁定在字符集生成时CharacterSet没设成 UTF-8或者识别端设了但生成端没设两边不一致。解决办法是生成和识别两端都显式指定CharacterSet UTF-8不要依赖默认值。另外内容里如果有 emoji 或者特殊符号部分扫码软件支持不好这是扫码端的问题不是生成端能完全解决的业务上尽量用纯文本加数字。4.2 摄像头预览正常但一直识别不出现象是画面里二维码清清楚楚结果列表就是不更新。原因通常有三个解码跑在 UI 线程导致帧被丢弃、TryHarder开着拖慢每帧耗时、或者抓到的帧格式不对比如 BGR 和 RGB 通道顺序反了。排查顺序是先确认解码在后台线程再关掉TryHarder看是否恢复最后检查帧转换代码里的通道顺序。通道反了的话二维码定位图案的对比度会异常zxing 找不到定位点表现就是永远返回 null。4.3 生成的长内容二维码扫不出来现象是短内容正常内容一长就扫不出。原因是内容变长后模块数增加固定像素尺寸下每个模块变小加上打印或屏幕显示的分辨率限制采样时误码率上升。解决办法是按内容长度动态提高像素尺寸或者降低纠错等级换取更少模块数。经验值是内容超过 200 字符时像素边长至少给到 600并且打印时保证物理尺寸不小于 3 厘米见方。4.4 部署到客户机器上报缺少 DLL现象是开发机跑得好好的拷到客户机器上启动就报找不到System.Drawing.Common或者 zxing 相关程序集。原因是发布方式没把依赖打全尤其是 .NET Framework 项目NuGet 包的 DLL 需要随程序一起拷过去。解决办法是用发布功能生成完整输出目录或者改成自包含发布。另外注意目标机器上的 .NET 运行时版本低于项目目标框架会直接起不来这个在交付前必须确认。4.5 连续扫码时内存持续上涨现象是跑十几分钟后任务管理器里内存一路涨。原因是每帧创建的Bitmap或WriteableBitmap没释放非托管资源靠 GC 回收不及时。解决办法是对所有实现IDisposable的图像对象用using包住或者在帧处理完后显式调用Dispose。摄像头采集这一侧也要注意有些采集库的帧缓冲需要手动释放不释放的话涨得更快。5. 进阶把 Demo 改成能进生产的最小闭环Demo 跑通只是起点真要放进业务系统还得补几块。第一块是识别结果的去重和校验连续扫码时同一张码会被反复识别需要加一个短时间窗口内的去重逻辑常见做法是记录上次识别的内容和时间戳两秒内相同内容不重复入库。第二块是异常兜底摄像头被占用、图片损坏、解码抛异常这些都要有明确的提示而不是让界面卡住或者静默失败。// 简单的去重逻辑 private string _lastText; private DateTime _lastTime; private bool ShouldAccept(string text) { var now DateTime.Now; // 两秒内相同内容视为重复 if (text _lastText (now - _lastTime).TotalSeconds 2) return false; _lastText text; _lastTime now; return true; }这个去重窗口的时长要按业务调入库工位扫得快可以缩到 1 秒手持设备扫得慢可以放到 3 秒。第三块是识别质量的量化别只靠肉眼判断「能不能扫」可以准备一组不同光照、不同角度的测试图跑一遍统计识别率改参数前后对比这样调参才有依据。我一般会留一个测试用的图片文件夹每次改完解码参数就跑一遍看识别率有没有回退。参数建议值影响ErrorCorrectionM普通/ Q易损纠错能力与容量成反比Margin2 以上静区不足导致定位失败Width/Height400 起长内容 600模块密度与可扫性TryHarder图片开视频关识别率与耗时权衡解码节流200ms/帧帧率与 CPU 占用最后说个习惯。从那以后我每次把这类 Demo 往业务里搬都强制走一遍「生成—打印—扫描—入库」的完整链路而不是只在屏幕上扫一下就算完。屏幕扫和打印扫是两回事打印的墨色、纸张反光、标签尺寸都会影响识别提前在真实介质上验证比上线后返工省事得多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑