Welcome To The Home Of The Visual FoxPro Experts  
home. signup. forum. archives. search. google. articles. downloads. faq. members. weblogs. sponsors. rss.
 From: Cetin Basoz
  Where is Cetin Basoz?
 Izmir
 Turkey
 Cetin Basoz
 To: Christopher Kemper
  Where is Christopher Kemper?
 Valparaiso
 Indiana - United States
 Christopher Kemper
 Tags
Subject: RE: Vista Excel Compatibility w/ Worksheets
Thread ID: 153535 Message ID: 153776 # Views: 98 # Ratings: 2
Version: Visual FoxPro 9 Category: Classes and Objects
Date: Friday, December 07, 2007 7:14:28 PM         
   


> Cetin,
> If you have any suggestions as to how to append to a DBF using OLEDB, it would be much appreciated.
>
> An alternative solution to my problem is...
> If you know how to use automation to save in 95 format, that could be used as well.
> (for some reason, if you save an 2007Excel file in 95.xls format, it works under foxPro... but not if saved under 97/2003.xls, or its native 2007.xlsx format)
>
> file1 = C:\temp\green.xls
> file2 = C:\temp\white.xls
>
> I am able to save as 97 format by using:
> oworkbook.SaveAs(file1)
>
> But am unable to convert using:
> copy file (file1) to (file2) TYPE XL5
>
> Thanks again
> Chris

Chris,
Multiple questions that is, hope I won't miss anyone while replying:)

Append to a dbf - that's from Excel to VFP. For excel you can use ODBC/OleDB driver to read excel tables (Excel tables are table like areas on a sheet and need not be starting from upper left corner. Besides sheets with 'tables' there system tables. I don't know how would you eliminate those system tables during getting the data as cursors but as long as you know sheet names to get the data from you can check sheet name vs table name. Excel 'tables' are named like SheetName$, may contain spaces and limited to 31 in length).

OK now that be a long post.

