
简介这份资源是面向C#与WPF开发者的二维码生成与识别实战Demo基于Zxing.Net库实现QR Code的编码与解码适合希望快速在桌面端集成扫码功能的初中级开发者参考。压缩包共148个文件约22.48MB以35个dll动态库、31个pdb调试符号、29个xml文档及12个cs源码为主另含xaml界面文件、config配置、nupkg包与exe可执行程序完整保留了Visual Studio工程结构与依赖引用便于直接编译运行和二次修改。已有1002人学习下载。通过该示例可掌握BarcodeWriter生成二维码并绑定Image控件的写法、QrCodeEncodingOptions尺寸参数设置以及结合多媒体库从摄像头或图片中读取二维码的思路同时能借鉴错误处理与界面交互的组织方式为实际项目落地提供可复用的代码骨架。1. 从一张贴纸到一套可复用的扫码模块工厂质检工位上贴着一张二维码扫出来是工单号仓库货架上贴着一张二维码扫出来是物料批次医院标本管上贴着一张二维码扫出来是患者与检验项目的绑定关系。这些场景背后往往是同一类需求在 Windows 桌面端用 C# WPF 做一个能生成二维码、也能识别二维码的小工具。标题里的「C#_WPF二维码的生成和识别demo」说的正是这件事——它不是要你从零实现编码算法而是把成熟的二维码编解码库接进 WPF 界面跑通「输入文本 → 生成图片 → 保存或显示 → 从图片/摄像头读回文本」这条链路。适合谁看一是刚接触 WPF 但需要快速交付一个扫码小工具的开发者二是手里已有 WinForm 或控制台版本、想迁到 WPF 的工程师三是需要把二维码能力嵌进现有桌面系统的团队。这篇笔记按「选库 → 生成 → 识别 → 避坑 → 进阶」的顺序展开代码可以直接抄参数会逐个说明踩过的坑也会标出来。2. 选库与工程搭建为什么是 ZXing.Net 而不是自己写2.1 二维码编解码的三种常见路线在 .NET 生态里做二维码绕不开三条路。第一条是纯手写编码算法把 Reed-Solomon 纠错、掩码评估、格式信息全部实现一遍——除非你的目标就是研究算法本身否则这条路投入产出比极低一个边界条件没处理好就是扫不出来。第二条是调用系统或第三方服务把文本发到远端生成图片再下载离线场景直接失效还引入网络依赖。第三条是引入成熟的本地库这也是我一般会推荐的做法。本地库里面ZXing.Net 是 .NET 平台移植最完整、文档相对齐全的一个。它同时支持生成BarcodeWriter和识别BarcodeReader覆盖 QR Code、Code 128、EAN-13 等常见格式纯托管代码不依赖本地动态库部署时不会因为缺 DLL 翻车。另一个常见选择是 QRCoder它生成质量不错、API 简洁但识别能力弱通常还要再配一个识别库等于维护两套依赖。所以如果生成和识别都要ZXing.Net 是更省心的单库方案。需要说明的是ZXing.Net 在不同目标框架下的包名有差异。WPF 项目一般用ZXing.Net配合ZXing.Net.Bindings.Windows.Composition或直接用ZXing.Net自带的BarcodeWriter输出WriteableBitmap。下面以 .NET 6/8 的 WPF 项目为例老版本 .NET Framework 的写法在参数上基本一致只是包引用方式不同。2.2 建工程与装包的最小步骤先建一个 WPF 应用然后在项目文件里加包引用。用命令行也可以dotnet new wpf -n QrDemo cd QrDemo dotnet add package ZXing.Net装完之后检查 csproj确认TargetFramework是net6.0-windows或net8.0-windows并且带UseWPF。如果目标框架写成net6.0不带-windowsWPF 相关类型会找不到这是新手最容易忽略的一步。Project SdkMicrosoft.NET.Sdk PropertyGroup OutputTypeWinExe/OutputType TargetFrameworknet8.0-windows/TargetFramework UseWPFtrue/UseWPF Nullableenable/Nullable /PropertyGroup ItemGroup PackageReference IncludeZXing.Net Version0.16.9 / /ItemGroup /Project版本号以你实际还原到的为准不必强求一致。装包后如果 IDE 里ZXing命名空间飘红先执行一次dotnet restore再检查是不是包源配置问题而不是急着换库。2.3 项目结构怎么分才不返工我一般会把二维码相关逻辑单独放一个Services目录界面只负责绑定和事件。原因是生成和识别后续大概率会被别的地方复用比如批量生成、后台服务调用如果逻辑写在MainWindow.xaml.cs里迁移时就要重写。一个够用的结构是Services/QrGenerator.cs负责生成Services/QrDecoder.cs负责识别MainWindow.xaml只做输入框、按钮和图片显示。这样即使后面换成 MVVM改动量也可控。3. 生成二维码从文本到 WriteableBitmap 的完整链路3.1 生成的核心参数怎么设ZXing.Net 生成二维码的核心是BarcodeWriter配合QrCodeEncodingOptions。几个关键参数直接决定成品能不能被扫出来Width和Height是输出像素尺寸太小会导致模块挤在一起识别率下降Margin是四周留白二维码标准要求至少 4 个模块的静区设成 0 会让很多扫码器直接失败CharacterSet决定中文等非 ASCII 字符的编码方式不设的话中文可能变乱码ErrorCorrection是纠错级别L/M/Q/H 四档级别越高可恢复的破损越多但同样尺寸下能装的数据越少。下面是一个可直接用的生成方法using ZXing; using ZXing.Common; using ZXing.QrCode; using ZXing.QrCode.Internal; using System.Windows.Media.Imaging; public static class QrGenerator { public static WriteableBitmap Generate(string content, int size 300) { var options new QrCodeEncodingOptions { Width size, Height size, Margin 4, // 静区别设 0 CharacterSet UTF-8, // 中文必须显式指定 ErrorCorrection ErrorCorrectionLevel.M, DisableECI false // 让 ECI 生效保证编码声明正确 }; var writer new BarcodeWriter { Format BarcodeFormat.QR_CODE, Options options }; // 直接输出 WPF 可用的 WriteableBitmap return writer.Write(content); } }逻辑说明BarcodeWriter.Write返回的类型取决于绑定的渲染器在 WPF 环境下会返回WriteableBitmap可以直接赋给Image.Source。DisableECI设为 false 是为了在数据里写入字符集声明扫码端才能正确按 UTF-8 解码中文。如果你的内容全是数字或英文这个参数影响不大但统一设成 false 更稳。参数说明size我默认给 300实际项目里如果二维码要打印在很小的标签上建议不低于 200如果要嵌入文档放大显示可以给 500 以上但注意别超过内容容量对应的最大版本否则库会自动升版本模块变密反而更难扫。ErrorCorrectionLevel.M是折中档约能恢复 15% 的破损如果二维码会印在易磨损的包装上升到 Q 或 H代价是同样尺寸下容量下降。3.2 把生成结果接到 WPF 界面上界面部分用一个TextBox收内容一个Button触发一个Image显示。XAML 保持最简StackPanel Margin20 TextBox x:NameInputBox Height30 Texthttps://example.com/ Button Content生成 ClickOnGenerateClick Margin0,10,0,0/ Image x:NameQrImage Width300 Height300 Margin0,10,0,0/ /StackPanel后台事件里调用生成方法并赋值private void OnGenerateClick(object sender, RoutedEventArgs e) { var text InputBox.Text?.Trim(); if (string.IsNullOrEmpty(text)) { MessageBox.Show(内容不能为空); return; } QrImage.Source QrGenerator.Generate(text, 300); }这里有个细节WriteableBitmap是 UI 线程对象生成过程本身很快直接在点击事件里同步调用没问题。但如果后面要做批量生成几百张就要放到后台线程生成完再Dispatcher.Invoke回 UI 线程赋值否则界面会卡住。3.3 保存成 PNG 文件的写法生成之后往往要落盘。WriteableBitmap转 PNG 用PngBitmapEncoderusing System.IO; using System.Windows.Media.Imaging; public static void SaveAsPng(WriteableBitmap bitmap, string path) { var encoder new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); using var fs new FileStream(path, FileMode.Create); encoder.Save(fs); }逻辑说明PngBitmapEncoder是 WPF 自带的编码器不需要额外依赖。BitmapFrame.Create把WriteableBitmap包成帧再写入文件流。注意FileMode.Create会覆盖同名文件如果业务上不允许覆盖先判断File.Exists再决定是改名还是提示。保存路径建议用Path.Combine拼接不要手写反斜杠避免跨环境出问题。4. 识别二维码从图片文件和摄像头帧里读回文本4.1 从本地图片识别的标准流程识别用BarcodeReader核心是设置AutoRotate和TryInverted等选项然后把位图传进去。WPF 里图片通常来自文件或BitmapSource需要先转成 ZXing 能处理的格式。ZXing.Net 在 WPF 下可以直接接受WriteableBitmap但如果是BitmapImage要先转一道。using ZXing; using ZXing.Common; using System.Windows.Media.Imaging; public static class QrDecoder { public static string? DecodeFromFile(string filePath) { var bitmap new BitmapImage(); bitmap.BeginInit(); bitmap.CacheOption BitmapCacheOption.OnLoad; // 避免文件被占用 bitmap.UriSource new Uri(filePath); bitmap.EndInit(); var reader new BarcodeReader { AutoRotate true, // 允许自动旋转应对拍歪的图 TryInverted true, // 尝试反色应对深底浅码 Options new DecodingOptions { TryHarder true, // 提高识别率代价是慢一点 CharacterSet UTF-8 } }; var result reader.Decode(bitmap); return result?.Text; } }逻辑说明BitmapCacheOption.OnLoad很关键它让图片在EndInit时就加载完并释放文件句柄否则后续想删除或覆盖这个文件会报占用。AutoRotate和TryInverted是识别率的两大救命参数尤其是用户用手机拍屏幕再传过来的图不设这两个经常读不出来。TryHarder会让解码器尝试更多二值化策略识别率上去了单张耗时可能从几毫秒涨到几十毫秒批量场景要权衡。参数说明CharacterSet要和生成端一致生成用 UTF-8识别也写 UTF-8否则中文会乱码。如果识别的是别人生成的二维码不确定字符集可以先不设看结果是否正常再决定要不要补。4.2 摄像头实时识别的接入方式实时识别比图片识别多一层取帧。WPF 里常用MediaCapture或第三方库取摄像头帧这里不展开摄像头采集本身重点说拿到帧之后怎么识别。假设你已经有一个WriteableBitmap类型的当前帧识别逻辑和上面几乎一样只是要控制频率——不要每帧都识别否则 CPU 占用会很高。private DateTime _lastDecode DateTime.MinValue; private void OnFrameReady(WriteableBitmap frame) { // 限制识别频率200ms 一次足够 if ((DateTime.Now - _lastDecode).TotalMilliseconds 200) return; _lastDecode DateTime.Now; var reader new BarcodeReader { AutoRotate true, TryInverted true, Options new DecodingOptions { TryHarder false } // 实时场景关掉保帧率 }; var result reader.Decode(frame); if (result ! null) { Dispatcher.Invoke(() ResultText.Text result.Text); } }逻辑说明实时场景把TryHarder关掉是因为它太耗时会拖垮帧率而摄像头连续取帧本身就有多次尝试机会不差这一张。识别到结果后通过Dispatcher.Invoke回 UI 线程更新文本直接跨线程操作控件会抛异常。频率限制用时间戳判断比用定时器更轻量。4.3 识别结果为空时的排查顺序识别返回 null 是常态不要一上来就怀疑库有问题。我一般按这个顺序查先确认图片里二维码区域是否完整、有没有被裁掉静区再确认图片是否过暗或过曝必要时先做灰度化和对比度拉伸然后确认二维码版本是否过高、模块太密可以尝试放大图片再识别最后才怀疑字符集或库版本。把这几步做成日志比反复改代码有效。5. 避坑与常见问题那些让 demo 跑不起来的细节5.1 中文变乱码现象、原因、解决现象生成时输入中文扫出来是问号或乱码。原因生成端没有显式指定CharacterSet或者DisableECI设成了 true导致数据里没有字符集声明扫码端按默认字符集解码。解决生成端CharacterSet UTF-8且DisableECI false识别端也设CharacterSet UTF-8两端对齐。5.2 二维码扫不出来静区被吃掉现象生成的二维码看着正常但扫码器怎么都对不上。原因Margin设成了 0或者生成后又被裁剪掉了边缘。解决Margin至少设 4如果二维码要贴到深色背景上静区还要留得更宽因为深色背景会干扰扫码器判断边界。5.3 图片文件被占用删不掉也覆盖不了现象识别完一张图后想删除或替换这个文件提示被占用。原因BitmapImage默认是延迟加载文件句柄没释放。解决BeginInit之后设CacheOption BitmapCacheOption.OnLoad让图片立即加载完并释放句柄。这个坑在批量处理图片时特别常见。5.4 实时识别卡顿每帧都解码现象摄像头预览很卡CPU 占用高。原因每来一帧就调用一次Decode而TryHarder又开着。解决加时间间隔限制实时场景关掉TryHarder并且识别到结果后可以短暂暂停解码等界面处理完再继续。5.5 部署到别的机器报缺依赖现象开发机跑得好好的拷到另一台机器就报找不到方法或类型。原因目标机器没装对应的 .NET 运行时或者项目用了框架依赖发布。解决确认目标机运行时版本或者改成自包含发布同时确认 ZXing.Net 的 DLL 随程序一起输出了不要只拷 exe。6. 进阶技巧把 demo 变成能进生产的模块6.1 批量生成时用后台线程和进度反馈单张生成几十毫秒几百张就是几十秒同步做界面必然假死。做法是把生成循环放到Task.Run里每生成一张通过IProgressint回报进度UI 线程只负责更新进度条。注意WriteableBitmap必须在 UI 线程创建所以后台线程只做数据准备和文件写入位图创建回 UI 线程或者改用不依赖 WPF 的位图类型再转换。6.2 识别成功率的关键参数对照参数作用建议值代价AutoRotate自动旋转识别true略增耗时TryInverted尝试反色true略增耗时TryHarder多策略二值化图片识别 true实时 false明显增耗时CharacterSet字符集UTF-8无Margin生成静区≥4占用尺寸这张表可以直接当调参起点。图片识别追求成功率实时识别追求帧率两边的取舍点就在TryHarder上。6.3 一个容易被忽略的验证习惯我现在的习惯是任何二维码生成改动之后不只用自己写的识别代码验还要用手机扫码和至少一个独立的扫码工具各验一次。原因是自己写的识别和生成用的是同一个库可能存在「自洽但不通用」的情况比如字符集声明不规范自己读得出来别的扫码器读不出来。多一道外部验证能提前拦住大部分兼容性问题。这个习惯帮我省过好几次返工。希望帮到你。本文还有配套的精品资源点击获取