本文于2023年5月15日首发于本人同名公众号,更多文章案例请关注微信公众号:Excel活学活用! ☆本期内容概要☆ 由于是从别的应用中复制过来的用户窗体,有部分代码没有删除干净,待后续调整,目前仅有限测试通过。 ☆猜你喜欢☆ Excel VBA 这样酷炫的日期控件,你不想要吗? Excel 公式函数/数据透视表/固定资产折旧计提表! Excel VBA 自定义函数/数组字段定位/数组字段排序 Excel 功能/公式函数/VBA/多种姿势处理重复值 Excel VBA 最简单的收发存登记系统 Excel 公式函数/查找函数之LOOKUP Excel VBA 文件批量改名 Excel 公式函数/数据验证/动态下拉列表 Excel VBA 输入逐步提示/TextBox+ListBox Excel 基础功能【数据验证】,你会怎么用? 本文于2023年5月15日首发于本人同名公众号,更多文章案例请关注微信公众号:Excel活学活用!Private Declare PtrSafe Function SendMessageLong Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As LongPtr, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Private Declare PtrSafe Function GetScrollPos Lib "user32" (ByVal hwnd As LongPtr, ByVal nBar As LongPtr) As LongPtr
Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hDC As LongPtr) As LongPtr
Private Const LVM_FIRST = &H1000
Private Const LVM_SCROLL = (LVM_FIRST + 20)
Private Const SB_HORZ = 0
Private Const LOGPIXELSX = 88
Private EditableCol As String '窗体初始化时指定可以编辑的列号,如"01/03/10"
Private EditableField As String '可编辑表头字段,根据它来转化成EditableCol
Private strRequiredCol As String '必填列,如"01/03/10",数据库中自动编号不能设置
Private strRequiredField As String '必填字段,根据它来转化成strRequiredCol
Private sngPixelPerPoint As Double '每像素的磅数,窗体初始化时计算一次即可
Private intCol As Integer '记录ListView第几列被点击,Listview标题索引从1开始
Private blnFlag As Boolean '按下Escape键时,指示InkEdit1_Exit事件不保存修改
'Private blnNewItem As Boolean '新增一行标识符。如果新增行未保存或未删除,该标识为TRUE
Private strOriginal As String '记录每次显示InkEdit时的原始值,用于其退出时的比较
Private arrData As Variant '数据数组,如果连接数据库,请使用ADO的Recordset对象
Dim p As Long
Dim SortType As Integer
Dim iTotal As Double
Dim DicMonth
Dim aData()
Dim iRow
Dim iCol
Dim tbTitle(), sTbtitle()
Dim arrStr() As String
Dim ItemStr As String
Dim ModifyStatus As Integer '修改状态,点
Dim DeleteStatus As Integer '删除状态,记录是否有删除动作
Dim arrModifyCode() '修改的科目代码
Dim arrModifyItems()
Dim arrOldItems(), arrNewItems()
Dim LvItem As ListItem
Dim arrWidth()
Dim arr(), arrType() 'Usf_Interm 中组合框数据源
Dim preDate As Date
Dim preColor
Dim preNumber As Integer
Dim intRow As Integer 'selecteditems的行号
'Dim AccCode As String, AccName As String
Dim CheckBoxStatus As Boolean
Dim strDeletedId As String
Dim strDeletedAccCode As String
Dim initSQL As String 'listview初始化数据的sql,在保存后再调用重新加载数据
Dim strModifiedID As String
Dim intStrikeTimes As Integer '记录Esc键的按键次数
Dim lastEscapeTime As Single '记录第一次按下ESC的时间
Private Sub Cmd_Exit_Click()
If ModifyStatus > 0 Or DeleteStatus > 0 Then
If Not wContinue("所有未保存的操作将丢失!") Then Exit Sub
End If
Call RestoreAPI
ModifyStatus = 0
DeleteStatus = 0
Unload Me
End Sub
Private Sub AddNewItem(Optional ByVal AddPos As String = "end")
Dim IDX As Integer
If ShiftKeyPressed Then
If AddPos = "before" Then
AddPos = "after"
ElseIf AddPos = "after" Then
AddPos = "before"
End If
End If
If Me.LvDetail.ListItems.Count = 0 Then
IDX = 1
Else
If AddPos = "end" Then
IDX = Me.LvDetail.ListItems.Count + 1
ElseIf AddPos = "top" Then
IDX = 1
ElseIf AddPos = "before" Then
If Me.LvDetail.SelectedItem.index = 1 Then
IDX = 1
Else
IDX = Me.LvDetail.SelectedItem.index - 1
End If
ElseIf AddPos = "after" Then
IDX = Me.LvDetail.SelectedItem.index + 1
Else
IDX = Me.LvDetail.ListItems.Count + 1
End If
End If
'根据指定字段转化可编辑列、必填列
With Me.LvDetail
For i = 1 To .ColumnHeaders.Count
If InStr(EditableField, "All") Then
If .ColumnHeaders(i) <> "ID" Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
ElseIf InStr(EditableField, "Except") Then
If .ColumnHeaders(i) <> "ID" And InStr(EditableField, .ColumnHeaders(i)) = 0 Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
Else
If InStr(EditableField, .ColumnHeaders(i)) Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
End If
Next
End With
'Stop
With Me.LvDetail
Set LvItem = .ListItems.Add(IDX, , "")
If currTable = "tb凭证" Then
LvItem.SubItems(7) = 0: LvItem.SubItems(8) = 0
LvItem.SubItems(3) = .ListItems(.ListItems.Count - 1).SubItems(3)
ElseIf currTable = "tb期初余额" Then
LvItem.SubItems(1) = CDate(currYear & "/1/1")
LvItem.SubItems(2) = "期初余额"
LvItem.SubItems(7) = 0
End If
.ListItems(IDX).EnsureVisible
End With
ModifyStatus = ModifyStatus + 1
End Sub
Private Sub CmdAddNew_Click()
Call AddNewItem("after")
End Sub
Private Sub CmdChangeColWidth_Click()
Dim lvWidth As Double
If Me.CmdChangeColWidth.Caption = "解冻列宽" Then
Me.FrmHeader.Visible = False
Me.LvDetail.HideColumnHeaders = False
Me.LvDetail.Top = Me.FrmHeader.Top
Me.CmdChangeColWidth.Caption = "固定列宽"
ElseIf Me.CmdChangeColWidth.Caption = "固定列宽" Then
Me.FrmHeader.Visible = True
With Me.LvDetail
For i = 1 To .ColumnHeaders.Count
.ColumnHeaders(i).Width = arrWidth(i - 1)
lvWidth = lvWidth + arrWidth(i - 1)
Next
.HideColumnHeaders = True
.Top = Me.FrmHeader.Top + Me.FrmHeader.Height
.Width = lvWidth
If currTable = "tb凭证" Then
Me.Width = lvWidth + 20 + 20
Else
Me.Width = lvWidth + 20
End If
End With
Me.CmdChangeColWidth.Caption = "解冻列宽"
Me.CmdChangeColWidth.Left = Me.LvDetail.Left + Me.LvDetail.Width - Me.CmdChangeColWidth.Width
Me.CmdChangeWidth.Left = Me.CmdChangeColWidth.Left - Me.CmdChangeWidth.Width - 2
Me.Frame3.Left = Me.LvDetail.Left + Me.LvDetail.Width - Me.Frame3.Width
End If
End Sub
Private Sub CmdCopyRecord_Click()
Call AddNewItem("after")
For j = 1 To LvDetail.ColumnHeaders.Count - 1
LvItem.SubItems(j) = Me.LvDetail.SelectedItem.SubItems(j)
Next
End Sub
Private Sub CmdDateDown_Click()
Dim temDate
If Me.TxbDate = "" Then Exit Sub
preMonth = Month(CDate(Me.TxbDate))
temDate = CDate(Me.TxbDate) - 1
If VoucherProcType = "凭证修改" Then
If Month(temDate) <> preMonth Then
Me.TxbDate = temDate + 1
Else
Me.TxbDate = temDate
End If
Else
If Year(temDate) < Val(currYear) Then
Me.TxbDate = CDate(currYear & "/1/1")
Else
Me.TxbDate = temDate
End If
End If
End Sub
Private Sub CmdDateUp_Click()
Dim temDate
If Me.TxbDate = "" Then Exit Sub
preMonth = Month(CDate(Me.TxbDate))
temDate = CDate(Me.TxbDate) + 1
If VoucherProcType = "凭证修改" Then
If Month(temDate) <> preMonth Then
Me.TxbDate = temDate - 1
Else
Me.TxbDate = temDate
End If
Else
If Year(temDate) > Val(currYear) Then
Me.TxbDate = CDate(currYear & "/12/31")
Else
Me.TxbDate = temDate
End If
End If
End Sub
Private Sub CmdDelete_Click()
Dim AccountCode As String
Dim AccTypeCode As String
Dim ItemName As String
Dim ItemTypeCode As String
Dim UserName As String
Dim arr()
strDeletedId = ""
strDeletedAccCode = ""
With LvDetail
For i = 1 To .ListItems.Count
If .ListItems(i).Checked = True Then
If .ListItems(i).Text <> "" Then
'把删除的id记录下来
strDeletedId = strDeletedId & Me.LvDetail.ListItems(i).Text & "/"
End If
s = s + 1
End If
Next
End With
If s = 0 Then
MsgBox "请钩选想要删除的记录!"
Exit Sub
End If
'Stop
With Me.LvDetail
For i = .ListItems.Count To 1 Step -1
If .ListItems(i).Checked = True Then
.ListItems.Remove (i)
End If
Next
End With
DeleteStatus = DeleteStatus + 1
'Stop
End Sub
Private Sub CmdIncreaseHeight_Click()
Dim H As Integer
If ShiftKeyPressed Then
Me.CmdIncreaseHeight.Caption = "减高"
Me.CmdIncreaseHeight.ForeColor = vbBlack
H = -20
Else
Me.CmdIncreaseHeight.Caption = "增高"
Me.CmdIncreaseHeight.ForeColor = &HFF00FF
H = 20
End If
Me.Height = Me.Height + H
Me.LvDetail.Height = Me.LvDetail.Height + H
Me.Frame3.Top = Me.Frame3.Top + H
End Sub
Private Sub CmdNumberDown_Click()
If VoucherProcType = "凭证修改" Then Exit Sub
Me.TxbNumber = IIf(Me.TxbNumber - 1 > 0, Me.TxbNumber - 1, 1)
End Sub
Private Sub CmdNumberUp_Click()
If VoucherProcType = "凭证修改" Then Exit Sub
Me.TxbNumber = IIf(Me.TxbNumber + 1 < 999, Me.TxbNumber + 1, 999)
End Sub
Private Sub CmdOutPut_Click()
If Not wContinue("即将导出!") Then Exit Sub
On Error Resume Next
Dim arrT()
Dim iPath As String, iYear As String
Dim iSheet As Worksheet
If Me.CkB_ChoseFolder.Value = True Then
iPath = PathSelected & "\"
Else
iPath = ThisWorkbook.Path & "\"
End If
fName = Me.LbTitle & Format(VBA.Now, "YYYYMMDDhhmmss") & ".xlsx"
Application.DisplayAlerts = False
iRow = Me.LvDetail.ListItems.Count + 1
iCol = Me.LvDetail.ColumnHeaders.Count
ReDim arrT(1 To iRow, 1 To iCol)
For i = 1 To iCol
arrT(1, i) = Me.LvDetail.ColumnHeaders(i)
Next
For i = 2 To iRow
arrT(i, 1) = Me.LvDetail.ListItems(i - 1).Text
For j = 2 To iCol
arrT(i, j) = Me.LvDetail.ListItems(i - 1).SubItems(j - 1)
Next
Next
Workbooks.Add
ActiveWorkbook.Sheets(1).Range("A1").Resize(iRow, iCol) = arrT
ActiveWorkbook.SaveAs Filename:=iPath & fName
ActiveWorkbook.Close
MsgBox ("成功导出文件" & iPath & fName)
Unload Me
Application.DisplayAlerts = True
End Sub
Private Sub CmdSave_Click()
Dim arrTable()
Dim LvItem As ListItem
Dim NullCount As Integer
Dim arrID() As String '先不确定数据类型,用来存放split(strdeletedid)
Dim arrAccCode() As String
On Error Resume Next
If CmdChangeColWidth.Caption = "固定列宽" Then
Call CmdChangeColWidth_Click
End If
If ModifyStatus = 0 And DeleteStatus = 0 Then
MsgBox "数据无任何修改,无需保存!"
Exit Sub
End If
'检查数据完整性、准确性↓↓↓↓↓↓↓↓↓↓↓↓↓↓
'1、检查必填项是否为空
With Me.LvDetail
For i = 1 To .ListItems.Count
If .ListItems(i).Text = "" Then
For j = 2 To .ColumnHeaders.Count
If InStr(strRequiredCol, Format(j, "00")) Then
If .ListItems(i).SubItems(j - 1) = "" Then
MsgBox "第【" & j & "】列【" & .ColumnHeaders(j) & "】不能为空!"
'Stop
Exit Sub
End If
End If
Next
Else
End If
Next
End With
'检查数据完整性、准确性↑↑↑↑↑↑↑↑↑↑↑↑↑↑
'删除记录
If Len(Replace(strDeletedId, "/", "")) > 0 Then
'Stop
strDeletedId = Left(strDeletedId, Len(strDeletedId) - 1)
arrID = Split(strDeletedId, "/")
If Not wContinue("即将删除以下ID的记录:" & Chr(10) & strDeletedId & Chr(10) & "此操作不可恢复,请谨慎执行!") Then Exit Sub
SQL = "delete * from " & currTable & " where id in (" & Join(arrID, ",") & ")"
Call ExecuteSQL(dataFile, SQL)
End If
'增加、修改记录,建立数据连接
Set cnn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
passWord = "p111111"
StrCnn = GetStrCnn(dataFile, passWord)
cnn.Open StrCnn
rs.Open currTable, cnn, 1, 3
With Me.LvDetail
For i = 1 To .ListItems.Count
If Len(Trim(.ListItems(i).Text)) > 0 Then '对id不为空的记录,即可能被修改的记录进行操作
If InStr(strModifiedID, .ListItems(i).Text) Then '判断存放id的数组是否为空值,如果为空,则表明没有修改的记录,不用执行更新
rs.movefirst
Do Until rs.EOF
If rs!ID = .ListItems(i).Text Then
'rs.Edit
For k = 1 To .ColumnHeaders.Count - 1
'数据库中“是/否”字段值为“-1/0”,但显示为“true/false”
rs.Fields(k) = IIf(.ListItems(i).SubItems(k) = "true", -1, IIf(.ListItems(i).SubItems(k) = "false", 0, .ListItems(i).SubItems(k)))
Next
rs.Update
End If
rs.MoveNext
Loop
End If
Else '对id为空的记录,即新增的记录进行操作,向数据库写入记录
rs.AddNew
For k = 1 To .ColumnHeaders.Count - 1
rs.Fields(k) = IIf(.ListItems(i).SubItems(k) = "true", -1, IIf(.ListItems(i).SubItems(k) = "false", 0, .ListItems(i).SubItems(k)))
Next
rs.Update
End If
Next
End With
rs.Close
cnn.Close
Set rs = Nothing
Set cnn = Nothing
If currTable = "tb凭证" Then
If VoucherProcType = "凭证制单" Then
MsgBox "成功保存凭证【" & Me.TxbNumber & "】号!", , Me.LbTitle
ElseIf VoucherProcType = "凭证修改" Then
MsgBox "成功修改凭证【" & Me.TxbNumber & "】号!", , Me.LbTitle
End If
Else
MsgBox "保存成功!", , Me.LbTitle
End If
ModifyStatus = 0
strDeletedId = ""
DeleteStatus = 0
Me.LvDetail.ColumnHeaders.Clear
Me.LvDetail.ListItems.Clear
Call UserForm_Initialize
End Sub
Private Sub CmdSearch_Click()
On Error Resume Next
Me.LvDetail.ListItems.Clear
iTotal = 0
Dim searchStr As String
Dim LvItem As ListItem
iRow = UBound(aData, 2)
iCol = UBound(aData, 1)
For i = 0 To iRow
For j = 0 To iCol
searchStr = searchStr & "|" & aData(j, i)
Next
If InStr(1, searchStr, Me.TextBox1.Value, 1) Then
Set LvItem = Me.LvDetail.ListItems.Add
LvItem.Text = aData(0, i)
For j = 1 To iCol
LvItem.SubItems(j) = aData(j, i)
Next
End If
searchStr = ""
Next
End Sub
Private Sub CmdVoucherCopy_Click()
Usf_VoucherList.Show
End Sub
Private Sub CmdVoucherProcess_Click()
If VoucherProcType = "凭证制单" Then
VoucherProcType = "凭证修改"
Else
VoucherProcType = "凭证制单"
End If
Unload Me
Usf_AddAndModify.Show
End Sub
Private Sub InkEdit1_DblClick()
Dim currID As String
On Error Resume Next
With Me.LvDetail
'共同选项
If .ColumnHeaders(intCol) = "使用状态" Or .ColumnHeaders(intCol) = "状态" Then
With Usf_Interm
.Caption = "选择【使用状态】"
arrType = Array("正常", "封存")
With Usf_Interm.CmbInterm
.Clear
.List = arrType
.Text = Me.InkEdit1.Text
End With
.Show
End With
End If
If currTable = "tb基础信息" Then '基础设置
ElseIf currTable = "tb用户" Then '用户管理
If .ColumnHeaders(intCol) = "权限" Then
With Usf_Interm
.Caption = "选择【权限】"
'选择用户权限
SQL = "select distinct 权限 from tb用户权限"
arrType = GetData(dataFile, SQL)
With Usf_Interm.CmbInterm
.Clear
For i = 0 To UBound(arrType, 2)
.AddItem arrType(0, i)
Next
.Text = Me.InkEdit1.Text
End With
.Show
End With
End If
End If
End With
End Sub
Private Sub LbTopDate_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
If Me.TxbDate = "" Then Exit Sub
preDate = CDate(Me.TxbDate)
Usf_ChangeDate.Show
End Sub
Private Sub LbTopModify_Click()
If Me.TxbDate = "" Then Exit Sub
Usf_ChangeDate.Show
End Sub
Private Sub TxbDate_Change()
If VoucherProcType = "凭证修改" Then Exit Sub
iMonth = Format(Me.TxbDate, "YYYYMM")
If Format(preDate, "YYYYMM") = iMonth Then Exit Sub
SQL = "select count(*) from tb凭证 where 月份='" & iMonth & "'"
n = RecordValue(dataFile, SQL)
If n > 0 Then
SQL = "select top 1 凭证号 from tb凭证 where 月份='" & iMonth & "' order by 分录号 DESC"
preNumber = RecordValue(dataFile, SQL)
Me.TxbNumber = preNumber + 1
Else
Me.TxbNumber = 1
End If
'Stop
End Sub
Private Sub LvDetail_Click()
If currTable = "tb用户" Then
EditableField = "All"
End If
EditableCol = ""
With Me.LvDetail
For i = 1 To .ColumnHeaders.Count
If InStr(EditableField, "All") Then
If .ColumnHeaders(i) <> "ID" Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
ElseIf InStr(EditableField, "Except") Then
If .ColumnHeaders(i) <> "ID" And InStr(EditableField, .ColumnHeaders(i)) = 0 Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
Else
If InStr(EditableField, .ColumnHeaders(i)) Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
End If
Next
End With
If InStr(EditableCol, Format(intCol, "00")) Then
Call ShowInkEdit
End If
End Sub
Private Sub TxbNumber_Change()
If VoucherProcType = "凭证修改" Then Exit Sub
Me.TxbNumber = Left(Me.TxbNumber, 3)
Me.TxbNumber = IIf(Val(Me.TxbNumber) = 0, 1, Val(Me.TxbNumber))
End Sub
Private Sub CmdChangeWidth_Click()
With Me.LvDetail
For i = 1 To .ColumnHeaders.Count
W = W + .ColumnHeaders(i).Width
Next
.Width = W
If currTable = "tb凭证" Then
Me.Width = .Width + 20 + 15
Else
Me.Width = .Width + 20
End If
W = 0
End With
Me.CmdChangeColWidth.Left = Me.LvDetail.Left + Me.LvDetail.Width - Me.CmdChangeColWidth.Width
Me.CmdChangeWidth.Left = Me.CmdChangeColWidth.Left - Me.CmdChangeWidth.Width - 2
Me.Frame3.Left = Me.LvDetail.Left + Me.LvDetail.Width - Me.Frame3.Width
Me.LbTitle.Left = Me.Width / 2 - Me.LbTitle.Width / 2
End Sub
Private Sub UserForm_Initialize()
dataFile = ThisWorkbook.Path & "\收费管理系统数据库.accdb"
currTable = "tb用户"
Dim ItemTypeCode As String
Dim lbCtrl As Control
If dataFile = "" Then
MsgBox "数据库文件路径异常,请重新登录!"
Exit Sub
End If
' Stop
On Error Resume Next
'SQL语句,列宽 ,指定可编辑列,必填列的字段名称,标题
If currTable = "tb用户" Then
' Stop
initSQL = "select * from " & currTable & " where 用户ID<>'admin' and 用户ID<>'Superuser'"
arrWidth = Array(40, 100, 100, 80, 80, 80, 80, 80, 80, 80, 80, 60, 60, 60, 60, 60)
Me.LbTitle = "用户管理"
EditableField = "Except/用户ID"
strRequiredField = "All"
Else
initSQL = "select * from " & currTable
arrWidth = Array(40, 100, 100, 80, 80, 80, 80, 80, 80, 80, 80, 60, 60, 60, 60, 60)
Me.LbTitle = Right(currTable, Len(currTable) - 2)
EditableField = "All"
strRequiredField = "All"
End If
' Stop
'删除动态添加的标签
For Each lbCtrl In Me.FrmHeader.Controls
If lbCtrl.Name Like "topLb_*" Then Controls.Remove lbCtrl.Name
Next
'Stop
'添加表头字段,以及标签遮盖层
Me.Frame1.Top = Me.LbTitle.Top + Me.LbTitle.Height + 5
tbTitle = GetFields(dataFile, initSQL)
For i = 0 To UBound(tbTitle, 1)
With Me.LvDetail
If i = 0 Then
.ColumnHeaders.Add , , tbTitle(i), arrWidth(i)
ElseIf InStr(tbTitle(i), "金额") Or InStr(tbTitle(i), "余额") Then
.ColumnHeaders.Add , , tbTitle(i), arrWidth(i), lvwColumnRight
Else
.ColumnHeaders.Add , , tbTitle(i), arrWidth(i)
End If
End With
Set lbCtrl = Me.FrmHeader.Controls.Add("Forms.Label.1", "topLb_" & i, True)
If i = 0 Then
iwidth = 0
Else
iwidth = iwidth + arrWidth(i - 1)
End If
With lbCtrl
.Caption = tbTitle(i)
.Height = 18.5
.Top = 0
.Width = arrWidth(i)
.Left = iwidth
.BorderStyle = 1
.FontSize = 10
.FontName = "微软雅黑"
.ForeColor = vbWhite 'RGB(50, 50, 255)
.BackColor = RGB(153, 153, 255)
.TextAlign = 2
.ZOrder (0)
End With
Next
'listview控件的显示外观
With Me.LvDetail
.View = lvwReport
.Gridlines = True '
'.Sorted = True
.CheckBoxes = True
.LabelEdit = lvwManual
.FullRowSelect = True
.ForeColor = vbBlue
'设置窗体、listview的宽度
For i = 1 To .ColumnHeaders.Count
W = W + .ColumnHeaders(i).Width
Next
.Width = W
End With
' Stop
'根据指定字段转化可编辑列、必填列
With Me.LvDetail
For i = 1 To .ColumnHeaders.Count
If InStr(EditableField, "All") Then
If .ColumnHeaders(i) <> "ID" Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
ElseIf InStr(EditableField, "Except") Then
If .ColumnHeaders(i) <> "ID" And InStr(EditableField, .ColumnHeaders(i)) = 0 Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
Else
If InStr(EditableField, .ColumnHeaders(i)) Then
EditableCol = EditableCol & Format(i, "00") & "/"
End If
End If
If InStr(strRequiredField, "All") Then '如果是所有列都必填,第一列ID也是不需要且不能编辑的
If .ColumnHeaders(i) <> "ID" Then
strRequiredCol = strRequiredCol & Format(i, "00") & "/"
End If
ElseIf InStr(strRequiredField, "Except") Then
If .ColumnHeaders(i) <> "ID" And InStr(strRequiredField, .ColumnHeaders(i)) = 0 Then
strRequiredCol = strRequiredCol & Format(i, "00") & "/"
End If
Else
If InStr(strRequiredField, .ColumnHeaders(i)) Then
strRequiredCol = strRequiredCol & Format(i, "00") & "/"
End If
End If
Next
End With
' Stop
If currTable = "tb凭证" Then
ReDim aData(0 To UBound(tbTitle, 1) - 1, 0 To 5)
'把金额预填0
For i = 0 To UBound(aData, 2)
aData(Pxy(tbTitle, "借方金额") - 1, i) = Format(0, "Standard")
aData(Pxy(tbTitle, "贷方金额") - 1, i) = Format(0, "Standard")
Next
Else
If RecordValue(dataFile, "select count(*) from " & currTable) > 0 Then
aData = GetData(dataFile, initSQL)
End If
End If
' Stop
'添加明细数据到listview
If Not IsArrEmpty(aData) Then
iRow = UBound(aData, 2)
iCol = UBound(aData, 1)
Me.LvDetail.ListItems.Clear
For i = 0 To iRow
Set LvItem = Me.LvDetail.ListItems.Add
LvItem.Text = aData(0, i)
ForeColor = IIf(LvItem.index Mod 2, vbBlack, RGB(102, 102, 153))
LvItem.ForeColor = ForeColor
For j = 1 To iCol
LvItem.SubItems(j) = aData(j, i)
If InStr(EditableCol, Format(j + 1, "00")) Then
If LvItem.index Mod 2 Then
LvItem.ListSubItems(j).ForeColor = RGB(0, 128, 128)
Else
LvItem.ListSubItems(j).ForeColor = RGB(51, 204, 204)
End If
Else
LvItem.ListSubItems(j).ForeColor = ForeColor
End If
Next
Next
End If
'调整控件位置、窗体大小等
With Me
.Width = .LvDetail.Width + 20
.LbTitle.Left = (.Width - .LbTitle.Width) / 2
.CkB_ChoseFolder.Left = .Width - .CkB_ChoseFolder.Width - 10
.CmdOutPut.Left = .CkB_ChoseFolder.Left - .CmdOutPut.Width - 10
.CmdSearch.Left = .CmdOutPut.Left - .CmdSearch.Width - 10
.TextBox1.Left = .CmdSearch.Left - .TextBox1.Width - 10
.Frame3.Left = .LvDetail.Left + .LvDetail.Width - .Frame3.Width
End With
'对于数据行比较少的表来说,统一的listview控件高度会有很多空行,不太美观,对少于20行的表进行动态调整显示
n = Me.LvDetail.ListItems.Count
If n < 20 Then
If n < 6 Then
Me.LvDetail.Height = 6 * Me.LvDetail.ListItems(n).Height + 20
Else
Me.LvDetail.Height = (n + 1) * Me.LvDetail.ListItems(n).Height + 20
End If
Else
Me.LvDetail.Height = (20 + 1) * Me.LvDetail.ListItems(n).Height + 20
End If
'Stop
With FrmHeader '表头替代字段,防止Listview表头拖动变化。
.Visible = True
.Top = Me.Frame1.Top + Me.Frame1.Height
.Left = Me.LvDetail.Left
.Width = Me.LvDetail.Width
.Height = 19
.Caption = ""
End With
With Me
.LvDetail.Top = FrmHeader.Top + FrmHeader.Height
.Height = LvDetail.Height + LvDetail.Top + 80
.Frame3.Top = .Height - .Frame3.Height - 30
.CmdChangeColWidth.Top = .FrmHeader.Top - .CmdChangeColWidth.Height
.CmdChangeColWidth.Left = .FrmHeader.Left + .FrmHeader.Width - .CmdChangeColWidth.Width
.CmdChangeWidth.Top = .CmdChangeColWidth.Top
.CmdChangeWidth.Left = .CmdChangeColWidth.Left - .CmdChangeWidth.Width - 2
End With
Me.Caption = "【模块:" & Me.LbTitle & "】" _
& "】【用户:" & currUserName & "】"
'单独对凭证的显示按钮进行定义
Me.Frame3.BackColor = Me.BackColor
'***************************↓使得ListView可编辑相关代码↓*********************************
preColor = RGB(0, 255, 255)
InkEdit1.BackColor = preColor
InkEdit1.Font.size = Me.LvDetail.Font.size
InkEdit1.Width = 0
'InkEdit1.MultiLine = False
InkEdit1.ZOrder 0 '把InkEdit1移到最上一层,避免被Listview遮住
sngPixelPerPoint = Pixel2PointX
blnFlag = True '指示InkEdit1_Exit事件是否保存修改。按下Escape键时设为False
LvmPreWndProc = GetWindowLong(Me.LvDetail.hwnd, GWL_WNDPROC)
InkPreWndProc = GetWindowLong(InkEdit1.hwnd, GWL_WNDPROC)
SetWindowLong LvDetail.hwnd, GWL_WNDPROC, AddressOf WndProc
SetWindowLong InkEdit1.hwnd, GWL_WNDPROC, AddressOf WndProc
'***************************↑使得ListView可编辑相关代码↑*********************************
End Sub
'***************************↓使得ListView可编辑相关代码↓*********************************
'InkEdit失去焦点时即可发生Exit事件
'InkEdit退出事件。退出时需要指定是否保存修改内容。
Private Sub InkEdit1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
HideInkEdit blnFlag
blnFlag = True
End Sub
'InkEdit控件的按键处理程序
Private Sub InkEdit1_KeyUp(pKey As Long, ByVal ShiftKey As Integer)
Dim lngItemIndex As Long
Dim lngColCount As Long
Dim lngItemCount As Long
Dim LvItem As ListItem
Dim currIntervals As Single
If pKey = 27 Then
intStrikeTimes = intStrikeTimes + 1
If intStrikeTimes = 1 Then
lastEscapeTime = Timer
ElseIf intStrikeTimes = 2 Then
currIntervals = Timer - lastEscapeTime
Else
intStrikeTimes = 0
End If
End If
With LvDetail
lngItemIndex = .SelectedItem.index
lngColCount = .ColumnHeaders.Count
lngItemCount = .ListItems.Count
blnFlag = True '原来是放到每一个Case分支里的,这里只是有一条分支是False值
Select Case pKey
Case 13 '13=回车键
.SetFocus
If .ColumnHeaders(intCol) = "贷方金额" Then
If lngItemIndex < lngItemCount Then
Set .SelectedItem = .ListItems(lngItemIndex + 1)
intCol = 4 '摘要
Else
Set LvItem = .ListItems.Add
LvItem.SubItems(7) = 0: LvItem.SubItems(8) = 0
Set .SelectedItem = .ListItems(.ListItems.Count)
intCol = 4
End If
ElseIf intCol = lngColCount Then
If lngItemIndex < lngItemCount Then
Set .SelectedItem = .ListItems(lngItemIndex + 1)
intCol = 2
Else
Set LvItem = .ListItems.Add
Set .SelectedItem = .ListItems(.ListItems.Count)
intCol = 2
End If
Else
Set .SelectedItem = .ListItems(lngItemIndex)
intCol = intCol + 1
End If
If InStr(EditableCol, Format(intCol, "00")) Then
.SelectedItem.EnsureVisible
ShowInkEdit
End If
Case 37 '37=向左键头
.SetFocus '先触InkEdit1_Exit事件,此后Listview已获焦
If intCol > 1 Then
intCol = intCol - 1
ShowInkEditForLRKey 37
End If
Case 38 '38=向上键头
.SetFocus
If lngItemIndex > 1 Then
Set .SelectedItem = .ListItems(lngItemIndex - 1)
.SelectedItem.EnsureVisible
ShowInkEdit
End If
Case 39 '39=向右键头
.SetFocus
If intCol < lngColCount Then
intCol = intCol + 1
ShowInkEditForLRKey 39
End If
Case 40 '40=向下箭头
.SetFocus
If lngItemIndex < lngItemCount Then
Set .SelectedItem = .ListItems(lngItemIndex + 1)
.SelectedItem.EnsureVisible
ShowInkEdit
End If
Case 27 '27 = Esc键,取消修改
If intStrikeTimes = 2 Then '按2次Esc键,并且两次按键时间小于2秒,才退出inkedit,在输入法中会用Esc取消输入
If currIntervals < 0.8 Then
blnFlag = False
.SetFocus
intStrikeTimes = 0
End If
End If
Case Else
End Select
End With
End Sub
'把X方向的像素值转为磅。VBA窗体的度量单位是磅。
'像素和磅的转换跟屏幕密度有关,不同电脑可能不同值。
Private Function Pixel2PointX() As Double
Dim hDC As Long, DPIx As Long
hDC = GetDC(0) '获取屏幕设备环境句柄
DPIx = GetDeviceCaps(hDC, LOGPIXELSX) '获取屏幕X方向像素密度
ReleaseDC 0, hDC '释放屏幕设备环境
Pixel2PointX = 72 / DPIx
End Function
'鼠标事件主要计算点击的列号。并可在此处鼠标按键条件,比如改为右键点击才计算列号,左键时列号置为零。这样InkEdit的显示程序就不会显示控件
Private Sub LvDetail_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As stdole.OLE_XPOS_PIXELS, ByVal Y As stdole.OLE_YPOS_PIXELS)
Dim sngDiff As Double '单击鼠标,弹起时即可触发事件。可用Button判断点击的是鼠标三键中的哪一个,1=左,2=右,4=中
Dim sngScrollPos As Double
Dim sngMousePosX As Double
With LvDetail
sngScrollPos = sngPixelPerPoint * GetScrollPos(.hwnd, SB_HORZ)
sngMousePosX = sngPixelPerPoint * X
For intCol = 1 To .ColumnHeaders.Count
sngDiff = .ColumnHeaders(intCol).Left - sngScrollPos
If sngMousePosX > sngDiff And sngMousePosX < sngDiff + .ColumnHeaders(intCol).Width Then Exit For
Next
If intCol > .ColumnHeaders.Count Then intCol = 0 '计算失败时,置为零
End With
End Sub
'InkEdit控件退出时的处理程序,将修改内容同步到Listview
Private Sub HideInkEdit(Optional ByVal blnSave As Boolean = True)
Dim OldFullName$, NewFullName$
Dim myID As Integer '当前修改的ID
On Error Resume Next
InkEdit1.BackColor = preColor
With LvDetail
If .SelectedItem Is Nothing Then Exit Sub '如果InkEdit1未失焦时就关闭窗体,必报错。必须加这一句。
If strOriginal = InkEdit1.Text Then InkEdit1.Width = 0: Exit Sub 'InkEdit的值有改变时才执行后面语句,否则浪费时间
If Len(strRequiredCol) Then
If InStr(strRequiredCol, Format(intCol, "00")) Then
If Len(InkEdit1.Text) = 0 Then
MsgBox "该项为必填项,修改已被取消!", vbCritical
InkEdit1.Width = 0: Exit Sub
End If
End If
End If
If blnSave Then
If intCol > 1 Then
'1用户管理
If currTable = "tb用户" Then
If .ColumnHeaders(intCol) = "用户ID" Then
If RecordValue(dataFile, "select count(用户ID) From tb用户 where 用户ID='" & InkEdit1.Text & "'") > 0 Then
MsgBox "已存在【" & InkEdit1.Text & "】用户ID不能重复!"
Me.InkEdit1.Text = ""
Exit Sub
End If
If Len(InkEdit1.Text) < 4 Then
MsgBox "用户ID不能低于4位"
InkEdit1.Text = ""
Exit Sub
End If
ElseIf .ColumnHeaders(intCol) = "姓名" Then
If RecordValue(dataFile, "select count(姓名) From tb用户 where 姓名='" & InkEdit1.Text & "'") > 0 Then
MsgBox "已存在【" & InkEdit1.Text & "】姓名不能重复!"
Me.InkEdit1.Text = ""
Exit Sub
End If
If Len(InkEdit1.Text) < 2 Then
MsgBox "姓名至少2个字符"
InkEdit1.Text = ""
Exit Sub
End If
ElseIf .ColumnHeaders(intCol) = "密码" Then
If Len(InkEdit1.Text) < 6 Then
MsgBox "密码不能低于6位"
InkEdit1.Text = ""
Exit Sub
End If
End If
.SelectedItem.SubItems(intCol - 1) = InkEdit1.Text
If .SelectedItem.Text = "" Then
.SelectedItem.SubItems(Pxy(tbTitle, "状态") - 1) = "正常"
End If
Else '对应 类似 ElseIf currtable="tb?" Then
.SelectedItem.SubItems(intCol - 1) = InkEdit1.Text
End If
Else '对应 if incol>1
.SelectedItem.Text = InkEdit1.Text
End If
If .SelectedItem.Text = "" Then
.SelectedItem.ListSubItems(intCol - 1).ForeColor = vbBlue '新增的记录标蓝
Else
.SelectedItem.ListSubItems(intCol - 1).ForeColor = vbRed '修改的记录标红
End If
ModifyStatus = ModifyStatus + 1
'将生产修改的记录的ID添加到strModifiedID中,两边用/隔开,做到精确匹配
myID = Val(.SelectedItem.Text)
If myID > 0 Then
If InStr(strModifiedID, "/" & myID & "/") = 0 Then
strModifiedID = strModifiedID & "/" & myID & "/"
End If
End If
'**********将生产涉及修改的其他核算项目记录的ID写入数组保存***********
End If
End With
InkEdit1.Width = 0
End Sub
Private Sub tb报表项目Process()
End Sub
'左右方向键处理程序。主要计算是水平滚动条的滚动量,以确保InkEdit可见
Private Sub ShowInkEditForLRKey(ByVal intKey As Integer)
Dim sngNewInkLeft As Double
Dim lngScrollAmount As Long
Dim blnInkLocked As Boolean
With LvDetail
If intCol = 0 Then Exit Sub
If .SelectedItem Is Nothing Then Exit Sub
If InStr(EditableCol, Format(intCol, "00")) = 0 Then Exit Sub
If intCol > 1 Then
InkEdit1.Text = .SelectedItem.SubItems(intCol - 1)
Else
InkEdit1.Text = .SelectedItem.Text
End If
If intKey = 37 Then '向左
sngNewInkLeft = InkEdit1.Left - .ColumnHeaders(intCol).Width
If sngNewInkLeft < .Left + 1.5 Then
lngScrollAmount = CLng((sngNewInkLeft - (.Left + 1.5)) / sngPixelPerPoint) '滚动量,单位像素
SendMessageLong .hwnd, LVM_SCROLL, lngScrollAmount, 0 '拖动Listview水平滚动条,保持InkEdit可见
InkEdit1.Left = .Left + 1.5
Else
InkEdit1.Left = sngNewInkLeft
End If
Else '向右
sngNewInkLeft = InkEdit1.Left + .ColumnHeaders(intCol - 1).Width
If sngNewInkLeft + .ColumnHeaders(intCol).Width > .Left + .Width Then
lngScrollAmount = CLng((sngNewInkLeft + .ColumnHeaders(intCol).Width - (.Left + .Width)) / sngPixelPerPoint)
SendMessageLong .hwnd, LVM_SCROLL, lngScrollAmount, 0
InkEdit1.Left = .Left + .Width - .ColumnHeaders(intCol).Width
Else
InkEdit1.Left = sngNewInkLeft
End If
End If
InkEdit1.Top = .Top + .SelectedItem.Top + 1.5
InkEdit1.Width = .ColumnHeaders(intCol).Width
InkEdit1.Height = .SelectedItem.Height
If Len(EditableCol) Then
blnInkLocked = (InStr(EditableCol, Format(intCol, "00")) = 0)
Else
blnInkLocked = False
End If
InkEdit1.Locked = blnInkLocked
InkEdit1.SelStart = 0
InkEdit1.SelLength = Len(InkEdit1.Text)
strOriginal = InkEdit1.Text
InkEdit1.SetFocus
End With
End Sub
'显示InkEdit控件的处理程序。需要显示InkEdit时调用
Private Sub ShowInkEdit()
Dim sngScrollPos As Double
Dim blnInkLocked As Boolean
Dim iItem As String
With LvDetail
If intCol = 0 Then Exit Sub '点击的列号未计算成功
If .SelectedItem Is Nothing Then Exit Sub 'Listview列表为空时退出
sngScrollPos = sngPixelPerPoint * GetScrollPos(.hwnd, SB_HORZ)
If intCol > 1 Then
InkEdit1.Text = .SelectedItem.SubItems(intCol - 1)
strOriginal = InkEdit1.Text
intRow = .SelectedItem.index
Else
InkEdit1.Text = .SelectedItem.Text
End If
InkEdit1.Left = .ColumnHeaders(intCol).Left + .Left + 1.5 - sngScrollPos
InkEdit1.Top = .Top + .SelectedItem.Top + 1.5
InkEdit1.Width = .ColumnHeaders(intCol).Width
InkEdit1.Height = .SelectedItem.Height
If Len(EditableCol) Then
blnInkLocked = (InStr(EditableCol, Format(intCol, "00")) = 0)
Else
blnInkLocked = False
End If
InkEdit1.Locked = blnInkLocked
InkEdit1.SelStart = 0
InkEdit1.SelLength = Len(InkEdit1.Text)
'strOriginal = InkEdit1.Text '移到前面
InkEdit1.SetFocus
End With
End Sub
'关闭窗体时,还原Listview和InkEdit控件的窗口程序,在退出窗体时调用
Private Sub RestoreAPI()
SetWindowLong LvDetail.hwnd, GWL_WNDPROC, LvmPreWndProc
SetWindowLong InkEdit1.hwnd, GWL_WNDPROC, InkPreWndProc
End Sub
'***************************↑使得ListView可编辑相关代码↑*********************************
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then '检测关闭模式是否为点击窗口右上角的 X
Cancel = True '取消关闭事件
End If
End Sub