I prefer producing an Excel report that has no links in it, so that it is lightweight and can easily be emailed.
So I have this report that I generate from MS Access - it writes the table to the TAB of a workbook that I have previously built a Pivot Table that uses that tab as it's source.
What I can't seem to get working properly is
xlPivot.PivotTables("pvAFP").RefreshTable
My code is as follows:
Public Sub ViewAFP(strPathToTemplate As String, Optional dbFullPath As String)
'10 Excel objects created
Dim xlApp As Object
Dim WB As Object
Dim xlSheet As Object
Dim xlPivot As Object
Dim rngWorkingRange As Object
Dim intCOL As Integer
Dim db As Database
Dim rsAFP_Pivot As DAO.Recordset
Dim rsAFP_Data As DAO.Recordset
Dim fld As Variant
Dim strLocation As String
'uses the 'My Documents\COB folder of the user
'C:\Users\[username]\Documents\COB
strLocation = "C:\users\" & Environ$("Username") & "\Documents\COB\AFP_Summary_" & DateString & ".xlsx"
Set xlApp = CreateObject("Excel.Application")
Set WB = xlApp.Workbooks.Open(strPathToTemplate)
Set xlSheet = WB.Sheets("AFP DATA")
Set xlPivot = WB.Sheets("AFP PIVOT")
'ALLOW FOR EXTERNAL ENGINES
If Len(dbFullPath) < 1 Then
Set db = CurrentDb
Else
Set db = OpenDatabase(dbFullPath, True)
End If
Set rsAFP_Data = db.OpenRecordset("Select * FROM AFP_DATA")
intCOL = 1
For Each fld In rsAFP_Data.Fields
xlSheet.Cells(1, intCOL).Value = fld.Name
Debug.Print fld.Name
intCOL = intCOL + 1
Next
xlSheet.Range("A2").CopyFromRecordset rsAFP_Data
Set rngWorkingRange = xlSheet.Range("A1").CurrentRegion
xlSheet.ListObjects.Add(xlSrcRange, rngWorkingRange, , xlYes).Name _
= "AFP_Data"
xlSheet.ListObjects("AFP_Data").TableStyle = "TableStyleLight9"
'set pvAFP to point to this table
xlPivot.PivotTables("pvAFP").ChangePivotCache WB.PivotCaches. _
Create(SourceType:=xlDatabase, SourceData:="AFP DATA!AFP_Data", _
Version:=xlPivotTableVersion12)
xlPivot.Select
xlPivot.PivotTables("pvAFP").RefreshTable
WB.RefreshAll
WB.SaveAs FileName:=strLocation
xlApp.Quit
Set xlSheet = Nothing
Set fld = Nothing
Set rsAFP_Data = Nothing
Set db = Nothing
Set WB = Nothing
Set xlApp = Nothing
End Sub
Would be nice to be ready to use, because the user needs to "Refresh ALL", before anything can be done to the Pivot Table
See if this works for you:
xlPivot.PivotTables("pvAFP").PivotCache.Refresh
Related
I want to create a pivot table with Access 2003 into Excel. Now I can create a pivot table but background process excel is not close. Results my program can run only once time.
I want to know how to close Excel background process.
My code:
Dim lRET As Integer
Dim lEXCEL_OBJ As Excel.Application
Dim lWKB As Excel.Workbook
Dim lSHEET As Excel.Worksheet
Dim lSHEET2 As Excel.Worksheet
Dim lFILEFULLNAME As String
Dim lTEMPLATEFILE As String
Dim lTEMPLATEPATH As String
Dim lBUTTON As String
Dim PTcache As Excel.PivotCache
Dim PT As Excel.PivotTable
Dim PRange As Range
Dim LastRow As Long
Dim LastCol As Long
Const lFILE As String = "template_macro2.xlt"
On Error GoTo EXCEL_RESULT_T_ERROR
lTEMPLATEPATH = "C:\Temp\" & lFILE
lTEMPLATEFILE = Dir(lTEMPLATEPATH)
Set lEXCEL_OBJ = CreateObject("Excel.Application")
Set lWKB = lEXCEL_OBJ.Workbooks.Add(lTEMPLATEPATH)
With lEXCEL_OBJ
Set lWKB = .Workbooks.Add(lTEMPLATEPATH)
Set lSHEET = .ActiveWorkbook.Sheets(1)
End With
With lEXCEL_OBJ
lWKB.Worksheets.Add
lWKB.ActiveSheet.Name = "test1"
Set lSHEET = .ActiveSheet
End With
With lSHEET
.Range("a:z").ColumnWidth = 10
.Range("b:b").ColumnWidth = 22
.Range("m:m").ColumnWidth = 24
.Range("q:q").ColumnWidth = 50
.Range("u:u").ColumnWidth = 15
End With
'add raw data in excel
Call MAKE_EXPORT_TABLE_DO_OR(lSHEET)
With lEXCEL_OBJ
lWKB.Sheets("Sheet1").Select
lWKB.Worksheets.Add
lWKB.ActiveSheet.Name = "test2"
Set lSHEET2 = .ActiveSheet
'Define Data Range
LastRow = lSHEET.Cells(lSHEET.Rows.COUNT, 1).End(-4162).Row
LastCol = lSHEET.Cells(1, lSHEET.Columns.COUNT).End(-4159).Column
Set PRange = lSHEET.Cells(1, 1).Resize(LastRow, LastCol)
'Create a Pivot Cache
Set PTcache = ActiveWorkbook.PivotCaches.Add(xlDatabase, PRange)
'Create the Pivot Table from the Cache
Set PT = PTcache.CreatePivotTable(TableDestination:=Sheets("test2").Cells(1, 1))
End With
lWKB.SaveAs hFULLPATH
EXCEL_RESULT_T_EXIT:
On Error Resume Next
lWKB.Close
PT.Application.Quit
lEXCEL_OBJ.Application.Quit
Set lWKB = Nothing
Set lEXCEL_OBJ = Nothing
Application.Echo True
DoCmd.Hourglass False
Exit Function
EXCEL_RESULT_T_ERROR:
Resume EXCEL_RESULT_T_EXIT
I'm working on a database that will compile 4 recordsets together in order to output 3 excel worksheets into a single workbook for each workcenter or Office Symbol. This will be updated weekly and new workbooks produced at each update.
I've managed to stumble my way into creating the workbooks the way I want them. However, saving the files has become an issue. The beginning of this sub creates a folder using today's date. Everything following creates the individual reports. The issue comes when I attempt to use the "wb.Saveas". Instead of saving the reports with the name from the "Do While Not" in the created folder, it saves it using today's date and the "Do While Not" output (See attached images).
I also have an issue with the Select Queries (AD1, PT1 and LV1) not giving me consistent results. Instead of filtering to only 1 Office Symbol, some of the time I get 3 or 4 on one excel output.
Thanks in advance for help with this.
Please excuse my lack of consistency with coding. I'm stumbling my way through this and I don't know the proper formatting etiquette.
incorrect naming format
Private Sub Export_Button_Click()
Dim sFolderName As String, sFolder As String
Dim sFolderPath As String
sFolder = "C:\Users\1023491733A\Desktop\TEST\"
sFolderName = Format(Now, "dd MMM yyyy")
sFolderPath = "C:\Users\1023491733A\Desktop\TEST\" & sFolderName
Set oFSO = CreateObject("Scripting.FileSystemObject")
If oFSO.FolderExists(sFolderPath) Then
MsgBox "Folder already exists with today's date!", vbInformation, "VBAF1"
Else
MkDir sFolderPath
MsgBox "Folder has created with today's date: " & vbCrLf & vbCrLf & sFolderPath, vbInformation, "VBAF1"
End If
Dim db As DAO.Database
Set db = CurrentDb
Dim OS As DAO.Recordset
Set OS = db.OpenRecordset("Office_Symbols")
Dim AD As DAO.Recordset
Set AD = db.OpenRecordset("XLS-Airfield")
Dim PT As DAO.Recordset
Set PT = db.OpenRecordset("XLS-Fitness")
Dim LV As DAO.Recordset
Set LV = db.OpenRecordset("XLS-Leave")
Dim xl
Set xl = CreateObject("Excel.Application")
Dim wb As Object
Set wb = xl.Workbooks.Add("C:\Users\1023491733A\Desktop\TEST\Template.xlsx")
Dim wr As Object
Set wr = wb.Worksheets("Airfield")
Dim ws As Object
Set ws = wb.Worksheets("Fitness")
Dim wt As Object
Set wt = wb.Worksheets("Leave")
Do While Not OS.EOF
Dim AD1 As DAO.Recordset
Set AD1 = db.OpenRecordset("SELECT [XLS-Airfield].* FROM [XLS-Airfield] WHERE ([XLS-Airfield].OFFICE_SYMBOL)='" & OS.Fields(0) & "';")
Dim PT1 As DAO.Recordset
Set PT1 = db.OpenRecordset("SELECT [XLS-Fitness].* FROM [XLS-Fitness] WHERE ([XLS-Fitness].OFFICE_SYMBOL) ='" & OS.Fields(0) & "';")
Dim LV1 As DAO.Recordset
Set LV1 = db.OpenRecordset("SELECT [XLS-Leave].* FROM [XLS-Leave] WHERE ([XLS-Leave].OFFICE_SYMBOL) ='" & OS.Fields(0) & "';")
wr.Select
wr.Range("A1").Select
For Each fld In AD1.Fields
xl.ActiveCell = fld.Name
xl.ActiveCell.Offset(0, 1).Select
Next
AD1.MoveFirst
wr.Cells(2, 1).CopyFromRecordset AD1
'Break
ws.Activate
ws.Range("A1").Select
For Each fld In PT1.Fields
xl.ActiveCell = fld.Name
xl.ActiveCell.Offset(0, 1).Select
Next
PT1.MoveFirst
ws.Cells(2, 1).CopyFromRecordset PT1
'Break
wt.Activate
wt.Range("A1").Select
For Each fld In LV1.Fields
xl.ActiveCell = fld.Name
xl.ActiveCell.Offset(0, 1).Select
Next
LV1.MoveFirst
wt.Cells(2, 1).CopyFromRecordset LV1
Dim sFileName As String
sFileName = OS.Fields(0)
wb.SaveAs sFolderPath & sFileName
Set AD1 = Nothing
Set PT1 = Nothing
Set LV1 = Nothing
OS.MoveNext
Loop
OS.Close
wr.Rows("1:1").Font.Bold = True 'Row 1 Bold
wr.Cells.EntireColumn.AutoFit 'Autofit all the columns
ws.Rows("1:1").Font.Bold = True 'Row 1 Bold
ws.Cells.EntireColumn.AutoFit 'Autofit all the columns
wt.Rows("1:1").Font.Bold = True 'Row 1 Bold
wt.Cells.EntireColumn.AutoFit 'Autofit all the columns
Set OS = Nothing
Set AD = Nothing
Set PT = Nothing
Set LV = Nothing
End Sub
I have solved my issue. I'm not sure if it's the best solution but here are the changes I made.
Dim the objects was moved into the the Do While Not loop and each was set to nothing before the OS.MoveNext.
Do While Not OS.EOF
Dim xl As Object
Set xl = CreateObject("Excel.Application")
Dim wb As Object
Set wb = xl.Workbooks.Open("C:\Users\1023491733A\Desktop\TEST\Template.xlsx")
Dim wr As Object
Set wr = wb.Worksheets("Airfield")
Dim ws As Object
Set ws = wb.Worksheets("Fitness")
Dim wt As Object
Set wt = wb.Worksheets("Leave")
I added a backslash to sFolderName as below which helped. And for some reason unknown to me, using two variables ("sfolderpath" and "OS.Fields(0)) would always give a run-time 1004 error. But inserting a constant between them seems to fix this issue but again I'm not sure why.
sFolderName = (Format(Now, "dd MMM yyyy") & "\")
Dim sfilename As String
sfilename = sFolderPath & "TEST" & OS.Fields(0)
wb.SaveAs sfilename
I understand the first fix since the loop was using the excel workbook from the previous iteration. But I can't wrap my head around why the sFileName fix worked. If anyone can explain this I would greatly appreciate it.
I want to export data from selected Outlook emails to a workbook. Each email's data (subject, body, etc.) should be stored in a different worksheet.
I'm trying to edit this macro because it is almost what I need—and especially the part of olFormatHTML and WordEditor—because of split.
The idea is
Select multiple emails in Outlook
Open file path
Data for each email selected will be stored in a single worksheet from file opened
The issue with the macro is in this third part
From the selected items, the macro does a loop and just takes the first email selected,
The data is stored in different workbooks; it should be stored in the same workbook that I opened.
Public Sub SplitEmail()
Dim rpl As Outlook.MailItem
Dim itm As Object
Dim sPath As String, sFile As String
Dim objDoc As Word.Document
Dim txt As String
Dim xlApp As Excel.Application
Dim wb As Excel.Workbook
Dim i As Long
Dim x As Long
'----------------------------
Dim myOlExp As Outlook.Explorer
Dim myOlSel As Outlook.Selection
Set myOlExp = Application.ActiveExplorer
Set myOlSel = myOlExp.Selection
For x = 1 To myOlSel.Count
'----------------------------------------------
Set itm = GetCurrentItem() 'A)I think the issuefrom selecting 1 item is located here
'|||||||||||||||||||||||||||||||||||||||||
sPath = "C:\Users\Ray\"
sFile = sPath & "Macro.xlsm"
If Not itm Is Nothing Then
Set rpl = itm.Reply
rpl.BodyFormat = olFormatHTML
'rpl.Display
End If
Set objDoc = rpl.GetInspector.WordEditor
txt = objDoc.Content.Text
'||||||||||||||||||||||||||||||||||||||||||||||
Set xlApp = CreateObject("Excel.application")
xlApp.Visible = True
Set wb = xlApp.Workbooks.Open(sFile) 'B) tried to move it to the beginning and macro doesn't work
'||||||||||||||||||||||||||||||||||||||||||||||
For i = LBound(Split(txt, Chr(13)), 1) To UBound(Split(txt, Chr(13)), 1)
wb.Worksheets(x).Range("A" & i + 1).Value = Split(txt, Chr(13))(i) 'B)emails in diferrent sheet but no same workbook
Next i
'------------------------------------------------------
Next x
End Sub
Function GetCurrentItem() As Object
Dim objApp As Outlook.Application
Set objApp = Application
On Error Resume Next
Select Case TypeName(objApp.ActiveWindow)
Case "Explorer"
Set GetCurrentItem = objApp.ActiveExplorer.Selection.item(1)
Case "Inspector"
Set GetCurrentItem = objApp.ActiveInspector.CurrentItem
End Select
GetCurrentItem.UnRead = False
Set objApp = Nothing
End Function
I made an update to this macro
as macro do loop in For x it open the file x times,
and then close it and open again instead of working on the first workbook opened
but the macro leaves open instances
here is the current code
Public Sub SplitEmail()
Dim rpl As Outlook.MailItem
Dim itm As Object
Dim sPath As String, sFile As String
Dim objDoc As Word.Document
Dim txt As String
Dim xlApp As Excel.Application
Dim wb As Excel.Workbook
Dim i As Long
Dim x As Long
'----------------------------
Dim myOlExp As Outlook.Explorer
Dim myOlSel As Outlook.Selection
Set myOlExp = Application.ActiveExplorer
Set myOlSel = myOlExp.Selection
For x = 1 To myOlSel.Count
'----------------------------------------------
Dim objApp As Outlook.Application
Dim GetCurrentItem As Object
Set objApp = Application
On Error Resume Next
Select Case TypeName(objApp.ActiveWindow)
Case "Explorer"
Set GetCurrentItem = objApp.ActiveExplorer.Selection.item(x)
Case "Inspector"
Set GetCurrentItem = objApp.ActiveInspector.CurrentItem
End Select
GetCurrentItem.UnRead = False
Set objApp = Nothing
'-----------------------------------------------
Set itm = GetCurrentItem
sPath = "C:\Users\Ray\"
sFile = sPath & "Macro.xlsm"
If Not itm Is Nothing Then
'de lo contrario, se crea un Reply del correo en formato HTML
Set rpl = itm.Reply
rpl.BodyFormat = olFormatHTML
'rpl.Display
End If
Set objDoc = rpl.GetInspector.WordEditor
txt = objDoc.Content.Text
'||||||||||||||||||||||||||||||||||||||||||||||
Set xlApp = CreateObject("Excel.application")
xlApp.Visible = True
Set wb = xlApp.Workbooks.Open(sFile)
xlApp.Windows("Macro.xlsm").Activate
'Set wb = ActiveWorkbook
'||||||||||||||||||||||||||||||||||||||||||||||
For i = LBound(Split(txt, Chr(13)), 1) To UBound(Split(txt, Chr(13)), 1)
wb.Worksheets(x).Range("A" & i + 1).Value = Split(txt, Chr(13))(i)
Next i
xlApp.Windows("Macro.xlsm").Close SaveChanges:=True
xlApp.Workbook.Close SaveChanges:=False
'------------------------------------------------------
Next x
'------------------------------------------------------
'the instances should closed but not working, instances are empty
For Each wb In xlApp
wb.Close SaveChanges:=False
Next
End Sub
done, I added xlApp.Quit after saving files and deleted the last part For Each wb In xlApp...
I'd like to ask you for the help with the Access VBA code, that would import all the data from 1 specified query table from the Access database (currently open database) to MS Excel (the file, that could be selected by the user).
I'm currently having this piece of code, but I'm getting the error message saying:
"Run-time error '-2147023170 (800706be)':
Automation error The remote procedure call failed."
Would any of you know how to fix the connection?
Option Explicit
Option Compare Database
Public Sub CopyRstToExcel_test()
'On Error GoTo CopyRstToExcel_Err
Dim sPath As String
Dim fd As FileDialog
Dim oExcel As Object
Dim oExcelWrkBk As Object
Dim oExcelWrSht As Object
Dim dbs 'Added
Dim qdfName As String
Dim fRecords As Boolean
Dim rst As dao.Recordset
Dim iCols As Integer
'-------------------------------------------------------------------------------------------------------------------------------------------------------------------
' Select the file and identify the path leading to the file
'-------------------------------------------------------------------------------------------------------------------------------------------------------------------
'Define database you want to work with
Set dbs = CurrentDb
'Select the Excel file you want to work with
Set fd = Application.FileDialog(msoFileDialogFilePicker)
'Define the path
If fd.Show = -1 Then
sPath = fd.SelectedItems(1)
End If
MsgBox sPath
'-------------------------------------------------------------------------------------------------------------------------------------------------------------------
' Defining names of variables
'-------------------------------------------------------------------------------------------------------------------------------------------------------------------
'Defining variables (queries/tables)
qdfName = "Query_1"
'------------------------------------------------------------------------------------------------
'Copying the data from Access into the new Excel
'------------------------------------------------------------------------------------------------
Set rst = CurrentDb.OpenRecordset(qdfName, dbOpenSnapshot)
fRecords = False
If rst.EOF = False Then
fRecords = True
Set oExcel = CreateObject("Excel.Application")
Set oExcelWrkBk = GetObject(sPath)
oExcel.Visible = True
oExcel.ScreenUpdating = False
Set oExcelWrSht = oExcelWrkBk.Sheets(1)
For iCols = 0 To rst.Fields.Count - 1
oExcelWrSht.Cells(9, iCols + 2).Value = rst.Fields(iCols).Name
Next
oExcelWrSht.Range(oExcelWrSht.Cells(9, 2), _
oExcelWrSht.Cells(9, rst.Fields.Count)).Font.Bold = True
oExcelWrSht.Range("B10").CopyFromRecordset rst
oExcelWrSht.Range(oExcelWrSht.Cells(9, 2), _
oExcelWrSht.Cells(rst.RecordCount + 9, rst.Fields.Count)).Columns.AutoFit
oExcelWrSht.Range("A1").Select
End If
'------------------------------------------------------------------------------------------------
CopyRstToExcel_Done:
On Error Resume Next
If fRecords = True Then
oExcel.Visible = True
oExcel.ScreenUpdating = True
End If
Set oExcelWrSht = Nothing
Set oExcelWrkBk = Nothing
Set oExcel = Nothing
Set rst = Nothing
''Error message:
'CopyRstToExcel_Err:
' MsgBox Err & ": " & Error, vbExclamation
' Resume CopyRstToExcel_Done
' Resume
'------------------------------------------------------------------------------------------------
End Sub
In this step, I only want to copy the data in the first sheet, but later on I would also like to specify the name of the sheet and I've got already prepared templates I want to copy the data over.
Thank you for your help!
Try to replace
Set oExcelWrkBk = GetObject(sPath)
by
Set oExcelWrkBk = oExcel.Workbooks.Open(sPath)
Also I'd recommend to replace
Set rst = CurrentDb.OpenRecordset(qdfName, dbOpenSnapshot)
by
Set rst = dbs.OpenRecordset(qdfName, dbOpenSnapshot)
Open specified worksheet:
Set oExcelWrSht = oExcelWrkBk.Sheets("MyWorksheetName")
I'm exporting an MS Access query to a template, doing some formatting, and then saving the template as a new name. When this is all done, I have an orphan MS Excel process that is interfering when the function is called again. I'm thinking this is either a problem with how I'm using ranges or a problem with my cleanup at the end.
Also I'm a novice coder so if anyone has any tips and tricks that I can take advantage of to make this better I'm always receptive.
Updated code after Andre's comments
Updated code after Rory's comments
Public Function OpenOrders(strSupplier As String)
'Excel file variables
Dim xlapp As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Dim xlsLRow As Long
Dim xlsLCol As Long
'Access variables
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
'Set up access objects
strSQL = "SELECT * FROM qryOpenOrderReport WHERE [Supplier Cd] = '" & strSupplier & "';"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL)
'Set up excel connection
Set xlapp = CreateObject("Excel.Application")
Set wb = xlapp.Workbooks.Open(Application.CurrentProject.Path & "\Open Order Template.xlsx")
Set ws = wb.Worksheets(1)
xlapp.Visible = True
'Make sure the form is clear
xlsLRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).row
xlsLCol = ws.UsedRange.Columns(ws.UsedRange.Columns.Count).Column
ws.Range("A2", ws.Cells(xlsLRow, xlsLCol)).ClearContents
'Copy recordset to worksheet
ws.Cells(2, 1).CopyFromRecordset rs
rs.Close
'Copy formats down and autofit
xlsLRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).row
xlsLCol = ws.UsedRange.Columns(ws.UsedRange.Columns.Count).Column
xlapp.CutCopyMode = False
ws.Range(ws.Cells(2, 1), ws.Cells(2, xlsLCol)).Copy
ws.Range(ws.Cells(3, 1), ws.Cells(xlsLRow, xlsLCol)).PasteSpecial (xlPasteFormats)
ws.UsedRange.Columns.AutoFit
'Clean up
xlapp.DisplayAlerts = False
Set ws = Nothing
wb.SaveAs Application.CurrentProject.Path & "\Open Orders\" & strSupplier & ".xlsx"
wb.Close True
Set wb = Nothing
xlapp.Quit
Set xlapp = Nothing
End Function
All of your Cells calls need to be changed to ws.Cells. That is what is causing your orphaned process.
My guess is that you still have an active reference to ws when doing the clean up, which prevents Excel from quitting.
I suggest doing it in this order:
'Clean up
xlapp.DisplayAlerts = False
Set ws = Nothing
wb.Close True, strSupplier
Set wb = Nothing
xlapp.Quit
Set xlapp = Nothing
ws.Range("A2", "XFD1048576").ClearContents seems a little radical :) - you can use .UsedRange for that.
An additional note:
After opening a recordset, you can never be in a situation where rs.EOF is False, but rs.BOF is True. So it is not necessary to test for rs.BOF.
With changing the loop to Do While, the If Not (rs.EOF And rs.BOF) Then becomes superfluous:
Set rs = db.OpenRecordset(strSQL)
Do While Not rs.EOF
' ...
rs.MoveNext
Loop
You have to be extremely specific with objects of Excel, opening them and closing in reverse order. Here's a skeleton that works:
Dim xls As Excel.Application
Dim wkb As Excel.Workbook
Dim wks As Excel.Worksheet
Dim rng As Excel.Range
Set xls = New Excel.Application
Set wkb = xls.Workbooks.Open("c:\test\workbook1.xlsx")
Set wks = wkb.Worksheets(1)
Set rng = wks.<somerange> ' Cells or whatever.
' Do stuff.
' Clean up.
Set rng = Nothing
wks.Name = "My New Name"
wkb.Close True
Set wks = Nothing
Set wkb = Nothing
xls.Quit
Set xls = Nothing
Don't ever use wkb.Sheets for a WorkSheet.