How to Export into Excel from Gridview - c#

I have a button to Export my Gridview1 data to Excel, but when i tried the code below(which i used in C#), it gives me a compiling error. Can you help me..
Error 1: Cannot apply indexing with [] to an expression of type 'System.Web.UI.WebControls.GridView'
Error 2: The name 'MessageBox' does not exist in the current context
try
{
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
excel.Visible = true;
Microsoft.Office.Interop.Excel.Workbook workbook = excel.Workbooks.Add(System.Reflection.Missing.Value);
Microsoft.Office.Interop.Excel.Worksheet sheet1 = (Microsoft.Office.Interop.Excel.Worksheet)workbook.Sheets[1];
int StartCol = 1;
int StartRow = 1;
int j = 0, i = 0;
//Write Headers
for (j = 0; j < GridView1.Columns.Count; j++)
{
Microsoft.Office.Interop.Excel.Range myRange = (Microsoft.Office.Interop.Excel.Range)sheet1.Cells[StartRow, StartCol + j];
myRange.Value2 = GridView1.Columns[j].HeaderText;
}
StartRow++;
//Write datagridview content
for (i = 0; i < GridView1.Rows.Count; i++)
{
for (j = 0; j < GridView1.Columns.Count; j++)
{
try
{
Microsoft.Office.Interop.Excel.Range myRange = (Microsoft.Office.Interop.Excel.Range)sheet1.Cells[StartRow + i, StartCol + j];
myRange.Value2 = GridView1[j, i].Value == null ? "" : GridView1[j, i].Value;
}
catch
{
;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}

Firstly, full disclosure: I'm not an expert on asp.net WebForms, so I'm reading this on a syntax basis only (not examining whether or not your actual framework usage is correct).
Error 1
To my eye, the error occurs here:
myRange.Value2 = GridView1[j, i].Value == null ? "" : GridView1[j, i].Value;
It looks like you are trying to reference GridView1 with array notation, inside a loop using variables i and j. However, look at your loops:
for (i = 0; i < GridView1.Rows.Count; i++)
for (j = 0; j < GridView1.Columns.Count; j++)
Your loops are accessing GridView1 via collections, specifically .Rows and .Columns respectively. So my assumption would be that you might need something like:
myRange.Value2 = GridView1.Rows[i].Columns[j].Value == null ? "" : GridView1.Rows[i].Columns[j].Value;
Remember, this is just a syntactic guess based on your earlier code (GridView1.Columns[j].HeaderText).
Error 2
The issue here is that the name (static class or variable) you are using, MessageBox, doesn't seem to exist at this point in the code. So I would ask:
Is the variable in scope? I.e., is it declared earlier in the try block, and has now gone out of scope in the catch?
If it's a static class, is the assembly referenced correctly? Intellisense should pick this up for you in Visual Studio, with refactor options to include a reference as a using statement above, or fully qualify the name for you.
From memory MessageBox is a static class in the WPF assemblies somewhere to do with windows forms, not as a part of ASP.NET - it is used to show a window in a desktop program. If you wish to show a message in a browser, that would be a javascript statement like:
alert("Your exception message here");
However, you will need some means of catching that Exception (ex), grabbing the text from it (ex.ToString()) and transmitting that into the page to be shown via javascript.
Last thought on that: if you are wanting to show that error, and it's as a result of a button clicked on the page, you may be intending to invoke the Export to Excel function as an AJAX call to the server. If this is the case, try returning a HTTP error code (like a 500 or something) back to the browser, and using the .fail() binder to display an appropriate client-side error. Just a suggestion :)

Related

How do I assign a 2 dimensional object[,] array the value of an Excel Interop Range.Value?

I am trying to assign a 2 dimensional object array to the value of an Excel interop Range.Value
I pull the 2 object dimensional array directly from the Range.Value of the UsedRange then do some changes to the data and put it back in as a 2 dimensional object[,].
When I save the workbook either as a copy or the original the new values I assign to the Range do not persist.
Here is my code:
/// <summary>
/// Writes the data from this excel document to <see cref="ExcelDoc.FilePath"/>
/// </summary>
/// <param name="newFilePath">used to specify a new file path</param>
/// <remarks>This will overwrite any data in the file</remarks>
public void WriteDataToExcelInterop(string newFilePath = "")
{
Excel.Application exApp = new();
GetWindowThreadProcessId(exApp.Hwnd, out int excelProcessId);
Process excelProcess = Process.GetProcessById(excelProcessId);
try
{
exApp.DisplayAlerts = false;
exApp.AutomationSecurity = MsoAutomationSecurity.msoAutomationSecurityForceDisable;
Excel.Workbook workbook = exApp.Workbooks.Open(FilePath);
for (int i = 1; i <= workbook.Worksheets.Count; i++)
{
Excel.Worksheet worksheet = workbook.Worksheets[i];
string name = worksheet.Name;
Excel.Range range = worksheet.UsedRange;
for (int j = 0; j < Worksheets.Count; j++)
{
if (Worksheets[j].Name == name)
{
range.Value = Worksheets[j].DataObjects; //this does not work
range.Formula = Worksheets[j].FormulaObjects; //this does work
}
}
}
if (newFilePath == "")
{
workbook.Close(true, FilePath);
exApp.Quit();
excelProcess.Kill();
}
else
{
workbook.SaveCopyAs(newFilePath);
workbook.Close(false, FilePath);
exApp.Quit();
excelProcess.Kill();
}
}
catch (Exception ex)
{
exApp.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
excelProcess.Kill();
ExceptionHandling.ExceptionData exData = new(ex);
MessageBox.Show(exData.FormatedMessage);
}
}
I thought maybe it had something to do with the fact that the UsedRange is ReadOnly according to documentation but the fact that I can change the formulas just fine says otherwise.
So I thought maybe it had to do with the datatype going into the variable so I tried changing the datatype of DataObjects but that did also not work.
I also thought maybe the line exApp.AutomationSecurity = MsoAutomationSecurity.msoAutomationSecurityForceDisable; was causing an issue with saving since this particular excel file is macro enabled. I commented out the line but this did nothing.
I'm thinking maybe the issue is Range.Value is not changing after save because some flag somewhere has not been raised properly.
I could really use a fresh set of eyes on this to see if I'm missing something.
I found what the issue was.
I assumed wrongly that Range.Value and Range.Formula were considered separate but when I make changes to Range.Formula it overwrites any changes I made to Range.Value since I am modifying Value BEFORE Formula. I will now have to change my code a little to account for this but my code was working as intended I just didn't realize why I was getting the results I was. Hopefully this can be helpful to anyone confused by this interaction in the future.

Iterating over all hyperlinks in an excel sheet using OpenXML doesn't return all hyperlinks

I am observing a very weird behavior here. I have an excel document (.xlsx) and My goal is to edit all the hyperlinks that meet a certain criteria.
The first time I iterate, I am able to edit most of the links. There are 4 links that still remain. Then I run the program again. Now 2 remain. Then I run the program yet again, and finally all links are replaced.
Here is the snippet:
using (SpreadsheetDocument doc = SpreadsheetDocument.Open(FilePath, true))
{
WorkbookPart wbPart = doc.WorkbookPart;
// Replace hyperlink targets
foreach (var worksheet in wbPart.WorksheetParts)
{
var hyperLinkRelations = worksheet.HyperlinkRelationships;
for (int i = 0; i < hyperLinkRelations.Count(); i++)
{
var link = hyperLinkRelations.ElementAt(i);
try
{
if (link != null && link.Uri != null && Utils.IsToBeReplaced(link.Uri.AbsoluteUri))
{
string relationId = link.Id;
string destUrl = GetReplacementUrl(link.Uri.AbsoluteUri);
if (destUrl != null)
{
worksheet.DeleteReferenceRelationship(link);
worksheet.AddHyperlinkRelationship(new Uri(destUrl, UriKind.Absolute), true, relationId);
}
}
}
catch (Exception ex)
{
Logger.Log(ex);
}
}
}
}
I have checked the utility methods "IsToBeReplaced()" and "GetReplacementUrl()" are functioning normally. I put a break point and discovered that the URLs that don't get replaced, simply do not show up in the "HyperlinkRelationships" collection.
Here is the excel file that I am working with: https://app.box.com/s/j3ulbxfafzxgcqiaep148a8yq4iy37ow
I would guess your indexing is throwing it off.
As a first attempt I would try working backwards.
ie something like:
var hcount = hyperLinkRelations.Count();
for (int i = hcount - 1; i >= 0; i--)
Otherwise, after you delete then add the relationship your i variable updates but the collection has been renumbered / shifted up by one so i will have skipped an element.

COM Exception : 0x800AC472 raised when System is Locked (Excel VSTO)

I'm using a 2D array to write data to an excel range. It works as expected when the system is not in Locked State.The code looks something like this:
Range range = SetRange(); // no. of rows and columns in the selected range is equal to the no.of rows and columns in the 2D Array
object[,] data = new object[rows, columns];
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
data[i, j] = i+j;
}
}
range.value2 = data;
The operation "range.value2 = data" gets executed without any issues when the system is not locked. However, when I lock the system and this operation is attempted, I get an exception message "Exception from HRESULT: 0x800AC472" when executing this operation.
I understand that this is a COM Exception but I don't have any idea, why is it getting raised under these circumstances. This issue only happens when my system is in locked state. Otherwise, the same piece of code works fine.
I'm currently working on Microsoft Visual Studio 2010 and Microsoft Excel 2007.

Why does javascript indexOf() method throw error when called from C# code-behind?

I am using Google Navigation charts in a project.
Everything works fine when I run the javascript code in the client side (.aspx page), but when I put it in the code behind and echo/write it out (via Response.Write()) it throws an error, specifically at the point where the javascript code trys to call the indexOf() method on an array.
I have tried to examine the cause of the error, but the only info I get is that this is a problem in IE8 and earlier with the indexOf() method- this cannot be my problem, because as I said it works fine when I call it directly from the client - it is only giving a problem form the code-behind.
This is the specific error I receive:
0x800a01b6 - Microsoft JScript runtime error: Object doesn't support property or method 'indexOf'
This will work fine (in client):
for (var i = 0; i < data.getNumberOfColumns() ; i++) {
if (i == 0 || defaultSeries.indexOf(i) > -1) {
// if the column is the domain column or in the default list, display the series
columns.push(i);
}
....
but this will throw an error (in code-behind):
htmlJS += "for (var i = 0; i < data.getNumberOfColumns() ; i++) {";
htmlJS += "if (i == 0 || defaultSeries.indexOf(i) > -1) {";
// if the column is the domain column or in the default li";st, display the series
htmlJS += "columns.push(i);";
htmlJS += "}";
....
Response.Write(htmlJS);
Does anyone know why this error only occurs from the code-behind?
Assuming defaultSeries is an array, you will need to polyfill Array.prototype.indexOf for IE<9, which only supports indexOf on strings.
Here's a polyfill from MDN:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
if ( this === undefined || this === null ) {
throw new TypeError( '"this" is null or not defined' );
}
var length = this.length >>> 0; // Hack to convert object.length to a UInt32
fromIndex = +fromIndex || 0;
if (Math.abs(fromIndex) === Infinity) {
fromIndex = 0;
}
if (fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {
fromIndex = 0;
}
}
for (;fromIndex < length; fromIndex++) {
if (this[fromIndex] === searchElement) {
return fromIndex;
}
}
return -1;
};
}
Two things:
Based on your code, you might have a white-space issue with the javascript - you are concatenating the string, and so, for example, you will have a section that looks like "{if" - however, this is not likely causing your issue.
What IS likely causing your issue is the timing of the javascript hitting the page. Does the object EXIST when the response.write gets flushed to the client? In order to make sure that all the required bits of the page exist when you need them, you normally will want to use the scripting object methods to add the script, and then CALL the code on once the page is loaded. Check out this page on adding script dynamically to a page: http://msdn.microsoft.com/en-us/library/ms178207(v=vs.100).aspx
Thank you everyone - I have found a soltuion that works:
If I create the same string which contains the javascript indexOf() method and then either assign it as output to a literal element on the aspx page, or if I "echo" it out via the <% %> special tags then the javascript code will run fine.
So the following runs:
Code-Behind:
public string jsHtml ="";
jsHtml +="<script type='text/javascript'>";
jsHtml+="var defaultSeries = [1,2,3];";
jsHtml+="alert(defaultSeries.indexOf(2));";
jsHtml+="</script>";
txtValueA.Text = jsHtml;
Client/aspx page:
<asp:Literal ID="txtValueA" runat="server></asp:Literal>
//OR
<%=jsHtml %>
Strange but True..... thanks for the input

Trying to Add itemSubtotalAddRq, but getting error for it

I have been making an app for a friend, but recently got stuck. For some reason, when I try to test "itemSubtotalAddRq" I get the following error:
Request[3]: ItemSubtotalAddRq ItemSubtotalAdd
Name: required fiels is missing
End of ItemSubtotalAdd
Im not sure what it is, but I know its one of the ItemSubtotalAddRq lines, or im using iItemSubtotalAdd wrong.
public void SalesInfoAdd(IMsgSetRequest requestMsgSet)
{
ISalesReceiptAdd salesReceiptAddRq = requestMsgSet.AppendSalesReceiptAddRq();
//IItemSubtotalAdd itemSubtotalAddRq = requestMsgSet.AppendItemSubtotalAddRq();
salesReceiptAddNew = new List<ISalesReceiptLineAdd>();
salesReceiptAddRq.CustomerRef.FullName.SetValue(Form.phoneNumber.Text);
salesReceiptAddRq.IsPending.SetValue(true);
salesReceiptAddRq.IsTaxIncluded.SetValue(false);
salesReceiptAddRq.FOB.SetValue(Form.orderID.Text);
salesReceiptAddNew.Clear();
int cnt = 0;
//while (i < Form.productID.Count)
for (int j = 0; j < Form.productID.Count; j++)
{
salesReceiptAddNew.Add(salesReceiptAddRq.ORSalesReceiptLineAddList.Append().SalesReceiptLineAdd);
salesReceiptAddNew[j].ItemRef.FullName.SetValue(Form.productID[j].ToString());
salesReceiptAddNew[j].ORRatePriceLevel.Rate.SetValue(Convert.ToDouble(Form.pricesList.Items[j]));
salesReceiptAddNew[j].Quantity.SetValue(Form.QBqt[j]);
salesReceiptAddNew[j].Desc.SetValue(Form.productsList.Items[j].ToString().ToUpper() + " -" + " " +
Form.QBsku[j].ToString().ToUpper());
cnt = j;
}
if (Form.DiscountType.Text != "None" || Form.DiscountType.Text != " ")
{
if (Form.productID.Count >= 2)
{
cnt++;
salesReceiptAddNew.Add(salesReceiptAddRq.ORSalesReceiptLineAddList.Append().SalesReceiptLineAdd);
salesReceiptAddNew[cnt].ItemRef.FullName.SetValue("SUBTOTAL");
salesReceiptAddNew[cnt].ORRatePriceLevel.Rate.SetValue(Form.totalOfAllItems);
//itemSubtotalAddRq.Name.SetValue("SUBTOTAL");
//itemSubtotalAddRq.IsActive.SetValue(true);
}
else
itemSubtotalAddRq.IsActive.SetValue(false);
cnt++;
salesReceiptAddNew.Add(salesReceiptAddRq.ORSalesReceiptLineAddList.Append().SalesReceiptLineAdd);
salesReceiptAddNew[cnt].ItemRef.FullName.SetValue(Form.DiscountType.Text);
}
if(Form.freeShipping.Checked == false)
{
cnt++;
salesReceiptAddNew.Add(salesReceiptAddRq.ORSalesReceiptLineAddList.Append().SalesReceiptLineAdd);
salesReceiptAddNew[cnt].ItemRef.FullName.SetValue("SHIPPING");
salesReceiptAddNew[cnt].ORRatePriceLevel.Rate.SetValue(Convert.ToDouble(Form.shipping.Text));
}
IMsgSetResponse responseMsgSet = sessionManager.DoRequests(requestMsgSet);
}
this is the only solution i could think of, and it still doesn't work. any thoughts?
I think you are referring to 'ItemType' which is under 'Line' tag
Ref - https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/v2/0500_quickbooks_windows/0600_object_reference/salesreceipt
XML
<Add RequestId="ca435123492c41b891f7841cf4d0e81d" xmlns="http://www.intuit.com/sb/cdm/v2">
<OfferingId>ipp</OfferingId>
<ExternalRealmId>65123456</ExternalRealmId>
<SalesReceipt>
<Header>
---
</Header>
<Line>
<Id idDomain="QB">66</Id>
<Desc>Description</Desc>
<ItemId idDomain="QB">4</ItemId>
<ItemName>Dusting</ItemName>
<ItemType>Subtotal</ItemType>
<UnitPrice>1231</UnitPrice>
</Line>
</SalesReceipt>
</Add>
Using Java, I had coded like this. I think, there should be some similar enums/classes in .Net too. Can you take a look at the .Net docs
Link - http://developer-static.intuit.com/SDKDocs/QBV2Doc/IntuitDataServicesSDK/
Instead of 'SUBTOTAL', you should use 'Subtotal'. In the service background, allowed values are [Assembly, Fixed Asset, Group, Inventory, Other Charge, Payment, Product, Service, Subtotal].
You can try this use case in the ApiExplorer.
Link - https://developer.intuit.com/apiexplorer?apiname=V2QBD#SalesReceipt
Java Code
QBSalesReceipt entityPojo = QBObjectFactory.getQBObject(context, QBSalesReceipt.class);
SalesReceiptLine receiptLine = QBObjectFactory.getQBObject(context, SalesReceiptLine.class);
receiptLine.setItemType(ItemTypeEnum.SUBTOTAL);
ArrayList receiptLines = new ArrayList();
receiptLines.add(receiptLine);
entityPojo.setLine(receiptLines);
QBSalesReceiptService service = QBServiceFactory.getService(context, QBSalesReceiptService.class);
service.addSalesReceipt(context, entityPojo);
Plz let me know how it goes.
Thanks

Categories