Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
134 views

Excel

This document provides guidance on automating Microsoft Excel using VFP objects. It discusses how to connect to Excel, open and create workbooks, interact with cells and ranges, find and select cells, format text, iterate through worksheets, and close Excel. Tips are included, such as using constants for consistency, setting properties like DisplayAlerts to False for unattended automation, and recording macros to learn object methods. The document also addresses potential issues like backward compatibility and transferring memo fields from VFP.

Uploaded by

ucie
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
134 views

Excel

This document provides guidance on automating Microsoft Excel using VFP objects. It discusses how to connect to Excel, open and create workbooks, interact with cells and ranges, find and select cells, format text, iterate through worksheets, and close Excel. Tips are included, such as using constants for consistency, setting properties like DisplayAlerts to False for unattended automation, and recording macros to learn object methods. The document also addresses potential issues like backward compatibility and transferring memo fields from VFP.

Uploaded by

ucie
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 8

Wide

Excel Automation
(Updated: 2007.08.24 01:47:10 PM)
Namespace: VFP
Edit -Find- Recent Changes Categories Road Map [A-F] [G-P] [Q-Z] New Topic Home
Go
Search:
For clarity's sake, the full object hierarchy is used for each command. Using a 'With
oExcel' will tend to make your code more readable.

Connecting to Excel
We'll assume throughout this page that you named your Excel object oExcel and your
workbook oWorkbook.
oExcel = CreateObject("Excel.Application")
if vartype(oExcel) != "O"
* could not instantiate Excel object
* show an error message here
return .F.
endif

Constants for Excel


Excel Constants documents the constants that Excel's macros use (xlThis, xlThat etc)
2007/08/30 Mike Mohr - Excel 2003 Constants MSDN Library
2007/08/30 Mike Mohr - Excel 2007 (office 12) Constants MSDN Library

Opening an existing XLS


oWorkbook = oExcel.Application.Workbooks.Open("C:\temp\test.xls")

Creating a blank workbook:


oWorkbook = oExcel.Application.Workbooks.Add()

Creating a new workbook based on a template:


oWorkbook = oExcel.Application.Workbooks.Add("C:\temp\template.xlt")

You can turn off Excel's alerts with the DisplayAlerts property: when set to False, Excel
automatically chooses the default answer to any message:
oExcel.DisplayAlerts = .F.
oWorkbook.Close() && Unsaved changes will be discarded
oExcel.DisplayAlerts = .T.
Saving a workbook as an Excel 97/2003 spreadsheet from Excel 2007
Using SaveAs while automating Excel 2007 creates a 2007 style workbook with an XLS
extension (regardless of compatibility settings) unless the file format is specified:
if val(oExcel.Version) > 11
oWorkbook.SaveAs("C:\temp\foobar.xls", 56) && xlExcel8
else
oWorkbook.SaveAs("C:\temp\foobar.xls")
endif

Controlling visibility
If the Excel window is not visible it is harder for the user to interact with Excel. This
makes it slightly safer for your automation as the user is less likely to issue commands in
the middle of your automation.
oExcel.visible = .T.
oExcel.visible = .F.

Controlling Interaction
Also, if it is preferred that Excel be seen during automation set these two properties to .F.
oExcel.Application.UserControl=.F.
oExcel.Application.Interactive=.F.

After completing automation, return their value to .T. to allow the user to start interaction
oExcel.Application.UserControl=.T.
oExcel.Application.Interactive=.T.

Storing data to a cell


oExcel.Range("b2").Value = "Hello world"

Set the font color and style


oExcel.Range("B6").font.bold = .t.
oExcel.Range("B6").font.colorindex = 3 && red

or

oExcel.Range("B6").Select()
oExcel.Selection.font.colorindex = 3 && red
oExcel.Selection.font.bold = .t.

-- David Fung

Getting data into Excel


Assuming your table is selected. First the easy way. You can make a new file in an old
Excel format which all version should be able to read.
copy to c:\mytable.xls type xl5

Or if you have a pre-formatted template (.XLS or .XLT) that you want to paste into. Note
that this method will not handle Memo fields.
_VFP.DataToClip(,,3) && current table onto the clipboard,
delimited with tab
oExcel.Range("A1").Select
oExcel.ActiveSheet.Paste() && from clipboard. since
delimited with tab, store data into columns

Selecting a range using the Cells collection


oExcel.Range(oExcel.cells(1, 1), oExcel.Cells(3.3)).Select

Resize all columns


oExcel.ActiveSheet.UsedRange.EntireColumn.Autofit

Insert two rows before the first row


oExcel.Rows("1:2").Insert(-4121) && xlDown

-- David Fung

