相关文章推荐
大气的手电筒  ·  GETUTCDATE ...·  7 月前    · 
冷冷的小笼包  ·  深入理解Java ...·  1 年前    · 
VBA学习笔记62: 窗体与控件综合实例

VBA学习笔记62: 窗体与控件综合实例

2 年前 · 来自专栏 VBA学习笔记(完结)

学习资源:《Excel VBA从入门到进阶》第62集 by兰色幻想


窗体的内容已经学习完了,这节试着用前几节窗体的内容来把入库单弄成窗体吧。

一、列举想实现的功能:

  1. 出库日期选择——添加日期控件:DTPicker
  2. 出库单号码——点击微调项可以递增或递减文本框的号码,和当号码不输入时,不能离开该文本框。
  3. 在商品代码里按回车或点击右侧的按钮可以打开价格表窗口。
  4. 价格表需要在窗体加载时即完成treeview控件中的单价信息导入,同时也要完成listview控件的标题行生成。
  5. 用鼠标单击或者按回车键后,可以把商品名称、型号以及单价输入到相应的文本框里,然后关闭该窗口回到输入主界面,并且焦点转向销售数量后面文本框里。
  6. 当输入数量时,自动计算销售金额。输入完成按回车,可以自动把本条信息导入到下面的listview中,并且焦点转到商品代码框中。
  7. 双击listview可以清空所有记录;右键单击可以提示是否删除选中的记录,如果选是则删除选中的记录。
  8. 点击输入,可以把listview中的数据添加到出库表中。出库单号码自动+1,焦点转到商品代码框中。


二、代码详解:

  1. 出库日期选择——添加日期控件:DTPicker

不多说,回顾点击以下链接,直接添加日期控件:DTPicker即可。

夏西蓝:VBA学习笔记61: 日期控件与进度条

运行演示


2. 出库单号码——点击微调项可以递增或递减文本框的号码,和当号码为输入时不能离开该文本框。

微调按钮的上下幅度通过Smallchange属性设置,然后数值显示用的是SpinDown事件和SpinUp事件。(复习链接↓)

夏西蓝:VBA学习笔记58:选项按钮、复选框、框架、滚动条、微调、图像与多页

不允许离开,则是Exit事件,判断语句,如果号码的文本框值为空,就禁止离开。

① SpinDown事件:点击向下的按钮出库单号码在原来的基础上+1。

Private Sub SpinButton1_SpinDown() 
   TextBox2.Text = Format(Val(TextBox2) + 1, "000") '点击向下的按钮出库单号码在原来的基础上加1
End Sub

② SpinUp事件:点击向上的按钮出库单号码在原来的基础上-1。

Private Sub SpinButton1_SpinUp()
   TextBox2.Text = Format(Val(TextBox2) - 1, "000") '点击向上的按钮出库单号码在原来的基础上减1
End Sub

③ Exit事件:出库单号码强制输入,否则不允许离开。

Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean) '出库单号码强制输入,否则不允许离开
    If TextBox2.Text = "" Then
        Cancel = True '取消离开操作
    End If
End Sub


3. 回车或点击按钮打开价格表窗口

价格表窗口用多页控件设置,因为多页控件的值从0开始,所以出库单页面是0,而价格单的页面就是1。

运行演示

回车按钮的值是13,用keydown事件判断按钮的值,确认keycode为13后,跳转到价格单页面(1)。

Private Sub TextBox3_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    If KeyCode = 13 Then
        Me.MultiPage1.Value = 1
    End If
End Sub

单击打开价格单的话,就是单击事件了。

Private Sub CommandButton3_Click() '点击按钮打开单价窗口
    Me.MultiPage1.Value = 1
End Sub
运行效果,选取值返回是第五个功能,代码在下面


4. 价格表需要在窗体加载时即完成treeview控件中的单价信息导入,同时也要完成listview控件的标题行生成。

窗体加载时,listview都已生成,用UserForm_Initialize事件,这里设置listview的标题行、格式、多页控件的显示等。

