Its my first experience with excel file using C#.
As a demo, I wrote a single line in excel file and it worked but now I am trying to insert all the rows of DataTable but it gives the following error:
"HRESULT: 0x800A03EC"
Here is the code:
for (int i = 0; i < table.Rows.Count; i++)
{
xlWorkSheet.Cells[i, 1] = table.Rows[i]["Ref"].ToString();
xlWorkSheet.Cells[i, 2] = table.Rows[i]["Name"].ToString();
}
Please tell me how can I fix it?
Use Range.Value instead. Worksheet.Cells property is readonly:
for (int i = 0; i < table.Rows.Count; i++)
{
xlWorkSheet.Cells[i, 1].Value = table.Rows[i]["Ref"].ToString();
xlWorkSheet.Cells[i, 2].Value = table.Rows[i]["Name"].ToString();
}
Instead of a DataTable, put all the values into a object[,]
Then you can assign them in one swoop:
using (var targetRangeHeader = _excelApp.Range[TargetRangeTopAddress].WithComCleanup())
using (var targetRangeFirstDataCell = targetRangeHeader.Resource.Offset[1, 0].WithComCleanup())
using (var targetRange = targetRangeFirstDataCell.Resource.Resize[MyObjectArrayValues.Length, 1].WithComCleanup())
{
targetRange.Resource.Value2 = MyObjectArrayValues;
Using http://jake.ginnivan.net/vsto-com-interop
I wholeheartedly endorse Jeremy Thompson's answer to use a 2-dimensional object array. The performance will be orders of magnitude faster. I'm writing to explain why the code you've written doesn't work (and why you probably thought it should work).
In Visual Basic, there's a concept of "Default" property. When you write this in VBA, you're using that concept:
xlWorkSheet.Cells(i, 1) = someValue
The Cells property returns a Range object. In VBA, since you haven't used the Set keyword, this line is not reassigning the value of the object (and, if you added the Set keyword, it would fail to reassign the value of the object, since it's a read-only property, as Ria notes).
The absence of the Set keyword causes the assignment to go to the default prpoperty of the Range object returned by Cells(i, 1). The default property, of course, is the Value property. The above VBA sample, in other words, is equivalent to this:
Dim r As Range
Set r = xlWorkSheet.Cells(i, 1)
r.Value = someValue
Of course, you can also skip the r variable:
xlWorkSheet.Cells(i, 1).Value = someValue
Now, C# doesn't have the default property concept. You're therefore unable to write this code the shortcut way, which leads us to the code suggested in Ria's answer:
xlWorkSheet.Cells[i, 1].Value = someValue;
Or, with the real value instead of someValue:
xlWorkSheet.Cells[i, 1].Value = table.Rows[i]["Ref"].ToString();
The default property makes it easy for beginners to write code, and it helps code to be concise, but it's also confusing for people who are between the beginner stage and the advanced stage.
Related
I read How to read single Excel cell value and tried it myself. But when I gets to
string s = (myExcelWorkSheet.Cells[3, "E"] as Excel.Range).Value2.ToString();
Everything was terminated and the form was shown.
//Everything worked fine here.
string s = (myExcelWorkSheet.Cells[3, "E"] as Excel.Range).Value2.ToString();
//Everything after this was all skipped!
Why is this, and how can I fix it?
The problem with reading excel cell is that if there is nothing in it, the cell object is Null. Thus, it does not have .Value2 neither .Value.
To find a way how to avoid the check for Null, you may use Convert.ToString() which evaluates the Null to an empty string and thus does not return an error:
for (int i = 1; i < 5; i++)
{
string a = Convert.ToString(wk.Cells[i, 1].Value2);
Console.WriteLine(a);
}
When the cell has a value, you need the ToString().
And when the cell doesn't has a value, then you don't need the ToString()!
Otherwise the whole program will skip out and everything after that was NEVER executed!!!
So I guess it's just a problem of if the system was trying to cast a null value into a string or not!!!
I'm using C# Interop to get some values from a Worksheet and I get the following error:
Non-invocable member 'Microsoft.Office.Interop.Excel.Range.End' cannot be used like a method.
This is my code:
var wb = (Excel.Workbook)Globals.ThisAddIn.Application.ActiveWorkbook;
var wsEvars = wb.Sheets["Evars"];
var wsProps = wb.Sheets["Props"];
var wsEvents = wb.Sheets["Events"];
var wsListVars = wb.Sheets["List Vars"];
var sheetList = new Excel.Worksheet[] { wsEvars, wsProps, wsEvents, wsListVars };
for (var i = 0; i < sheetList.Length; i++)
{
// I get the error on the line below
var rowLast = sheetList[i].Range["I" + sheetList[i].Rows.Count].End(Excel.XlDirection.xlUp).Row;
}
The thing is that is works if I try as follows:
for (var i = 0; i < sheetList.Length; i++)
{
var rowLast = wsEvars .Range["I" + wsEvars .Rows.Count].End(Excel.XlDirection.xlUp).Row;
}
Am I missing something?
Looks like you found a bug in the C# compiler. The bug is actually present in the workaround, it ought to not compile for the same reasons the first snippet did not. Albeit that it is difficult to definitely claim that this is a bug, the C# language spec does not describe what is acceptable in this case.
The Range.End property is an indexed property. Such properties are not formally supported in C#, the language permits only the class indexer (aka this[]) to be the one-and-only indexed property of a class. But that restriction was lifted in C# version 4, specifically to make interop with COM servers easier. Like Excel, indexed properties are very common in COM object models.
Like the normal indexer, you have to use square brackets. Fix:
var rowLast = sheetList[i].Range["I" + sheetList[i].Rows.Count]
.End[Excel.XlDirection.xlUp].Row;
And the workaround you had to use in older C# versions is still available:
var rowLast = sheetList[i].Range["I" + sheetList[i].Rows.Count]
.get_End(Excel.XlDirection.xlUp).Row;
Hard to guess why it finds () parentheses acceptable in the second snippet. It looks like a bug, swims like a bug and quacks like a bug, so it is probably a bug. Let them know about it by clicking the New Issue button. I doubt they'll fix it but there might be more wrong than meets the eye.
I am trying to take the values in the textboxes, named sequentially from 0-9, and add that to a List using a for loop. I am having problems with the syntax or something.
here is what I have now.
for (int i = 0; i <= amt.Count(); i++)
{
amt[i] = int.Parse(amtBox[i].Text);
}
The error is that amtBox doesnt exist in the current context.
My problem is within the loop where i have amtBox[i].Text. I have tried this several ways and VS always throws an error. I have tried "amtBox" + i and that compiles but then causes an error when I try to do something with it and says "data is of wrong type".
I am new to C# and come from PHP so maybe that is why I think this approach will work. PHP doesnt care about data types where C# really does. I have done this exact thing in PHP many times without any issue.
Any suggestions on another way to do this are appreciated as I am probably coming at this all wrong.
Thanks
One solution would be to declare an array and assign amtBox'es to the individual indexes in the array and then you can iterate on that array.
var amtBoxes = new TextBox[] { amtBox0, amtBox1, .... };
for (int i = 0; i <= amt.Count(); i++)
{
amt[i] = int.Parse(amtBoxes[i].Text);
}
If you end up needing to iterate on your TextBox controls in other places I would consider making the array an instance member of your object.
I suppose that your textbox are named "amtBox" + a number.
(The Name property is "amtBox1" as an example)
In this case you could use
Control[] t = Controls.Find("amtBox" + i, false);
for a code like this
for (int i = 0; i <= amt.Count(); i++)
{
Control[] t = Controls.Find("amtBox" + i, false);
if(t != null && t.Length > 0)
{
amt[i] = int.Parse(t[0].Text);
}
}
My understanding is that you have text boxes named amtBox1, amtBox2, etc., and what you are trying to do is sequence through them. As you point out, this is very easy in PHP. It is possible to do what you're suggesting using reflection, but that is expensive and, in any event, there's probably a better way to do what you're looking for.
You could put all of your amount boxes into an array, and then what you have would work:
var amtBoxes = new[] {
amtBox1,
amtBox2,
amtBox3
}
I'm trying to use something like this:
for (int i = 0; i < 40; i++)
{
string x + i = TextBox + i.Text;
}
Is there any solution for this? I want to use i as an index as if I had an array of TextBox. How can I achieve a textbox's Text property like this?
I want to create for example a string named x1,x2,x3,x4... and value of x1 = TextBox1.Text.
But I could not :(
I have TextBox1,2,3...40 and I just want to pass their text values to new string or string list ex. like x1 = TextBox1.Text, x2 = TextBox2.Text; ..... :(
You're trying to get the Text property from a collection of TextBox - which, by the way, you haven't shown us how you're getting. It's possible that you don't even have an array of TextBox.
If you do, you'd use code like this (note that SomeCodeToGetTextBoxArray() needs to be defined by you):
TextBox[] myTextBoxes = SomeCodeToGetTextBoxArray();
var arrayLength = myTextBoxes.Length;
String[] x = new String[arrayLength];
for (int i = 0; i < arrayLength; i++)
{
x[i] = myTextBoxes[i].Text;
}
What you're looking to do is have a reference to a local variable, field, or property by it's string name that you generate at runtime. The only way to do what you want is to use reflection.
There are different ways to do this depending on if the variable is locally scoped, a field member, or a property member.
Reflection - LocalVariableInfo - FieldInfo - PropertyInfo
Note that while that would be the answer to your question, it is probably better to use an array (or List<T>) and use indexes. Though, it is really impossible to know without knowing what you are trying to do fully (not just in your little snippet). But, it's more likely that an array (or List<T>) is the more correct solution.
I am reading Silverlight 4 in Action and simultaneouly trying examples in it. However, i can't seem to understand the new dynamic keyword and why is it needed for COM. This is my code :-
if (AutomationFactory.IsAvailable)
{
dynamic excel =
AutomationFactory.CreateObject("Excel.Application");
excel.Visible = true;
dynamic workbook = excel.workbooks;
workbook.Add();
dynamic sheet = excel.ActiveSheet;
int i = 1;
double[] data = new double[] { 1.0, 5.0, 9.5, 2.7, 3.2, 0.6 };
foreach (double d in data)
{
dynamic cell = sheet.Cells[i, 1];
cell.Value = "Row " + i;
cell.ColumnWidth = 10;
cell = sheet.Cells[i, 2];
cell.Value = d;
i++;
}
dynamic shapes = sheet.Shapes;
shapes.AddChart(-4100, 120, 2, 300, 200);
}
I am wondering how did the author come to know that shapes support AddChart method and how did he know which parameters he needed to pass to this function? Can somebody throw some light on this?
Thanks in advance :)
It is not needed for COM. However, the code that doesn't use dynamic is a real mess.
As to how the author knew what methods are available, he read the Excel VBA documentation. Looking at the Excel type library would also tell you the method signatures, but without the explanation.
Dynamic basically means you don't know at compile time what type you will have (or sometimes the type doesn't even exist yet). Hence the absence of intellisense.
With a dynamic object you just call a method, throw in some parameters, and if that exact method with that signature exists, the DLR will call it.
If it doesn't exist you'll get a runtime exception.
" method and how did he know which parameters he needed to pass to this function? "
He probable coded it or read the documentation.