Read excel tables into cursors (here it's a quick version that simply names cursors as sheet01,..sheetNN):
#Define adSchemaCatalogs 1
#Define adSchemaColumns 4
#Define adSchemaTables 20
Close Databases All
Local lFirstRowHasHeader,lReturnAllAsText,lcDataSource,lcConStr
lFirstRowHasHeader = .T.
lReturnAllAsText = .F.

lcDataSource = 'c:\temp\ReadMeBack.xls'
*lcDataSource = 'c:\temp\ReadMeBack.xlsx'

* Excel97-2003
lcConStr = ;
  'Provider=Microsoft.Jet.OLEDB.4.0;'+;
  'Data Source='+m.lcDataSource+';'+;
  'Extended Properties="Excel 8.0;HDR='+;
  IIF(m.lFirstRowHasHeader,'Yes','No')+';IMEX='+;
  IIF(m.lReturnAllAsText,'1','0')+'"'

* Excel2007
*lcConStr = ;
  'Provider=Microsoft.ACE.OLEDB.12.0;'+;
  'Data Source='+m.lcDataSource+';'+;
  'Extended Properties="Excel 12.0;HDR='+;
  IIF(m.lFirstRowHasHeader,'Yes','No')+'"'

ReadSchema(m.lcConStr)
Select TableList
Scan
  lcTableName = Trim(Table_name)
  lcLocalCursor = Textmerge("Sheet<<PADL(RECNO(),2,'0')>>")
  ADOQuery(m.lcConStr, Textmerge("select * from [<<m.lcTableName>>]"), m.lcLocalCursor)
Endscan

Procedure ADOQuery(tcConStr,tcQuery,tcCursorName)
  Local oConn As 'ADODB.Connection'
  Local oRS As ADODB.RecordSet
  oConn = Createobject('ADODB.Connection')
  oConn.Mode= 1  && adModeRead
  oConn.Open( m.tcConStr )
  oRS = oConn.Execute(m.tcQuery)
  RS2Cursor(oRS,m.tcCursorName)
  oRS.Close
  oConn.Close
Endproc

Procedure ReadSchema(tcConStr)
  Local oConn As 'ADODB.Connection'
  Local rstSchema As ADODB.RecordSet
  oConn = Createobject('ADODB.Connection')
  oConn.Mode = 1  && adModeRead
  oConn.Open( m.tcConStr )
  rstSchema = oConn.OpenSchema(adSchemaTables)
  *rstSchema = oConn.OpenSchema(adSchemaColumns)
  RS2Cursor(rstSchema,'TableList')
  rstSchema.Close
  oConn.Close
Endproc

Procedure RS2Cursor(toRS, tcCursorName) && simple single cursor - not intended for complex ones
  tcCursorName = Iif(Empty(m.tcCursorName),'ADORs',m.tcCursorName)
  Local xDOM As 'MSXML.DOMDocument'
  xDOM = Createobject('MSXML.DOMDocument')
  toRS.Save(xDOM, 1)
  Xmltocursor(xDOM.XML, m.tcCursorName)
Endproc


To create the data to use in sample you could use OLEDB again, this time from VFP to Excel (my favorite method BTW over any other export to Excel):
* These represent complex SQL as a sample
Select emp_id,First_Name,Last_Name,;
  Title,Notes ;
  from (_samples+'\data\employee') ;
  into Cursor crsEmployee ;
  readwrite
Replace All Notes With Chrtran(Notes,Chr(13)+Chr(10),Chr(10))

Select cust_id,company,contact,Title,country,postalcode ;
  from (_samples+'\data\customer') ;
  into Cursor crsCustomer ;
  nofilter

Select * ;
  from (_samples+'\data\orders') ;
  into Cursor crsOrders ;
  nofilter

Select * ;
  from (_samples+'\data\orditems') ;
  into Cursor crsOrderDetail ;
  nofilter

Select * ;
  from (_samples+'\data\products') ;
  into Cursor crsProducts ;
  nofilter

* Now we want to get these on 3 sheets
* Sheet1: Employees only
* Sheet2: Customers only
* Sheet3: Orders, ordItems, Products layed out horizontally

Local oExcel
oExcel = Createobject("Excel.Application")
With oExcel
  .DisplayAlerts = .F.
  .Workbooks.Add
  .Visible = .T.
  With .ActiveWorkBook
    For ix = 1 To 3 && We want 3 Sheets
      If .sheets.Count < m.ix
        .sheets.Add(,.sheets(.sheets.Count)) && Add new sheets
      Endif
    Endfor
    * Name the sheets
    .WorkSheets(1).Name = "Employees"
    .WorkSheets(2).Name = "Customers"
    .WorkSheets(3).Name = "Order, OrderDetail, Products" && max sheetname is 31 chars

    * Start sending data
    * First one has headers specified
    VFP2Excel('crsEmployee',    .WorkSheets(1).Range("A1"), ;
      "Id,First Name,Last Name,Employee Title,Comments about employee" ) && To sheet1, start at A1
    VFP2Excel('crsCustomer',    .WorkSheets(2).Range("A1") ) && To sheet2, start at A1
    VFP2Excel('crsOrders',      .WorkSheets(3).Range("A1") ) && To sheet3, start at A1
    * Need to know where to put next
    * Leave 2 columns empty - something like 'G1'
    lcRange = _GetChar(.WorkSheets(3).UsedRange.Columns.Count + 3) + '1'
    * To sheet3, start at next to previous
    VFP2Excel('crsOrderDetail', .WorkSheets(3).Range(m.lcRange) )

    lcRange = _GetChar(.WorkSheets(3).UsedRange.Columns.Count + 3) + '1'
    * To sheet3, start at next to previous
    VFP2Excel('crsProducts',    .WorkSheets(3).Range(m.lcRange) )

    #Define xlJustify                                         -4130
    #Define xlTop                                             -4160

    * I just happen to know notes in at column 5 from SQL
    * No need to query from excel to keep code simple
    * Lets format that column specially instead of leaving
    * at the mercy of Excel's autofitting
    .WorkSheets(1).UsedRange.VerticalAlignment = xlTop && set all to top
    With .WorkSheets(1).Columns(5)
      .ColumnWidth = 80 && 80 chars width
      .WrapText = .T.
      *      .HorizontalAlignment = xlJustify && doesn't work good always
    Endwith

    * Finally some cosmetic stuff
    For ix=1 To 3
      With .WorkSheets(m.ix)
        .Columns.AutoFit
        .Rows.AutoFit
      Endwith
    Endfor

    .WorkSheets(1).Activate
  Endwith
  #Define xlWorkbookNormal                                  -4143
  * for another test save in current Excel version
  .ActiveWorkBook.SaveAs('c:\temp\ReadMeBack.xls',xlWorkbookNormal)
Endwith


* Warning:
* Copying to a free table (might be dbc based)
* temporarily and using field names as headers
* if not passed.
* This might lead to problems like:
* Truncated fieldnames of 2 columns collide and copy to errors
* Field names might be cryptic for the enduser
Function VFP2Excel
  Lparameters tcCursorName, toRange, tcHeaders
  tcCursorName = Iif(Empty(m.tcCursorName),Alias(),m.tcCursorName)
  Local loConn As AdoDB.Connection, loRS As AdoDB.Recordset,;
    lcTemp, oExcel,ix

  lcTemp = Forcepath(Sys(2015)+'.dbf',Sys(2023))
  Select (m.tcCursorName)
  Copy To (m.lcTemp)
  loConn = Createobject("Adodb.connection")
  loConn.ConnectionString = "Provider=VFPOLEDB;Data Source="+Sys(2023)
  loConn.Open()
  loRS = loConn.Execute("select * from "+m.lcTemp)

  * Use first row for headers
  Local Array aHeader[1]

  toRange.Offset(1,0).CopyFromRecordSet( loRS )  && Copy data starting from headerrow + 1
  For ix=1 To Iif( !Empty(m.tcHeaders), ;
      ALINES(aHeader, m.tcHeaders,1,','), ;
      loRS.Fields.Count )
    toRange.Offset(0,m.ix-1).Value = ;
      Iif( !Empty(m.tcHeaders), ;
      aHeader[m.ix], ;
      Proper(loRS.Fields(m.ix-1).Name) )
    toRange.Offset(0,m.ix-1).Font.Bold = .T.
  Endfor

  loRS.Close
  loConn.Close
  Erase (m.lcTemp)
Endfunc

* Return A, AA, BC etc noation for nth column
Function _GetChar
  Lparameters tnColumn && Convert tnvalue to Excel alpha notation
  If m.tnColumn = 0
    Return ""
  Endif
  If m.tnColumn <= 26
    Return Chr(Asc("A")-1+m.tnColumn)
  Else
    Return 	_GetChar(Int(Iif(m.tnColumn % 26 = 0,m.tnColumn - 1, m.tnColumn) / 26)) + ;
      _GetChar((m.tnColumn-1)%26+1)
  Endif
Endfunc



> An alternative solution to my problem is...
> If you know how to use automation to save in 95 format, that could be used as well.
> (for some reason, if you save an 2007Excel file in 95.xls format, it works under foxPro... but not if saved under 97/2003.xls, or its native 2007.xlsx format)
>
> file1 = C:\temp\green.xls
> file2 = C:\temp\white.xls
>
> I am able to save as 97 format by using:
> oworkbook.SaveAs(file1)
>

VFP doesn't really know formats later than 5 and I even doubt it knows 5 (somewhere I have demo code to make it crash with 5 format as well. BUT that might have been corrected, I haven't used Import or append from with excel for years).
Anyway if you want to save in a old format like 5:

#define xlExcel9795 43
lcXLS = getfile('XLS')
oExcel = createobject('Excel.Application')
with oExcel
 .DisplayAlerts = .f.
 .WorkBooks.Open( m.lcXLS )
 .SaveAs(m.lcXLS, xlExcel9795)
 .Quit
endwith


> But am unable to convert using:
> copy file (file1) to (file2) TYPE XL5

AFAIK the only supported 'copy' with type XLS5 is:

use someTable
copy to (m.lcXLS) type xl5


Beware it could fail, plus it doesn't do its job well under all versions of VFP. Better alternatives:
1)OLEDB copying (as shown above)
2)
copy to (m.lcXLS) type CSV

3)
copy to (m.lcXLS) type fox2x

4)
copy to (m.lcXLS) type delimited
&& this one is followed by automation oExcel.OpenText(...).
5)Write out the table as HTML (textmerge) and open with excel
6)Like 5 but XML. Some Excel versions do not understand and need some automation.
7)Full XML creation of xls file (complex IMHO if you don't have libraries ready).

Cetin Basoz



COMPLETE THREAD
Vista Excel Compatibility w/ Worksheets Posted by Christopher Kemper @ 12/5/2007 7:23:29 PM
RE: Vista Excel Compatibility w/ Worksheets Posted by Cetin Basoz @ 12/5/2007 7:29:33 PM
RE: Vista Excel Compatibility w/ Worksheets Posted by Christopher Kemper @ 12/5/2007 7:34:00 PM
RE: Vista Excel Compatibility w/ Worksheets Posted by Christopher Kemper @ 12/6/2007 11:08:23 PM
RE: Vista Excel Compatibility w/ Worksheets Posted by Cetin Basoz @ 12/7/2007 7:14:28 PM
RE: Vista Excel Compatibility w/ Worksheets Posted by Christopher Kemper @ 12/12/2007 6:00:07 PM