Closing Excel
You'll still need to handle closing questions like saving changes and file format changes.
And you'll need to release your oExcel object
oExcel.quit()

Closing Excel with all changes discarded - no question asked


oExcel.DisplayAlerts = .F.
oExcel.ActiveWindow.Close(.f.) && assuming only one active window
oExcel.quit()

-- David Fung

Iterate through Excel's Worksheets


For i=1 To oExcel.ActiveWorkbook.Sheets.Count
? oExcel.ActiveWorkbook.Sheets(i).Name
Endfor

Finding text in Excel


Searching for "Tax"
oExcel.Range("A1:H250").Find("Tax").Select && simple default search
* Alternately
* Range.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection,
MatchCase, MatchByte, SearchFormat)
oExcel.Range("A1:H250").Find("Tax", oExcel.ActiveCell, -4123, 1).Select

Range("A1:H250") specifies that we're searching columns A to H (inclusive) and rows 1-


250.
oExcel.ActiveCell is where to start searching, and -4123 is the constant for xlFormulas. I
theorize that this means 'if there is a formula, search its code rather than its output.' 1 is
the constant for xlWhole, meaning match against all the text in the cell. You could swap
in 2 to get partial matches.
-- Tom Cerul
You have to be careful when specifying the extra parameters to Find as they persist
between searches, as specified in the Excel documentation:
The settings for LookIn, LookAt, SearchOrder, and MatchByte are saved each time you
use this method. If you don't specify values for these arguments the next time you call the
method, the saved values are used. Setting these arguments changes the settings in the
Find dialog box, and changing the settings in the Find dialog box changes the saved
values that are used if you omit the arguments. To avoid problems, set these arguments
explicitly each time you use this method.

-- Stuart Dunkeld

Developing new code


Sometimes the easiest way to figure out how to code an automation routine is this: open
Excel, tell it to record a macro, do the task you want to automate. Stop the recording and
look at the code that it generated.

Putting it all together, a runnable example

First, COPY TO all fields (or some) in Excel Format


#define xlLastCell 11
#define xlMaximized -4137
#define xlRangeAutoformatClassic2 2
#define xlPortrait 1

use MyTable && or SELECT * INTO MyCursor

cFileName = "MyXLSFile" && or whatever, including path


*copy to (cFileName) fields (cFields) TYPE xls
copy to (cFileName) TYPE xls

* then open excel and make the data look good, like this
oExcel = CreateObject("Excel.Application")
if vartype(oExcel) != "O"
* could not instantiate Excel object
* show an error message here
return .F.
endif

* make excel visible during development


*oExcel.visible = .T.

* open the workbook you just created


oExcel.SheetsInNewWorkBook = 1
oWorkbook = oExcel.Workbooks.Open(cFileName)

* rename the Sheet to whatever you like


oActiveSheet = oExcel.ActiveSheet
oActiveSheet.Name = "MyData"

oExcelApp = oExcel.Application
oExcelApp.WindowState = xlMaximized
* find address of last occupied cell
lcLastCell = oExcel.ActiveCell.SpecialCells(xlLastCell).Address()

* resize all columns


lnMarker1 = at("$",lcLastCell,1) && i.e. 1 when lcLastCell = "$AF$105"
lnMarker2 = at("$",lcLastCell,2) && i.e. 4 when lcLastCell = "$AF$105"
lnStartPos = lnMarker1 + 1
lnStrLen = lnMarker2 - lnStartPos
oExcel.Columns("A:" + substr ;
(lcLastCell,lnStartPos,lnStrLen)).EntireColumn.AutoFit

* you can even add a nice autoformat


oExcel.Range("A" + alltrim(str(nTOPBLANKROWS+1)) + ":" +
lcLastCell).Select
oExcel.Selection.AutoFormat(xlRangeAutoformatClassic2,.t.,.t.,.t.,.t.,.t
.,.t.)

* set Excel Print Area


oActiveSheet.PageSetup.PrintArea = "$A$1:" + lcLastCell

* define printed page footer


With loActiveSheet.PageSetup
*.LeftHeader = ""
*.CenterHeader = ""
*.RightHeader = ""
.LeftFooter = "&BMy Footer goes here&B"
.CenterFooter = "&D"
.RightFooter = "Page &P"
*.PrintHeadings = .F.
.PrintGridlines = .F.
.CenterHorizontally = .T.
.CenterVertically = .F.
.Orientation = xlPortrait
endwith

* save Excel file in new Excel format (COPY TO XLS uses old format)
oWorkbook.Save()

* display finished product to the user


oExcel.visible = .T.

-- Alex Feldstein

Sometimes the Last Cell is not up-to-date after deleting a row in Excel,
Calling ActiveSheet.UsedRange after deleting a row will keep Last Cell
up-to-date.

