I use Access 2007, SQL Server 2005 and 2008, and C#.net with VS20010.
I need to validate, then import an Excel workbook that has been posted to a SP document library. I wrote the program using VBA in A2k7 so I could get it working, get all my thoughts together, etc. Shortly, I'm going to start converting it to C# so I can run it on a scheduled basis from my SQL Server agent. Authentication may be fun, we'll see.
In my Access VBA program, I use a simple statement to create a multi-dimensional array of "variants". This allows me to test every single cell for the correct type before I try to assign it to a local variable. Certain cells must be numeric. Others, I use as strings, even if they're numeric. Since the data may be hand-entered, I could see virtually any content in the cells...one of the reasons I don't just import it, since most import vehicles (ACCESS, SSIS) use Excel's determination as to the column's "type".
'
' Get the range of used cells from the worksheet and stuff it into an array
'
xlApp.visible = False 'Don't let the workbook be shown
Set xlWB = xlApp.Workbooks.Open(URL) 'Open the workbook
Set xlSH = xlWB.Worksheets(1) 'Must be the first worksheet in the workbook
HandleMessages 1, 0, 1, "Working on Workbook " & xlWB.NAME & ", Worksheet " & xlSH.NAME
Set xlRA = xlSH.UsedRange 'xlRA is the range of cells in the first workbook that are "USED"
strSheetArray = xlRA 'This sets an array of variants to the two dimensional range xlRA
So, going to C#, what do I do about the lack of a non-typed "variant"? How do I proceed? Anyone done it?
You can use dynamic in C# 4.0.
http://msdn.microsoft.com/en-us/library/dd264736.aspx
The type is a static type, but an object of type dynamic bypasses
static type checking. In most cases, it functions like it has type
object. At compile time, an element that is typed as dynamic is
assumed to support any operation. Therefore, you do not have to be
concerned about whether the object gets its value from a COM API, from
a dynamic language such as IronPython, from the HTML Document Object
Model (DOM), from reflection, or from somewhere else in the program.
However, if the code is not valid, errors are caught at run time.
The link shows examples of using dynamic with Excel.
Related
I'm trying to create a WinForms application that interacts with Excel using the Excel Object Library v.15.
I'm using this to get the Excel Application object
(Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
And I know I can get the Range for the named table I want by using
_application.Range["MyTableName"];
Now, the problem I'm facing is that if I have two workbooks opened at the same time and each one of them has a named table with the same name, I don't know which Range will be returned.
The Excel application is unique, so I cannot simply try to get the process based on the window title or something like that.
I can get the Workbook object based on its title:
_application.Workbooks.Cast<Workbook>().FirstOrDefault(w.Name.Equals(title))
However I cannot access the Ranges of a particular Workbook.
I know I could iterate through the Sheets of the Workbook element trying to find the table but I was wondering if there was another "cleaner" way.
I would appreciate any guideline on this.
Thanks,
Will
The simplest/most direct way I can think of would be to iterate through the Names collection, check the parent, and move on.
E.g:
For Each NamedRange as Range in MyApplication.Names
Dim Check as Object = TryCast(NamedRange.Parent, Workbook)
If Check Is Nothing Then
Check = TryCast(NamedRange.Parent, Worksheet)
Check = TryCast(Check.Parent, Workbook)
End If
If Not Check Is Nothing AndAlso Check.Name = "MyWorkbook" Then
'Do something
End If
Next
I'm stuck on the last hurdle to finish my program. I have a excel doc I want to import into the one I'm building in C#
wb.Sheets.Add();
Microsoft.Office.Interop.Excel.Worksheet staffCosts = (Microsoft.Office.Interop.Excel.Worksheet)wb.Worksheets[1];
staffCosts.Name = "Staff Costs";
staffCosts.QueryTables[1].Name = Path.GetFileNameWithoutExtension("C:\\tilldataoutput\\excelcreator\\excelcreator\\bin\\Debug\\Staff.xlsx");
Any help would be massively appreciated.
Take a look at this MSDN link, which states...
Returns the QueryTables collection that represents all the query
tables on the specified worksheet. Read-only.
Since you're getting a QueryTable by using an Index, you should check the collection first to check if any exist, or 2 in your case since you're looking at the second QueryTable.
I have an Excel template which I am opening, which has a pie chart bound to a table (ListObject), and I want to insert rows into the table. There are plenty of examples of how to ADD a table into a worksheet, but I can't find anywhere that lets me select the table.
I have tried:
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
oWB = (Excel._Workbook)(oXL.Workbooks.Open(LastFile));
oSheet = (Excel._Worksheet)oWB.ActiveSheet;
Excel.ListObject ValList = oSheet.ListObjects("ValueData");
but that gives the compile error:
Non-invocable member 'Microsoft.Office.Interop.Excel._Worksheet.ListObjects' cannot be used like a method. (CS1955)
I have also tried doing it with a macro, then trying to convert the VB to C#, but still with no joy.
For information, I am relatively new to C# Excel automation, although I have done quite a lot in C# and have done Excel COM object work using other languages.
good morning,
mr.Reband is right, i think... maybee this is worth a look? as the error says, taken the MSDN link into account, i think, oSheet.ListObjects is a field or property, rather than a method. so, your invocation is not allowed. cheers!
I'm writing an application level add-in for Excel in C#.
The add-in is to be used for getting data from a foreign source (the add-in provides some GUI options for this etc.) into Excel. This data is not going to be updated and sent back to the data source or anything like that - although the user is of course free to edit the data in the local Excel application.
The data arrives in an XML format and currently I have used a code generation tool to be able to deserialize the xml documents into C# objects. The data follows a relational model.
The things I'm thinking about right now:
Should I translate everything to a DataSet object with DataTables?
If I've done that, how can I then get this data into an Excel sheet? Is it possible to e.g. create a table in excel and databind to my datatables/dataset?
Really I don't think I want a "table" per se but just throw in the data into cells and the user can then work with the cells. Is it better then to just supply 2D arrays? But won't it be a pain to go from DataTable data rows to 2D arrays?
Some other questions as well...
What is the easiest/best way to read data back from Excel to C#? I think I'd mostly be satisfied with just getting 2D arrays here. But traversing the "Range" objects seems cumbersome. Must be some better way?
The sheet will likely have column names in the first row and then data in the rest of the rows. Is there any way for the C# code to recognize this when the user has selected the cells that make up my "table"? Or is this just something I'm going to have to take care of manually in the code?
I've never worked with this before so apologizing if some questions seem stupid. Any help is appreciated.
Here are some example from my previous work to open excel and get data from excel:
public class ExcelModule
{
private Excel.Application excelApp;
private Excel.Workbook excelBook;
private Excel.Worksheet excelSheet;
object misValue = System.Reflection.Missing.Value;
object oMissing = System.Reflection.Missing.Value;
public ExcelModule()
{
}
public void OpenWorksheet(string fileName, int sheetNum)
{
excelApp = new Excel.Application();
excelBook = excelApp.Workbooks.Open(fileName,
0,
true,
5,
"",
"",
true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows,
"\t",
false,
false,
0,
true,
1,
0);
excelSheet = (Excel.Worksheet)excelBook.Worksheets.get_Item(sheetNum);
}
public string GetValue(string cellAddress)
{
if (excelSheet.get_Range(cellAddress, cellAddress).Value2 != null)
return excelSheet.get_Range(cellAddress, cellAddress).Value2.ToString();
else
return "";
}
public int Close()
{
excelApp.Quit();
return 0;
}
~ExcelModule()
{
excelApp.Quit();
}
}
To write data into Excel you may use:
excelSheet.get_Range(cellAddress, cellAddress).Value2 = "your text";
Notes:
*I'm using VS10 with Office2007
Not sure why my question was downvoted... At least give reasons, how else can they become better next time around?
Anyway. The best solution, it seems to me, is to get my data into a DataSet and then create a ListObject in the Excel application and use it's data binding features to get my data into Excel.
Was not aware of this great control before.
Using range along with 2d array in Excel will give you beter performance. Here as you are deserializing incoming xml to object, there is no need to convert it into dataset then to 2d array. Would recommend in view layer of your code you directly tranform your object to 2d array and then bind with range in excel sheet. For reading back read data into 2d array from range and then tranform it back to object which you can serialize and send it back to server. Now how effectively or exactly you use range or array will depend on how data in your sheet looks. To distinguish between header and data you may have a look at named range, it can be helpful.
Excel tables (aka "ListObjects") give you formatting for free, and they are easy to use. The corresponding type is ListObject.
You can use them with LINQ, without having to manufacture a DataSet object:
ListObject myTable; // usually declared somewhere else, eg. via the designer
var data = from x in myObjects select new
{
Foo = x.Foo,
Bar = x.Bar
};
myTable.SetDataBinding(data.ToList());
This will fill the table with your data using reflection. In the example above, you will have two columns titled Foo and Bar, and as many rows as you had elements in myObjects.
Of course, you can use more complex queries. In your case, using Linq to XML is probably a good idea. The point is that you can do exactly what you want in a handful of lines.
Also, you can put any IList<object> into SetDataBinding.
Access added a new data type in the 2007 version--the Attachment type. We are currently working on a WinForms application with .NET 3.5 (C#) that uses an Access 2007 database. We want to be able to add new attachments through the WinForms interface. I can't seem to locate any information on how to insert or select attachment data with .NET. I did try using DAO (version 12) but it didn't seem to have the SaveToFile or LoadFromFile methods discussed here: http://msdn.microsoft.com/en-us/library/bb258184.aspx
So, how can I get at the attachments with .NET?
I finally got this working in C# using a reference to Microsoft.Office.Interop.Access.Dao.
DBEngine dbe = new DBEngine();
Database db = dbe.OpenDatabase("C:\\SomeDatabase.accdb", false, false, "");
Recordset rs = db.OpenRecordset("SELECT * FROM TableWithAttachmentField", RecordsetTypeEnum.dbOpenDynaset, 0, LockTypeEnum.dbOptimistic);
rs.MoveFirst();
rs.Edit();
Recordset2 rs2 = (Recordset2)rs.Fields["AttachmentFieldName"].Value;
rs2.AddNew();
Field2 f2 = (Field2)rs2.Fields["FileData"];
f2.LoadFromFile("C:\\test.docx");
rs2._30_Update();
rs2.Close();
rs._30_Update();
rs.Close();
This will add test.docx to the Attachment field "AttachmentFieldName" in table "TableWithAttachmentField". One thing to note is that attempting to add the same file twice will throw an error.
Interesting question. I don't use A2007, but have the runtime installed, so I used the Access object browser to see what's in there. I discovered something really odd -- there are two FIELD objects, Field and Field2. The attachment functions are members of Field2 but not Field. So, my suggestion would be that perhaps what you need to do is convert this:
Recordset.Fields("FileData").LoadFromFile(<filename>)
to something like this:
Dim rs As DAO.Recordset
Dim fld2 As DAO.Field2
Set rs = CurrentDb.OpenRecordset("[SQL]")
Set fld2 = Recordset.Fields("FileData")
fld2.LoadFromFile(<filename>)
rs.Close
Set fld2=Nothing
Now, I don't know if that will correct the problem for you, but it seems to me that given the two Field objects with different properties/methods/members, you need to be explicit about which Field object you're using. The code example you cite is specifically for use in Access and maybe Access does something to automatically resolve the differences between the two object (perhaps it uses the Field object by default for non-ACCDB databases and the Field2 object for ACCDB files).
Look at this write up on the Access team blog It has basically what David is suggesting with a bit of a twist. It sets a Recordset2 type object equal to the value of the attachment field. Then appends a record to that recordset a puts the file contents in that new record.
I've been struggling to try and do this same thing. There is a reference you can include in your project to "Microsoft.Office.Interop.Access.Dao" that will get you the Recordset2 and Field2 Interfaces, but no implementation classes.
That's about as far as I've gotten, I'll post more once/if I figure it out...
I was not able to get this working in C#, so I moved on to a different solution. I would love to know how to do this though if someone figures it out.