上一篇说到,我得到了员工的嘱托和领导的信任,临危受命制作一款公平抽奖软件。
当时我就竖起手指敬了个礼,饱含热泪地接下了任务,带着这么多人的期盼,我得加把劲放手干不是吗!
OK,上一篇中程序的基本架子有了,那接下来就要进一步完善强化某些功能了。
(文末有安装包下载,分享给小伙伴儿们使用哦!)
一、界面控件布局要美丽
抽奖是个高兴的事儿,即便没中奖人人也乐得参与,万一要中了呢,玩的就是个心跳。
既然大家高高兴兴的来,那就不能扫了大家的兴致,这抽奖界面的模样怎么也得打扮得好看点儿。
背景图片那自不用多说,来张大红的,特喜庆的那种。
虽然都是红的,但考虑到各人喜好不同,可以在程序目录中放上一张文件名为 Background.jpg
的图片文件作为自定义背景图片,程序启动自动加载哦。
你说你还要自己去找图片,麻烦不,我这就有啊!
好吧,我认识的小伙伴儿们有几个得了懒癌,都是我给惯的,那就再让我惯你们一次吧。
我也是从网上扯了几张图片,放在文末自行下载吧。
除了背景图这张脸,还有就是脸上的五官了。
为了更加方便地调整五官的位置,程序上开了个菜单,点击 手动移动控件
这一菜单项即可启用或禁用这项功能。
启用后就可以用鼠标拖动界面上的控件,嘿嘿,想放哪儿就放哪儿,爽不爽?
拖一次爽一次,一直拖一真爽,不过拖完了别忘记再点一次菜单项禁用拖拽功能。
当然,我相信你们之中肯定隐藏着精致强迫症患者,好吧,那还可以用数字坐标来调整控件位置。
二、最简单有效的数字滚动效果
抽奖时要有动态效果,我们不搞复杂的,最简单的就是数字滚动了。
不用多说,这个等待摇奖停止的动态滚动过程才是最让人激动和期待的,完全有一种瞬间让人感觉生活还是有点儿盼头的哈。
具体怎么做,往下看!
先定义一个 Timer
控件,命名为 tmrLuckDraw
,然后随机生成11位数字,显示到标签 lblCardID
上。
当 tmrLuckDraw.Interval = 1
时即可触发滚动效果,这个数值越大,数字滚动得越慢。
' RndNumCardId 函数产生11位数字字符串
Dim strRndCircle As String
strRndCircle = RndNumCardId(999999999)
lblCardId.Caption = strRndCircle
lblCardId.Refresh
这里的数字滚动设定为11位的手机号码,当然这一项也可以改为员工的工号或其它可以唯一标识员工的字串。
如果需要修改成其它位数的字串,可以联系我定制,人民币是我大哥,他让我干啥我就干啥!
三、用快捷按键控制抽奖的开始和停止
鼠标点击抽奖比较LOW,也不好控制,用按键才是正道。
而且,注意,开始和暂停的按键最好不要相同,因为你多按一下人就有可能会凌乱,还是分开好,垃圾都讲究分类了不是。
另外,当窗体非聚焦状态下,定义在窗体上的按键可能会不起作用,所以建议用全局消息定义和处理按键。
具体可分两步,第一步先定义按键,第二步再在系统消息函数中写上相应按键的处理代码。
在主窗体的 Load
事件中定义相应的快捷按键。
开始抽奖为 F11
键,ID为1;暂停抽奖为 空格
,ID为2。
Dim ret As Long
'记录原来的window程序地址
preWinProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)
'用自定义程序代替原来的window程序
ret = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf Wndproc)
'开始抽奖
idHotKey = 1 ' ID为1
Modifiers = 0 ' 组合键使用加号连接,此处为空(MOD_SHIFT,MOD_ALT,MOD_CONTROL)
uVirtKey = vbKeyF11 ' F11键
ret = RegisterHotKey(Me.hwnd, idHotKey, Modifiers, uVirtKey)
'暂停抽奖
idHotKey = 2 ' ID为2
Modifiers = 0 ' 组合键使用加号连接,此处为空(MOD_SHIFT,MOD_ALT,MOD_CONTROL)
uVirtKey = vbKeySpace ' 空格键
ret = RegisterHotKey(Me.hwnd, idHotKey, Modifiers, uVirtKey)
快捷按键的处理,通过以下 Wndproc
函数调用,通过前面定义的ID来判断按键。
Public Function Wndproc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If Msg = WM_HOTKEY Then
Select Case wParam
Case 1
' 抽奖开始的处理代码...
Case 2
' 抽奖暂停的处理代码...
End Select
End If
'如果不是热键信息则调用原来的程序
Wndproc = CallWindowProc(preWinProc, hwnd, Msg, wParam, lParam)
End Function
四、抽奖过程中断了怎么办
除了中奖本身还有一种情况让人感觉激动,那就是好好抽着奖,它居然死机了!
重新打开一看,好么,啥都没了!
前面抽一半的还算数吗,奖品都领了,你说这闹不闹心!
别着急,不怕死机、停电,我这儿有办法!
一个简单的办法,就是在重新加载奖项时,先判断是否存在上一次的抽奖结果。
如果存在(比如抽奖剩余数大于1),则表示上一次抽奖过程中断,那么可以给出两种选择。
一种是继续上一次抽奖,另一种是重置上一次结果,开始新一轮的抽奖。
其实你仔细研究就会发现,这两种选择其实有先后顺序,完全可以合并为一种。
那么就简单了,一旦抽奖过程遭遇中断,重启程序后自动继续上一次抽奖过程。
如果不想继续,那么完全可以重置结果从头来过。
objIteSql.p_Sql = "SELECT luckdraw FROM result_temp WHERE counts_rest > 0"
If objIteSql.SelectSQL = True Then
If objIteSql.p_RecordCount > 0 Then
'上次抽奖未结束,则自动导入剩余的奖项和计数
End If
End If
五、如何做到奖项任意抽取
一般情况下,抽奖总是按奖项先低后高的顺序抽取的。
但你是知道的,这仅仅是一般情况,世事无常,总会有特殊情况的嘛。
那特殊情况是个啥?
比如有人喜欢先抽三等奖,再抽五等奖,最后抽一等奖。
哈哈,有这么奇葩吗?
哎,你还别说,这只是初级奇葩哦!
高级奇葩的需求是,先抽三等奖3名,再抽五等奖5名,最后抽一等奖1名。
噗~一口农夫山泉喷出,一点儿也不甜!
说奇葩其实也不能说有啥奇怪的,因为有时的确不一定按固定顺序抽奖。
比如中间临时要补个奖项呢?
想想复杂,做起来...好像也没那么简单好不好!
WELL,我擦擦汗水(眼泪)尽量做就是了。
放几个 ComboBox
下拉框,再放几个文本框,分别用来显示奖项、剩余数量、已抽取数量和每次可抽取数量。
在开始每一次抽奖前,可自由选择奖项和抽取数量。
按下暂停按键后,程序通过这些项目和数量来进一步判断和计算。
代码太多,就不放上来了。
六、数据如何导入,抽奖结果又如何导出来呢
数据导入就两样,一个是人员信息,包括编号(手机号)、姓名、部门啥的,还一个是奖项信息,包括名称和数量。
数据库使用 SQLite
,速度更快,关键是断电不丢数据啊!
前文参考:
接着,使用 MSFlexGrid
控件来填充结果数据。
这个控件是微软出品,比较原始,所以导出速度并不理想,但作为抽奖结果没几条数据,用用还算凑合。
以后需要改进的话,可以使用 MSHFlexGrid
或其它更高级的表格控件。
Dim strSql As String
strSql = "SELECT a.cardid, a.luckdraw, a.datetimes, b.username, b.department FROM result AS A LEFT OUTER JOIN members AS B ON a.cardid=b.cardid"
msfgResult.Redraw = False
msfgResult.Tag = ""
Call fillGridScanlog(msfgResult, strSql)
msfgResult.Redraw = True
' MSFlexGrid 填充数据函数
Private Sub fillGridScanlog(grid As MSFlexGrid, Optional strSql As String)
Dim strRowData As String, rowindex As Integer
rowindex = 1
With grid
If Len(.Tag) = 0 Then
.Rows = 1
.Cols = 6
.Row = 0
.Col = 0
.Text = "NO"
.ColWidth(0) = 400
.ColAlignment(0) = flexAlignCenterCenter
.Col = 1
.Text = "编号"
.ColWidth(1) = 1200
.ColAlignment(1) = flexAlignLeftCenter
.Col = 2
.Text = "姓名"
.ColWidth(2) = 1200
.ColAlignment(2) = flexAlignLeftCenter
.Col = 3
.Text = "部门"
.ColWidth(3) = 1500
.ColAlignment(3) = flexAlignLeftCenter
.Col = 4
.Text = "奖项"
.ColWidth(4) = 3000
.ColAlignment(4) = flexAlignLeftCenter
.Col = 5
.Text = "时间"
.ColWidth(5) = 2400
.ColAlignment(5) = flexAlignLeftCenter
Dim objIteSql As New cls_iteSql
objIteSql.p_DbFilePath = strDbPath
objIteSql.p_CheckSql = False
objIteSql.p_Sql = strSql
If objIteSql.SelectSQL = True Then
Dim j As Long
Dim intRecordCount As Long
intRecordCount = objIteSql.p_RecordCount - 1
For j = 0 To intRecordCount
strRowData = rowindex & vbTab
strRowData = strRowData & Trim(objIteSql.p_RecordSetValue(j, 0)) & vbTab
strRowData = strRowData & Trim(objIteSql.p_RecordSetValue(j, 3)) & vbTab
strRowData = strRowData & Trim(objIteSql.p_RecordSetValue(j, 4)) & vbTab
strRowData = strRowData & Trim(objIteSql.p_RecordSetValue(j, 1)) & vbTab
strRowData = strRowData & Trim(objIteSql.p_RecordSetValue(j, 2)) & vbTab
.AddItem strRowData
rowindex = rowindex + 1
Next
End If
Set objIteSql = Nothing
.Tag = 1
End If
End With
End Sub
七、公平抽奖真的公平吗
到最后了,重点差点忘了说,这最终应该是一个公平抽奖。
公司两千多号人,怎么做到公平抽奖呢?
我们不扯那些什么正态分布啥的概念,我们就用史上最简单最单纯的算法,像洗牌一样,随机洗牌,一次不行那就多洗几次。
说直白一点儿就是,把公司员工的手机号(或编号)排列成一个一维数组,然后从数组的第一位开始,将当前位的号码与其他随机位置的号码互换,然后第二位与随机位置号码互换,以此类推直至最后一位。
这样就等于洗牌了嘛,当然,一次肯定不够,我设定最少洗10次,这样顺序就打得比较乱了。
然后实际抽奖时,随机从已经打乱顺序的数组中选取一个,那么就可以实现随机中奖了。
当然在这个过程中,已经中奖的人员事先会被剔除数组列中的。
' rstTmpdata 是人员数组,intShuffle 是洗牌次数
For intCC = 0 To intShuffle
lngRecordCount = UBound(rstTmpdata) - 1
' 从头至尾,当前位置与随机位置互换
For j = 0 To lngRecordCount
intPos = RndNum(lngRecordCount + 1)
rTpData = rstTmpdata(intPos)
rstTmpdata(intPos) = rstTmpdata(lngRecordCount)
rstTmpdata(lngRecordCount) = rTpData
lngRecordCount = lngRecordCount - 1
Next
Next
由于人员顺序是打乱的,抽取的时候也是在这打乱的基础上随机抽取的,所以说够公平了吧。
如果你觉得打得还不够乱不够随机,程序里你可以指定洗牌次数,不过最好不要洗太多次,因为程序速度可能会受影响。
鉴于更公平的原则,洗牌和随机抽取都是在按下暂停时才开始计算的。
也就是说,在开始抽奖(数字开始滚动)时,并没有已经产生中奖者,而是按下暂停键后才开始洗牌、开始抽取中奖者。
并且需要强调的是,每抽取一次中奖就随机洗牌一轮(多次),保证每次数组的排列是不同的,够公平不?
最后,会员免费下载、免费注册、免费使用
程序经过多年的测试和实际使用,效果还是不错的,得到了广大员工和领导的肯定,问题是到现在我怎么还没有中过奖呢?!
好吧,在这儿免费分享程序给小伙伴儿们,未注册版限制10人,但是可以免费注册!
看下面这行字~
关注公众号@网管小贾,发送 xjluckdraw
免费获取注册码,注册后没有任何使用限制了。
欢迎留言区三连,谢谢!
安装包免费下载链接(文章ID在二维码下方附近)
Setup_XJLuckDraw.exe (3.81M)
下载链接:https://pan.baidu.com/s/1lx9esi_jrMSA07GrDzlfEw
提取码:
★扫码关注公众号, 发送【000791】获取阅读密码
背景图片打包下载
60个年会抽奖背景.7z (15.81M)
下载链接:https://pan.baidu.com/s/1gxJvDlx6fm2IuIkx7PjC1Q
提取码:
★扫码关注公众号, 发送【000791】获取阅读密码
上一篇链接:如何手工打造一款公平的年会抽奖软件(一)
WeChat@网管小贾 | www.sysadm.cc