loExcel = createobject('Excel.Application')
loExcel.Workbooks.Open(tcFile)
loExcel.Rows("1").Delete(xlUp)
lnLastRowIncorrect = loExcel.Cells.SpecialCells(xlCellTypeLastCell).Row
loExcel.ActiveSheet.UsedRange && add this line
lnLastRowCorrect = loExcel.Cells.SpecialCells(xlCellTypeLastCell).Row
-- David Fung

Having worked with excel a lot a few notes:


Office applications are not as much backward compatible as VFP is. For that reason I
suggest not to use VFP's direct Excel related commands like:

copy to myExcel type xls && or xl5,xl8...


import ... type xl8

etc. These commands are version dependant directly within VFP themselve. You
immediately lose data with these commands.
-The number of rows you can copy is limited for example (VFP5 copied 16384 max
while VFP9 copies 65536 max, but as new Excel versions come into market those limits
are not sufficient anymore).
-Memo fields are immediately dropped as with any 'copy to' command

Data To Clip() - Do not use Data To Clip() for Excel transfers.


-You lose Memo fields but worse you get a transform() version of memo fields.
-You're limited on rowcount that you can transfer with this method. There is no exact
number and sounds like related with Excel version + available memory. It's possible you
end with much fewer rows than you could transfer using 'copy to'. Likely you'd get
unpredictable exceptions.

Instead you can use:

Copy to myExcel.xls type fox2x && actually creating a dBaseIII compatible file. Excel
recognizes internally
Copy to myExcel.csv type csv && CSV files are natively recognized

Both fail to transfer memo fields and CSV might have problems with datatypes converted
correctly (mostly with date fields) but in general are better than Data To Clip() and
'copy ... type xls'.

Similar to Data To Clip() you can copy to a tab delimited file, read it into clipboard with
FileToStr() and pasteSpecial() in Excel. Works better than Data To Clip() but it again falls
short of transferring memo fields.

Excel (especially newer versions) also recognizes XML and HTM ( table tags ).

My best preferance is to transfer data using ADO instead. Passing with ADO uses Excel's
own VBA commands to 'paste' the data. Here is a sample sending data to Excel and
putting data starting at A10:
LOCAL oExcel
oExcel = Createobject("Excel.Application")
With oExcel
.WorkBooks.Add
.Visible = .T.
VFP2Excel(_samples+'data\testdata.dbc','select * from
employee',.ActiveSheet.Range('A10'))
Endwith

function VFP2Excel
lparameters tcDataSource, tcSQL, toRange
Local loConn As AdoDB.Connection, ;
loRS As AdoDB.Recordset,;
ix
loConn = Createobject("Adodb.connection")
loConn.ConnectionString = "Provider=VFPOLEDB;Data
Source="+m.tcDataSource
loConn.Open()
loRS = loConn.Execute(m.tcSQL)

FOR ix=1 TO loRS.Fields.Count


toRange.Offset(0,m.ix-1).Value =
PROPER(loRs.Fields(m.ix-1).Name)
toRange.Offset(0,m.ix-1).Font.Bold = .t.
ENDFOR
toRange.Offset(1,0).CopyFromRecordSet( loRS )
loRs.Close
loConn.Close

Note that .Visible = .T. is very early in code just after adding the workbook. Having that
"later after you're done with populating cells males things faster" is a myth. Surprisingly
having it early in code makes your code faster in many cases.

My suggestions:
Working with Excel means you're doing COM calls using VBA which by nature is slow.
Therefore, whenever possible, do things at VFP side and call Excel automation
commands as few as you can. ie:

Instead of this:
for ix = 1 to 5000
for jx=1 to 10
.Cells(m.ix,m.jx).Value = m.ix*100+m.jx
endfor
endfor

Do this:
dimension aExcelData[5000,10]
for ix = 1 to 5000
for jx=1 to 10
aExcelData[m.ix,m.jx] = m.ix*100+m.jx
endfor
endfor

WITH oExcel.ActiveWorkBook.ActiveSheet
.Range(.Cells(1,1), .Cells(5000,10)).Value = GetArrayRef('aExcelData')
endwith

PROCEDURE GetArrayRef(tcArrayName)
RETURN @&tcArrayName

Above code also shows another alternative to transferring data to Excel via array instead
of pasting.

Cetin Basoz

Contributors: Alex Feldstein Tom Cerul Stuart Dunkeld David Fung Cetin Basoz

Category Code Samples Category Automation


Edit -Find- Recent Changes Categories Road Map [A-F] [G-P] [Q-Z] New Topic Home
Go
Search:
http://fox.wikis.com - a low-impedance, fat-free Visual Foxpro site. - 2009.08.03
10:40:55 AM EST

You might also like