夏西蓝:VBA学习笔记59-1: listview控件

Private Sub UserForm_Initialize()
'Dim ITM As ListItem
ListView1.ColumnHeaders.Add 1, , "销售日期", ListView1.Width / 8 '设置第1列
ListView1.ColumnHeaders.Add 2, , "出库单号", ListView1.Width / 8, lvwColumnCenter '设置第2列
ListView1.ColumnHeaders.Add 3, , "商品代码", ListView1.Width / 8, lvwColumnCenter '设置第3列
ListView1.ColumnHeaders.Add 4, , "商品名称", ListView1.Width / 8, lvwColumnCenter '设置第4列
ListView1.ColumnHeaders.Add 5, , "型号", ListView1.Width / 8, lvwColumnCenter '设置第5列
ListView1.ColumnHeaders.Add 6, , "销售数量", ListView1.Width / 9, lvwColumnCenter '设置第6列
ListView1.ColumnHeaders.Add 7, , "销售单价", ListView1.Width / 8, lvwColumnCenter '设置第7列
ListView1.ColumnHeaders.Add 8, , "销售金额", ListView1.Width / 8, lvwColumnCenter '设置第8列
ListView1.View = lvwReport '设置为报告格式
ListView1.Gridlines = True '显示表格线
ListView1.FullRowSelect = True '可以选取整行
ListView1.MultiSelect = True
Call 添加Treeview数据
Me.MultiPage1.Value = 0 '显示输入界面
Me.MultiPage1.Style = 2 '隐藏选项卡
End Sub

在上面我们也看到,价格表准备用treeview来展示,所以这里也需加载treeview的内容。为方便查看,上面用call语句来引用Treeview数据。这里用到的知识点是treeview的数据导入。

夏西蓝:VBA学习笔记60-1: Treeview控件

价格表的数据放在价格表.sheet里面。

Sub 添加Treeview数据()
Dim Nodx As Node
Dim arr, d As New Dictionary
Dim mykey, sr, x
TreeView1.ImageList = ImageList1  '从imagelist控件中提取图片
arr = Sheets("价格表").Range("A2:D" & Sheets("价格表").Range("a65535").End(xlUp).Row)
For x = 1 To UBound(arr)
    mykey = arr(x, 1) & "," & arr(x, 2) & "," & arr(x, 3) & "," & arr(x, 4) 
    '把商品所有信息连接起来,后面放在key里存放,以便随时调用    
    sr = arr(x, 3) & "(" & arr(x, 1) & ") 价格:" & arr(x, 4)
    '设置节点显示的内容 
    If Not d.Exists(arr(x, 2)) Then '如果该顶级节点不存在
        d(arr(x, 2)) = "" '添加到字典里,以便下次判断是否存在
        Set Nodx = TreeView1.Nodes.Add(, , arr(x, 2), arr(x, 2), 1, 1) '添加顶级节点
        Set Nodx = TreeView1.Nodes.Add(arr(x, 2), tvwChild, mykey, sr, 2, 2) '添加子节点
        Nodx.EnsureVisible '打开节点
        Set Nodx = TreeView1.Nodes.Add(arr(x, 2), tvwChild, mykey, sr, 2, 2) '添加子节点
    End If
Next x
End Sub
添加数据后的treeview


5. 用鼠标单击或者按回车键后,可以把商品名称、型号以及单价输入到相应的文本框里,然后关闭该窗口回到输入主界面,并且焦点转向销售数量后面文本框里。

①单击事件

Private Sub TreeView1_Click() '单击事件
Dim MyItem As Node
Set MyItem = TreeView1.SelectedItem 'SelectedItem正在选取的节点
If MyItem.Parent Is Nothing Then '不能选取一级节点
    Exit Sub
    '从key取出值折分开分别放在四个文本框内
    TextBox3.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(0) 
    TextBox4.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(1)
    TextBox8.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(2)
    TextBox6.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(3)
    Me.MultiPage1.Value = 0 '跳转回入库单界面
    TextBox5.SetFocus '光标移动至到销售数量文本框
