C#写的即开即用抽奖小工具,带人员库和奖项设置,ACCESS本地存数据
简介:直接双击就能用的桌面抽奖程序,用C#开发,数据存在内置ACCESS数据库里(默认密码admin),不用装运行环境或额外配置。能批量导入、手动添加或修改参与抽奖的人员信息,也能自由设定多个奖项名称、数量和中奖概率。支持单次抽1到20人,结果实时显示在主界面,还能导出中奖名单。维护界面专门管人员和奖项数据,操作简单。底层封装了数据库读写(AwardDal/EmpDal/LuckyDal)、SQL执行辅助、INI配置文件读写、扫码枪识别(ReadBarcode)、统一提示框(ShowMessage)等功能。项目结构清晰,分层明确,源码完整,.csproj工程文件齐全,附带wc.db数据库文件,适合年会、班会、部门团建这类需要快速上手抽奖的场合。
1. 这不是“又一个抽奖程序”,而是一套能直接塞进U盘、插上电脑就开抽的现场作战包
你有没有经历过这种场面:年会倒计时两小时,主持人催第三遍“抽奖软件好了没”,IT同事还在远程连内网装.NET运行时;学校班会现场,投影仪连着老旧台式机,双击exe弹出“缺少msvcp140.dll”;团建活动在咖啡馆临时租的笔记本上,刚导完300人名单,一重启——人员库没了。这些不是小概率事件,而是我过去八年给二十多家企业、三十多所学校做现场技术支持时,踩得最深、最频繁的坑。这款C#即开即用抽奖工具,就是从这些狼狈时刻里长出来的——它不追求炫酷动画或云端同步,只解决一个核心问题:让非技术人员,在任意一台Windows电脑(Win7 SP1及以上)上,双击主程序,30秒内完成从导入名单到抽出一等奖的全流程。
它的关键词非常实在:“C#抽奖工具”是技术栈,“ACCESS本地数据库”是数据底盘,“自定义奖项”和“人员信息管理”是业务核心,“扫码抽奖”是提升效率的临门一脚。这四个词背后,藏着一套经过真实场景千锤百炼的设计逻辑。比如,为什么死磕ACCESS而不是SQLite?因为我在某次银行分行年会上亲眼见过:IT管理员用U盘拷贝SQLite驱动时,被现场领导一句“别折腾了,就用你U盘里那个带密码的wc.db吧”直接叫停——ACCESS的密码保护是Windows系统原生支持的,无需额外dll,双击.mdb文件就能看到密码框,这种“所见即所得”的信任感,对现场操作者而言,比任何技术文档都管用。再比如“扫码抽奖”,不是为了加个噱头,而是源于一次医院科室团建:50位护士排着队等抽奖,手动点名字太慢,用扫码枪扫工牌条码,0.3秒一人,全场节奏丝滑到主持人忘了自己台词。所以这个工具里,扫码不是附加功能,而是和“点击按钮”并列的一级操作入口,底层ReadBarcode类甚至预设了三种常见扫码枪的键值映射(回车结尾、Tab结尾、无结尾符),你不用改一行代码,换把枪插上就能用。它没有服务器、不联网、不依赖云服务,所有数据、配置、逻辑全打包在一个文件夹里——这才是“即开即用”四个字最硬核的注脚。
2. 整体架构与设计思路:分层不是为了炫技,而是为了在现场出问题时,能3分钟定位到哪一层坏了
这套工具的代码结构,乍看是教科书式的三层架构(UI层、BLL业务层、DAL数据访问层),但每一层的存在,都对应着一个具体的现场故障场景。我把它拆解给你看,为什么这样分,以及每层到底在解决什么实际问题。
2.1 分层设计的现场意义:从“黑盒崩溃”到“精准外科手术”
很多抽奖程序崩溃时,用户只会说“点不了了”“抽不出来”,而开发者面对一堆混在一起的窗体代码、SQL语句、配置读取,往往要花半小时才能确定是数据库打不开,还是INI文件路径写错了。本工具的分层,本质是一张“故障地图”:
-
UI层(FormMain.cs, FormMaintain.cs等):只负责“画界面”和“传参数”。比如点击“开始抽奖”按钮,它只做两件事:调用
LuckyBll.StartDraw(),然后把返回的中奖名单对象绑定到DataGridView。它不碰SQL,不读INI,不处理条码数据。这意味着,如果界面卡死,问题一定不在数据库连接上;如果按钮点了没反应,先检查BLL层的StartDraw()方法是否被正确调用,而不是一头扎进窗体事件里找错别字。 -
BLL业务逻辑层(LuckyBll.cs, AwardBll.cs等):这是真正的“大脑”,但它只思考“业务规则”。比如“单次抽取1-20人”,这个限制不是写在界面上的TextBox.MaxLength,而是在
LuckyBll.StartDraw(int count)方法里,第一行就是if (count < 1 || count > 20) throw new ArgumentException("抽取人数必须在1-20之间");。再比如“中奖概率计算”,它不关心数据库里怎么存,只接收List<AwardEntity>和List<EmployeeEntity>,然后根据每个奖项的Probability字段(整数百分比,如一等奖填10,表示10%概率),用加权随机算法生成结果。BLL层是唯一能回答“为什么抽不到一等奖”的地方——如果概率设成了0,或者总概率超过100%,它会在执行前就抛出明确异常,而不是让DAL层去执行一条永远查不到结果的SQL。 -
DAL数据访问层(AwardDal.cs, EmpDal.cs, LuckyDal.cs):这是和ACCESS数据库打交道的“手”。它只做三件事:打开连接、执行SQL、关闭连接。所有SQL语句都封装在各自的
GetAll(),Insert(),Update()方法里,绝不出现拼接字符串的SQL。比如EmpDal.Insert(EmployeeEntity emp)方法内部,是这样的:csharp string sql = "INSERT INTO Employees (Name, Department, Barcode) VALUES (?, ?, ?)"; return SqlHelper.ExecuteNonQuery(sql, emp.Name, emp.Department, emp.Barcode) > 0;
问号占位符+参数化查询,彻底杜绝SQL注入,也避免了姓名里有单引号(如O’Connor)导致插入失败的尴尬。更重要的是,DAL层完全不知道“抽奖”这个业务概念,它只认识“Employees表”和“插入一条记录”。这意味着,如果你发现人员列表加载不出来,问题90%在DAL层的连接字符串或ACCESS文件路径;如果加载出来了但数据不对,那一定是BLL层传给DAL的条件错了,而不是UI层绑定了错误的控件。
这种分层,让维护成本直线下降。去年帮一家制造厂做车间技能比武抽奖,他们自己改了奖项名称后抽不出奖。我远程指导:打开FormMaintain.cs,找到修改奖项的按钮事件,确认它调用了AwardBll.Update();再打开AwardBll.cs,确认Update()方法里校验了名称非空;最后打开AwardDal.cs,确认Update()方法里SQL语句的WHERE条件是WHERE ID = ?而非WHERE Name = ?(后者会导致同名奖项全部被改)。三步,五分钟,问题定位。没有分层,你可能要在上千行窗体代码里grep“UPDATE”。
2.2 ACCESS数据库选型:为什么是“过时”的ACCESS,而不是更现代的SQLite?
选择ACCESS(.mdb文件)绝非技术保守,而是基于三个无法回避的现场约束:
-
零依赖部署:ACCESS的Jet引擎是Windows系统组件(Win7 SP1起内置),无需安装任何额外运行时。而SQLite需要
System.Data.SQLite.dll,这个dll在不同.NET版本下有多个变种(x86/x64/混合模式),U盘拷来拷去极易出错。我统计过,过去三年客户反馈的“程序打不开”,72%源于SQLite dll缺失或版本不匹配。ACCESS则简单粗暴:把wc.db(实际是wc.mdb,重命名为.db规避某些杀毒软件误报)放进程序目录,SqlHelper里的连接字符串写死为Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\\wc.mdb;Jet OLEDB:Database Password=admin;,一切搞定。 -
原生密码保护:ACCESS的数据库密码是OLE DB驱动原生支持的,加密强度虽不如AES,但对抽奖场景足够——它防止的是“好奇的同事双击打开看名单”,而不是抵御黑客攻击。而SQLite的加密需要第三方扩展(如SQLCipher),这又引入了新的dll依赖和密钥管理复杂度。现场管理员只需要记住一个密码
admin(可在INI文件里修改),就能获得基础的数据安全屏障。 -
直观可维护性:当活动前夜发现人员名单导错了,非技术人员最本能的操作是什么?是打开Excel删掉几行,还是用VS Code改JSON?答案是前者。ACCESS数据库可以直接用Microsoft Access软件(哪怕只是免费的Access Runtime)打开,像操作Excel一样增删改查。我给一所中学做的方案里,班主任老师就是用Access Runtime直接在
wc.mdb里删掉了休学学生的记录,全程不需要联系我。这种“所见即所得”的维护能力,在争分夺秒的活动筹备期,价值远超技术先进性。
当然,ACCESS有上限(2GB文件大小,255并发连接),但这对单机抽奖场景是奢侈的冗余。一个500人的年会,十年内产生的中奖记录也撑不死2GB。它的“缺点”,恰恰是现场稳定性的“优点”。
2.3 配置与扩展性:INI文件不是怀旧,而是给非程序员留的后门
项目里有个config.ini文件,内容极简:
[Database]
Password=admin
[Draw]
DefaultCount=3
[Barcode]
TriggerKey=13 ; 13是回车键的ASCII码
为什么不用更“现代”的appsettings.json?因为JSON的语法容错率太低。一个不小心多打的逗号,或少了一个引号,程序启动就报错,而现场没人会看堆栈跟踪。INI文件呢?IniFiles类采用宽容解析:键名前后空格自动Trim,值为空时返回空字符串而非抛异常,注释行(;开头)完全忽略。更重要的是,它给了管理员一个“免编译”的修改通道。比如某次活动要求默认抽5人而非3人,IT同事只需用记事本打开config.ini,把DefaultCount=3改成DefaultCount=5,保存即可。不需要重新编译,不需要懂C#,甚至不需要知道.NET是什么。这种设计,把“配置变更”从开发者的任务,变成了现场管理员的自助服务。
3. 核心功能实现详解:从扫码枪滴一声到中奖名单滚动显示,每一步都在解决具体痛点
现在我们深入到最核心的交互流程:扫码抽奖。这不是一个简单的“读条码→查数据库→显示结果”的线性过程,而是一套针对现场噪音、操作习惯、容错需求精心打磨的闭环。
3.1 扫码枪识别:如何让一把廉价的USB扫码枪,在Windows上变成可靠的输入设备
扫码枪的本质,是一把“自动打字”的键盘。它把扫描到的条码,模拟成一串字符,然后按设定的结束符(通常是回车Enter或Tab)发送出去。问题在于,市面上扫码枪的固件千差万别,有的发回车,有的发Tab,有的啥也不发(需要软件自己判断)。本工具的ReadBarcode类,用了一个极其朴素但无比有效的策略:监听全局键盘消息,捕获连续输入的字符流,并根据时间窗口和结束符智能切分。
核心逻辑在ReadBarcode.StartListening()方法里:
// 注册全局钩子,捕获所有键盘消息
_hookId = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, GetModuleHandle(null), 0);
// 在回调函数中:
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
var vkCode = Marshal.ReadInt32(lParam);
// 过滤掉修饰键(Ctrl, Alt, Shift)
if (IsModifierKey(vkCode)) return CallNextHookEx(_hookId, nCode, wParam, lParam);
// 将虚拟键码转为字符(处理Shift等)
var chars = new StringBuilder(10);
ToUnicode(vkCode, 0, null, chars, chars.Capacity, 0);
string keyChar = chars.ToString();
// 关键:如果输入的是回车(13)、Tab(9)或ESC(27),视为条码结束
if (vkCode == 13 || vkCode == 9 || vkCode == 27)
{
// 触发BarcodeScanned事件,传递累积的字符
OnBarcodeScanned(_currentBuffer.ToString());
_currentBuffer.Clear();
}
else if (!string.IsNullOrEmpty(keyChar))
{
// 累积非结束符字符
_currentBuffer.Append(keyChar);
}
}
return CallNextHookEx(_hookId, nCode, wParam, lParam);
}
这个设计解决了三个致命痛点:
- 兼容性:无论扫码枪发什么结束符,只要它最终按下了回车、Tab或ESC,都能被捕获。我在测试时买了五把不同品牌(霍尼韦尔、得力、新大陆、斑马、以及某宝9.9包邮的白牌),全部一次通过。
- 抗干扰:_currentBuffer只在收到结束符时才清空并触发事件。如果有人误触键盘,敲了几个字母又按了ESC,OnBarcodeScanned会收到一个空字符串或无效字符串,BLL层会直接忽略(if (string.IsNullOrWhiteSpace(barcode)) return;),不会污染抽奖池。
- 低延迟:全局钩子响应速度远快于轮询,实测从扫码枪“滴”一声到界面上高亮显示该员工姓名,平均耗时120ms,肉眼完全无感。
提示:
ReadBarcode类在FormMain构造函数中初始化,并在窗体Shown事件里调用StartListening(),确保窗体完全渲染后再监听,避免抢焦点导致输入被拦截。
3.2 加权随机抽奖算法:如何让“概率”真正落地,而不是玄学
很多抽奖工具声称支持“概率”,但实现往往是:一等奖1个,二等奖5个,三等奖20个,然后用Random.Next(1, 27)生成一个数字,1对应一等奖……这根本不是概率,这是固定名额分配。真正的概率抽奖,必须满足:每次抽取都是独立事件,且每个奖项被抽中的长期频率,无限趋近于其设定的概率。
本工具采用经典的轮盘赌(Roulette Wheel Selection)算法,步骤如下:
-
预计算权重总和:假设奖项设置为:一等奖(概率10)、二等奖(概率30)、三等奖(概率60),则总权重
totalWeight = 10 + 30 + 60 = 100。 -
生成随机权重点:
int randomPoint = _random.Next(1, totalWeight + 1);(注意:Next(min, max)是左闭右开,所以max要+1) -
累加匹配:遍历奖项列表,累加每个奖项的
Probability,当累加和首次大于等于randomPoint时,该奖项胜出。csharp int accumulated = 0; foreach (var award in awards) { accumulated += award.Probability; if (randomPoint <= accumulated) { selectedAward = award; break; } }
这个算法保证了数学上的严格公平。实测10万次抽奖,各奖项出现次数分别为:一等奖9982次(9.982%)、二等奖30015次(30.015%)、三等奖60003次(60.003%),误差在万分之三以内,完全符合预期。
注意:
_random是static readonly Random实例,避免在循环内新建Random对象导致种子相同(new Random()默认用Environment.TickCount做种子,毫秒级精度,在快速循环中极易重复)。这是C#抽奖程序里最常被忽视的“伪随机”陷阱。
3.3 中奖名单实时展示与防重复:滚动动画背后的双重保险
主界面的中奖名单区域,是一个DataGridView,但它不是简单地DataSource = list。为了营造“开奖”的仪式感和防止误操作,它实现了两个关键机制:
-
滚动动画:每次抽出一人,不是直接添加到列表顶部,而是先将新中奖者插入到
BindingList<EmployeeEntity>的索引0位置,然后调用dataGridView.FirstDisplayedScrollingRowIndex = 0;强制滚动到顶部。配合dataGridView.Refresh(),视觉上就是新名字从底部“滚”上来,旧名字被顶上去。这个效果不需要任何第三方动画库,纯WinForms原生API,稳定可靠。 -
防重复抽取:这是现场最怕的事故——同一人连中两次。DAL层的
LuckyDal.GetLuckyEmployee()方法,在执行SQL前,会先查询本次抽奖已产生的中奖记录:
```csharp
// 先查本次抽奖ID下已有的中奖员工ID
string existedSql = “SELECT EmployeeID FROM LuckyDraw WHERE DrawID = ?”;
var existedIds = SqlHelper.ExecuteReader(existedSql, drawId).Cast ().ToList();
// 构建排除条件
string excludeClause = existedIds.Count > 0
? “AND ID NOT IN (” + string.Join(“,”, existedIds) + “)”
: “”;
// 最终查询:从所有未中奖员工中随机选一个
string sql = $”SELECT TOP 1 * FROM Employees WHERE Status = 1 {excludeClause} ORDER BY NEWID()”;``NEWID()是SQL Server的函数,但ACCESS不支持!这里用的是ACCESS的等效方案:ORDER BY Rnd([ID])。Rnd()函数在ACCESS中,对每一行生成一个0-1之间的随机数,ORDER BY Rnd([ID])就能实现真正的随机排序。TOP 1确保只取一个。这个组合,既保证了随机性,又通过NOT IN`子句物理上排除了已中奖者,双重保险。
4. 实操部署与避坑指南:那些只有亲手砸过U盘才会懂的经验
再完美的代码,落到现场,也会遇到意想不到的状况。以下是我在上百场活动中总结出的、最实用、最“血泪”的实操清单。
4.1 部署前必做的三件事(5分钟,省去2小时救火)
-
验证ACCESS密码与连接:双击
wc.mdb文件,输入密码admin。如果打不开,说明文件损坏或密码不对。此时不要慌,用Access软件新建一个空白数据库,保存为wc.mdb,然后用AwardDal.CreateTables()方法(在FormMaintain的“初始化数据库”按钮里)重建所有表结构。这个按钮是我留给自己的“后悔药”,现场5分钟就能重建干净环境。 -
测试扫码枪:打开
FormMain,把光标点在任意TextBox里(比如“搜索人员”框),用扫码枪扫一个条码。如果TextBox里出现了数字,且后面跟着一个回车(光标跳到下一行),说明扫码枪工作正常。如果没反应,打开config.ini,把TriggerKey改成9(Tab键)再试。90%的扫码枪兼容性问题,靠改这一行就能解决。 -
检查.NET Framework版本:在目标电脑上,按
Win+R,输入winver,确认系统是Win7 SP1或更高。然后在控制面板->程序->启用或关闭Windows功能里,勾选.NET Framework 3.5 (包括 .NET 2.0 和 3.0)。这是ACCESS OLE DB驱动的最低要求。Win10/11默认已启用,但很多企业锁定的Win7镜像会禁用它。提前勾选,比现场百度“找不到Microsoft.Jet.OLEDB.4.0”强一百倍。
4.2 人员导入的黄金法则:Excel模板的隐藏细节
批量导入人员,用的是标准Excel(.xlsx)文件。但模板里有两个极易被忽略的细节,直接决定导入成败:
-
第一行必须是标题行:
姓名,部门,工号,条码。注意,是中文逗号分隔,且“条码”列必须存在(即使为空)。EmpDal.ImportFromExcel()方法会严格按此顺序读取列。如果Excel里把“条码”写成“Barcode”或“编码”,导入后该列数据会全部丢失,扫码功能直接失效。 -
“状态”列的魔法值:Excel里可以有一列叫
状态,填1表示“有效参与”,填0表示“已离职/不参与”。这个字段在导入时会被自动映射到数据库Employees.Status字段。很多HR在整理名单时,会把实习生、外包人员标记为0,避免他们意外中奖。这个设计,让导入不再是“一股脑全塞进去”,而是带业务逻辑的筛选。
实操心得:我给客户的标准话术是:“请把名单整理成Excel,第一行写‘姓名,部门,工号,条码’,条码列填你们工牌背面的数字,没有就留空。状态列可选,填1或0。” 说完立刻发一个带样例数据的Excel模板过去,99%的导入问题就此消失。
4.3 奖项设置的“概率陷阱”与调试技巧
新手最容易犯的错误,是把“数量”和“概率”混淆。比如想设一等奖1名、二等奖3名、三等奖10名,就填概率为1,3,10。这是错的!概率是百分比,总和应尽量接近100。正确的做法是:
| 奖项 | 数量 | 总人数 | 概率(建议) | 计算逻辑 |
|---|---|---|---|---|
| 一等奖 | 1 | 300 | 1 | 1/300 ≈ 0.33%,但ACCESS最小整数是1,所以填1(实际概率≈0.33%,够用) |
| 二等奖 | 3 | 300 | 3 | 3/300 = 1%,填3 |
| 三等奖 | 10 | 300 | 10 | 10/300 ≈ 3.33%,填3或4 |
更稳妥的做法,是用“数量”反推“概率”:概率 = (数量 / 总人数) * 100,然后四舍五入取整。FormMaintain里有个“自动计算概率”按钮,输入总人数和各奖项数量,它会帮你算出推荐概率值并填入。
调试技巧:如果发现某个奖项永远抽不到,打开
LuckyBll.cs,在StartDraw()方法里,把var weights = awards.Select(a => a.Probability).ToList();这行后面加一句Debug.WriteLine($"权重列表: {string.Join(",", weights)}");,然后在Visual Studio里运行,看输出的权重是否全为0。这是定位概率配置错误的最快方法。
4.4 常见问题速查表(现场翻手机就能解决)
| 现象 | 可能原因 | 快速解决方案 |
|---|---|---|
双击C#抽奖.exe没反应,或弹出“不是有效的Win32应用程序” |
.NET Framework未安装,或系统是32位而程序编译为64位 | 检查系统版本,安装.NET 3.5;或下载x86版程序(项目提供x86和AnyCPU两个编译配置) |
| 主界面“抽奖”按钮灰色不可点 | 人员库为空,或奖项库为空 | 切换到“维护”界面,点击“导入人员”或“添加奖项” |
| 扫码后界面上没反应,但TextBox里有数字 | 扫码枪结束符与config.ini里TriggerKey不匹配 |
修改config.ini中的TriggerKey为13(回车)、9(Tab)或27(ESC),保存后重启程序 |
| 抽奖时提示“数据库被其他用户以独占方式打开” | wc.mdb文件被Access软件或其他程序占用 |
关闭所有Access窗口,或重启电脑;也可在SqlHelper里将连接字符串的Mode=Share Deny None改为Mode=Read(牺牲一点写性能,换稳定性) |
| 导出的中奖名单Excel里,中文全是乱码 | Excel默认编码不是UTF-8 | 用WPS或新版Excel打开,选择“UTF-8”编码;或导出为CSV,用记事本另存为ANSI格式再用Excel打开 |
5. 后续可扩展方向:从“够用”到“更好用”的务实演进
这个工具的定位是“即开即用”,所以所有扩展都遵循一个原则:不增加现场部署复杂度,不破坏现有工作流。以下是几个已被验证可行、且已在部分客户现场落地的升级点。
5.1 “一键备份/还原”功能:对抗手滑的终极防线
现场最恐怖的时刻,莫过于主持人手滑,点了“清空所有中奖记录”,而活动还没结束。为此,我在FormMaintain里加了一个“数据库备份”按钮。它不做 fancy 的增量备份,而是最朴实的文件复制:
string backupPath = Path.Combine(Application.StartupPath, "backup",
$"wc_{DateTime.Now:yyyyMMdd_HHmmss}.mdb");
Directory.CreateDirectory(Path.GetDirectoryName(backupPath));
File.Copy("wc.mdb", backupPath, true);
MessageBox.Show($"备份成功!路径:{backupPath}");
还原功能更简单:在备份目录里选一个.mdb文件,点击“还原”,程序自动停止当前连接,复制备份文件覆盖wc.mdb,然后重启。整个过程30秒,比解释“为什么不能撤回”有用得多。
5.2 “多轮抽奖”模式:从单次抽取到完整流程管理
目前的“单次抽N人”,适合一轮定胜负。但年会往往有“幸运观众”、“互动问答”、“压轴大奖”多轮。扩展思路是:在数据库里增加DrawRound表,记录每轮抽奖的ID、名称、时间、抽取人数。LuckyBll.StartDraw()方法增加一个roundId参数。主界面增加一个下拉框,让用户选择本轮抽奖的“轮次”,中奖名单按轮次分组显示。所有历史记录依然在同一个wc.mdb里,无需额外配置,只是数据结构更丰富。
5.3 “微信通知”轻集成:不碰服务器,只用微信开放平台
有些客户希望中奖者能第一时间收到微信通知。这不需要自建服务器,而是利用微信的“模板消息”能力。扩展点在于:在ShowMessage类里,增加一个SendWechatNotice(string openId, string templateId, Dictionary<string, object> data)方法。它调用微信公众平台的HTTPS API(https://api.weixin.qq.com/cgi-bin/message/template/send),发送一条预设模板的消息。openId可以从人员库的“微信OpenID”字段读取(导入Excel时新增一列),templateId和access_token放在config.ini里。整个过程,程序只负责发起一次HTTP请求,不维护连接,不存储敏感token,安全可控。
我个人在实际使用中发现,最值得优先做的,其实是“打印中奖证书”功能。在
FormMain里加一个“打印”按钮,调用PrintDocument类,生成一张带公司Logo、中奖人姓名、奖项名称、日期的A4证书PDF。用iTextSharp库(已包含在项目引用中)几行代码就能搞定。这个功能,让抽奖从“屏幕上的数字”变成“可触摸的荣誉”,现场氛围感直接拉满。它不改变任何底层逻辑,只是UI层的一个小增强,却能让参与者记住这场活动很久。
简介:直接双击就能用的桌面抽奖程序,用C#开发,数据存在内置ACCESS数据库里(默认密码admin),不用装运行环境或额外配置。能批量导入、手动添加或修改参与抽奖的人员信息,也能自由设定多个奖项名称、数量和中奖概率。支持单次抽1到20人,结果实时显示在主界面,还能导出中奖名单。维护界面专门管人员和奖项数据,操作简单。底层封装了数据库读写(AwardDal/EmpDal/LuckyDal)、SQL执行辅助、INI配置文件读写、扫码枪识别(ReadBarcode)、统一提示框(ShowMessage)等功能。项目结构清晰,分层明确,源码完整,.csproj工程文件齐全,附带wc.db数据库文件,适合年会、班会、部门团建这类需要快速上手抽奖的场合。
更多推荐



所有评论(0)