-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTable.bas
More file actions
424 lines (342 loc) · 13 KB
/
Table.bas
File metadata and controls
424 lines (342 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
Attribute VB_Name = "Table"
Option Explicit
' 2020-04-08
' Added capability to read/write to/from an access database
Private Const Module_Name As String = "Table."
Public Function TryCopyDictionaryToTable( _
ByVal TableType As iTable, _
ByVal Dict As Dictionary, _
Optional ByVal Tbl As ListObject = Nothing, _
Optional ByVal Rng As Range = Nothing, _
Optional ByVal TableName As String = vbNullString, _
Optional CopyToTableRegardless As Boolean = False, _
Optional ByVal KeepWorkbookOpen As Boolean = True _
) As Boolean
' This routine copies a dictionary to an Excel table or a database
' If Dict is nothing then use TableType.LocalDictionary
'
' If Tbl is nothing then build a table using Rng and TableName
'
' If Tbl and Rng are both Nothing then use TableType.LocalTable
'
' CopyToTableRegardless = True forces copying to an Excel table
' regardless of whether there is an associated database
'
' KeepWorkbookOpen = True avoids the workbook close in TryCopyDictionaryToExcelTable
Const RoutineName As String = Module_Name & "CopyDictionaryToTable"
On Error GoTo ErrorHandler
If Dict.Count = 0 Then
GoTo Done
End If
TryCopyDictionaryToTable = True
If TableType.IsDatabase And Not CopyToTableRegardless Then
Dim Ary As Variant
If TableType.TryCopyDictionaryToArray(Dict, Ary) Then
Else
ReportError "Error copying dictionary to array", "Routine", RoutineName
TryCopyDictionaryToTable = False
GoTo Done
End If
If TryCopyArrayToDatabase( _
Ary, TableType.DatabaseName, TableType.DatabaseTableName, False) _
Then
Else
ReportError "Error copying dictionary to database", "Routine", RoutineName
TryCopyDictionaryToTable = False
GoTo Done
End If
Else
If TryCopyDictionaryToExcelTable(TableType, Dict, Tbl, Rng, TableName, KeepWorkbookOpen) Then
Else
ReportError "Error copying dictionary to Excel Table", "Routine", RoutineName
TryCopyDictionaryToTable = False
GoTo Done
End If
End If
Done:
Exit Function
ErrorHandler:
ReportError "Exception raised", _
"Routine", RoutineName, _
"Error Number", Err.Number, _
"Error Description", Err.Description
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function ' TryCopyDictionaryToTable
Private Function TryCopyDictionaryToExcelTable( _
ByVal TableType As iTable, _
ByVal Dict As Dictionary, _
Optional ByVal Tbl As ListObject = Nothing, _
Optional ByVal Rng As Range = Nothing, _
Optional ByVal TableName As String = vbNullString, _
Optional ByVal KeepWorkbookOpen As Boolean = True _
) As Boolean
' This routine copies a dictionary to an Excel table
' If Dict is nothing then use TableType.LocalDictionary
'
' If Tbl is nothing then build a table using Rng and TableName
'
' If Tbl and Rng are both Nothing then use TableType.LocalTable
'
' KeepWorkbookOpen = True avoids the workbook close
Const RoutineName As String = Module_Name & "TryCopyDictionaryToExcelTable"
On Error GoTo ErrorHandler
TryCopyDictionaryToExcelTable = True
Dim ThisDict As Dictionary
If Dict Is Nothing Then
If Not TableType.Initialized Then TableType.Initialize
Set ThisDict = TableType.LocalDictionary
Else
If Dict.Count = 0 Then
TryCopyDictionaryToExcelTable = False
GoTo Done
End If
Set ThisDict = Dict
End If
Dim ThisTbl As ListObject
If Tbl Is Nothing Then
If Rng Is Nothing Then
Set ThisTbl = TableType.LocalTable
Else
If TableName = vbNullString Then
ReportError "Need to provide a table name", "Routine", RoutineName
TryCopyDictionaryToExcelTable = False
GoTo Done
Else
Set ThisTbl = Rng.Parent.ListObjects.Add(xlSrcRange, _
Range(Cells(1, 1), Cells(2, TableType.HeaderWidth)), , xlYes)
ThisTbl.Name = TableName
End If
End If
Else
Set ThisTbl = Tbl
ClearTable ThisTbl
End If
Dim ThisRng As Range
Set ThisRng = ThisTbl.HeaderRowRange
ThisRng.Resize(1, TableType.HeaderWidth) = TableType.Headers
Dim Ary As Variant
ReDim Ary(1 To ThisDict.Count, 1 To TableType.HeaderWidth)
If TableType.TryCopyDictionaryToArray(ThisDict, Ary) Then
' Success; do nothing
Else
ReportError "Error copying dictionary to array", "Routine", RoutineName
TryCopyDictionaryToExcelTable = False
GoTo Done
End If
' Format the worksheet
TableType.FormatArrayAndWorksheet Ary, ThisTbl
' Move to DatabodyRange
Set ThisRng = ThisRng.Offset(1, 0)
ThisRng.Resize(UBound(Ary, 1), TableType.HeaderWidth) = Ary
ThisRng.Resize(UBound(Ary, 1), TableType.HeaderWidth) = Ary ' Seems to be needed to get the column formatting right
ThisRng.Parent.Cells.EntireColumn.AutoFit
ThisRng.Parent.Activate
ActiveWindow.FreezePanes = False
ThisTbl.DataBodyRange(1, 1).Select
ActiveWindow.FreezePanes = True
Dim WorkbookWithTable As String
WorkbookWithTable = ThisRng.Parent.Parent.Name
If Not KeepWorkbookOpen Then
Dim Wkbk As Workbook
Set Wkbk = Workbooks(WorkbookWithTable)
Wkbk.Save
Wkbk.Close
End If
Done:
Exit Function
ErrorHandler:
ReportError "Exception raised", _
"Routine", RoutineName, _
"Error Number", Err.Number, _
"Error Description", Err.Description
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function ' TryCopyDictionaryToExcelTable
Public Function TryCopyTableToDictionary( _
ByVal TableType As iTable, _
ByRef Dict As Dictionary, _
ByVal Tbl As ListObject, _
Optional ByVal ReadFromDatabase As Boolean = True _
) As Boolean
' Copies a table to a dictionary
Const RoutineName As String = Module_Name & "TryCopyTableToDictionary"
On Error GoTo ErrorHandler
TryCopyTableToDictionary = True
Dim Ary As Variant
If TableType.IsDatabase And ReadFromDatabase Then
If TryReadDatabaseToArray(TableType.DatabaseName, TableType.DatabaseTableName, Ary) Then
Else
ReportError "Error copying database to array", _
"Routine", RoutineName, _
"Table Type", TableType.LocalName
TryCopyTableToDictionary = False
GoTo Done
End If
Else
If TryCopyExcelTableToArray(TableType, Ary, Tbl) Then
Else
ReportError "Error copying table to array", _
"Routine", RoutineName, _
"Table Type", TableType.LocalName
TryCopyTableToDictionary = False
GoTo Done
End If
End If
Dim ThisDict As Dictionary
If Dict Is Nothing Then
Set ThisDict = TableType.LocalDictionary
Else
Set ThisDict = Dict
End If
If TableType.TryCopyArrayToDictionary(Ary, ThisDict) Then
' Success; do nothing
Else
ReportError "Error loading dictionary", "Routine", RoutineName
End If
Set Dict = ThisDict
Done:
Exit Function
ErrorHandler:
ReportError "Exception raised", _
"Routine", RoutineName, _
"Error Number", Err.Number, _
"Error Description", Err.Description
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function ' TryCopyTableToDictionary
Private Function TryCopyExcelTableToArray( _
ByVal TableType As iTable, _
ByRef Ary As Variant, _
ByVal Tbl As ListObject _
) As Boolean
' Used to return a boolean and some other value(s)
' Returns True if successful
Const RoutineName As String = Module_Name & "TryCopyExcelTableToArray"
On Error GoTo ErrorHandler
TryCopyExcelTableToArray = True
On Error Resume Next
Ary = Tbl.DataBodyRange
If Err.Number <> 0 Then
ReportError "The " & TableType.LocalName & " table is empty", "Routine", RoutineName
TryCopyExcelTableToArray = False
GoTo Done
End If
Err.Clear
On Error GoTo ErrorHandler
Done:
Exit Function
ErrorHandler:
ReportError "Exception raised", _
"Routine", RoutineName, _
"Error Number", Err.Number, _
"Error Description", Err.Description
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function ' TryCopyExcelTableToArray
Public Function TryReadDatabaseToArray( _
ByVal DatabaseName As String, _
ByVal DatabaseTableName As String, _
ByRef Ary As Variant, _
Optional ByRef FieldNames As Variant _
) As Boolean
' https://docs.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/recordset-object-dao
' This example demonstrates Recordset objects and the Recordsets collection
' by opening four different types of Recordsets,
' enumerating the Recordsets collection of the current Database,
' and enumerating the Properties collection of each Recordset.
Const RoutineName As String = Module_Name & "TryReadDatabaseToArray"
On Error GoTo ErrorHandler
TryReadDatabaseToArray = True
Dim DB As Database
Dim RS As Recordset
' Set DB = OpenDatabase(GetDataFilesFolder & Application.PathSeparator & DatabaseName)
Set RS = DB.OpenRecordset(DatabaseTableName, dbOpenTable)
Dim I As Long
If RS.RecordCount = 0 Then
' I had to do this because RS.RecordCount=0 even though there were 1,000s of records
I = 0
RS.MoveFirst
Do While Not RS.EOF
RS.MoveNext
I = I + 1
Loop
Else
I = RS.RecordCount
End If
RS.MoveFirst
ReDim Ary(1 To I, 1 To RS.Fields.Count)
I = 1
Dim J As Long
Do While Not RS.EOF
For J = 0 To RS.Fields.Count - 1
If IsNull(RS.Fields(J)) Then
Ary(I, J + 1) = vbNullString
Else
Ary(I, J + 1) = RS.Fields(J)
End If
Next J
RS.MoveNext
I = I + 1
Loop
ReDim FieldNames(1 To RS.Fields.Count)
Dim Fld As DAO.Field
I = 1
For Each Fld In RS.Fields
FieldNames(I) = Fld.Name
I = I + 1
Next Fld
RS.Close
DB.Close
Done:
Exit Function
ErrorHandler:
TryReadDatabaseToArray = False
ReportError "Exception raised", _
"Routine", RoutineName, _
"Error Number", Err.Number, _
"Error Description", Err.Description
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function ' TryReadDatabaseToArray
Public Function TryCopyArrayToDatabase( _
ByVal Ary As Variant, _
ByVal DatabaseName As String, _
ByVal DatabaseTableName As String, _
Optional ByVal FieldNamesInFirstRow As Boolean = False _
) As Boolean
' Copies a dictionary to its corresponding database table
' Assumes the first row of the array contains the field names
Const RoutineName As String = Module_Name & "TryCopyArrayToDatabase"
On Error GoTo ErrorHandler
TryCopyArrayToDatabase = True
If UBound(Ary, 1) = 0 Then GoTo Done
Dim DB As Database
' Set DB = OpenDatabase(GetDataFilesFolder & Application.PathSeparator & DatabaseName)
Dim SQLQuery As String
SQLQuery = "DELETE " & DatabaseTableName & ".* FROM " & DatabaseTableName
DB.Execute SQLQuery
Dim RS As Recordset
Set RS = DB.OpenRecordset(DatabaseTableName)
Dim I As Long
Dim J As Long
Dim FirstRow As Long
Dim FieldNumber As Long
FirstRow = IIf(FieldNamesInFirstRow, LBound(Ary, 1) + 1, LBound(Ary, 1))
For I = FirstRow To UBound(Ary, 1)
RS.AddNew
FieldNumber = 0
For J = LBound(Ary, 2) To UBound(Ary, 2)
RS.Fields(FieldNumber) = Ary(I, J)
FieldNumber = FieldNumber + 1
Next J
RS.Update
Next I
RS.Close
DB.Close
Done:
Exit Function
ErrorHandler:
RS.Close
DB.Close
ReportError "Exception raised", _
"Routine", RoutineName, _
"Error Number", Err.Number, _
"Error Description", Err.Description
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function ' TryCopyArrayToDatabase