End If
End Sub

②回车事件

Private Sub TreeView1_KeyDown(KeyCode As Integer, ByVal Shift As Integer) '按钮事件
Dim MyItem As Node
Set MyItem = TreeView1.SelectedItem 'SelectedItem正在选取的节点
If KeyCode = 13 Then '点击回车键
    If MyItem.Parent Is Nothing Then '不能选取一级节点
        Exit Sub
        '从key取出值折分开分别放在四个文本框内
        TextBox3.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(0)
        TextBox4.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(1)
        TextBox8.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(2)
        TextBox6.Text = Split(Me.TreeView1.SelectedItem.Key, ",")(3)
        Me.MultiPage1.Value = 0 '跳转回入库单界面
        TextBox5.SetFocus '光标移动至到销售数量文本框
    End If
End If
End Sub
运行效果


6. 当输入数量时,自动计算销售金额。输入完成按回车,可以自动把本条信息导入到下面的listview中,并且焦点转到商品代码框中。

①首先是change事件,输入数量就自动计算金额。

Private Sub TextBox5_Change()
  TextBox7.Value = Val(TextBox5) * Val(TextBox6.Value) '输入数量时自动计算金额
End Sub

②输入完成,按回车,把数据导入listview中

Private Sub TextBox5_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Dim lv As ListItem
'按回车且文本框数值不为0
If KeyCode = 13 And TextBox5 <> "" Then
    '向listview中框添加记录
    With ListView1
        Set lv = .ListItems.Add
        lv.Text = DTPicker1.Value
        lv.SubItems(1) = TextBox2.Text
        lv.SubItems(2) = TextBox3.Text
        lv.SubItems(3) = TextBox4.Text
        lv.SubItems(4) = TextBox8.Text
        lv.SubItems(5) = TextBox5.Text
        lv.SubItems(6) = TextBox6.Text
        lv.SubItems(7) = TextBox7.Text
        TextBox5 = ""
        TextBox3 = ""
        TextBox3.SetFocus
    End With
End If
End Sub
运行演示


7. 双击listview可以清空所有记录;右键单击可以提示是否删除选中的记录,如果选是则删除选中的记录。

① 双击事件,双击后进行询问再次确认以免误删。

Private Sub ListView1_DblClick() '为listview控件增加清空所有行的功能
    If MsgBox("你要清空所有行吗", vbOKCancel) = vbOK Then
       ListView1.ListItems.Clear '用ListItems对象的clear方法可以清空所有行
    End If
End Sub

② VBA中没有右键单击事件,但我们可以用点击事件+按钮判断(按右键是2)。同样删除前最后先进行询问,以免误删。

Private Sub ListView1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
    If Button = 2 Then
      If MsgBox("你要删除选取的行吗", vbOKCancel) = vbOK Then
         ListView1.ListItems.Remove ListView1.SelectedItem.Index  '用ListItems对象的clear方法可以清空所有行
      End If
    End If
End Sub
运行演示


8. 完成整张出库单的输入,点击“输入”按钮,可以把listview中的数据添加到出库表中。出库单号码自动+1,焦点转到商品代码框中。

Private Sub CommandButton1_Click() '把listview列表中的数据输出到工作表中
Dim arr()
Dim icount As Integer, y As Integer, x
icount = ListView1.ListItems.Count 'ListItems.Count 返回总行数
ReDim arr(1 To icount, 1 To 8)
For x = 1 To icount '把数据导入至数组
    arr(x, 1) = ListView1.ListItems(x).Text  '把listview第1列(text)放在数组第一列
    For y = 1 To 7
        arr(x, y + 1) = ListView1.ListItems(x).SubItems(y)
     Next y
Next x
Range("a65536").End(xlUp).Offset(1, 0).Resize(icount, 8) = arr