|
public FileContentResult ExportToExcel(object datasource)
{
var grid = new GridView();
grid.DataSource = datasource;
grid.DataBind();
var sw = new StringWriter();
var htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
byte[] excelFileBytesContent = this.Response.ContentEncoding.GetBytes(sw.ToString());
var excelFileContentResult = new FileContentResult(excelFileBytesContent, "application/vnd.ms-excel");
return excelFileContentResult;
}
It kinda works, but WTF???
|
|
|
|
|
Excel can load Html tables no problem. There are some CSS issues that arise from it. But it really does work quite well.
And while it doesn't appear that in this case you're going to have any styles being applied, if you did apply formatting to the GridView, they would be reflected in the excel document.
|
|
|
|
|
GibbleCH wrote: Excel can load Html tables no problem.
I agree, but for a business requirement?
|
|
|
|
|
I'm actually using a similar technique in a recent app we have written. However, the HTML passed to excel comes from the browser. This allows us to take the data, render HTML, the user then has the ability to hide/show columns, sort, filter, etc, then export the result to excel.
If I don't pass the HTML to excel (well, a per-processor that cleans it up), then I have to look at it, determine what they are currently showing, query the db all over, then try to create an excel document that mimics the displayed data WITH all the current formatting, which is a duplication of effort since all that work has already been done in rendering the HTML.
It also allows us to just modify the rendering of the HTML, and the export to excel is almost always working automatically without also having to modify the code that generates the excel document.
|
|
|
|
|
It is all good an well till you get to text that looks like numeric data to excel.
Eg: a telephone number with a leading 0. You end up with a number without the leading zero. Or a long number (say ID or SS nr), displays in exponential form.
|
|
|
|
|
Yea.. had the problem with the 0 in the excel part too.
But we used it too, because the calc method had a lot of logic to output HTML, so we reused it for the excel outtput. Worked like a bomb (but the 0 was problematic)
|
|
|
|
|
I generate my cells with classnames, then run the data through a filter, and setup proper mso-number-format styles on the cells prior to sending it to excel. It works well
|
|
|
|
|
Thanks for the tip.
Yea.. that project was done in record time about a year ago. But will keep it in mind!
|
|
|
|
|
That's actually relatively neat. I can't think of a more concise way of getting data into an Excel readable format without using interop or a library like Aspose which costs money (and sticking it on the clipboard and reading it back doesn't count, that has a big nasty side-effect).
|
|
|
|
|
|
Well, using a whole library just for that is kind of overkill, still. That's a really useful looking library, though.
|
|
|
|
|
Here is a lovely one I hand coded earlier. (names changed and many innocuous lines removed to protect the innocent code around it.)
void ReduceCandidates(OblongCollection candidateOblongs)
{
for (int index = 0; index < candidateOblongs.Count; ++index)
{
Oblong fittedOblong = candidateOblongs[index].Reduce();
}
}
This looks like a simple piece of code that nothing can go wrong with.
Unfortunately it occasionally produces an object disposed exception on the line indicated.
OblongCollection and Oblong are both immutable classes with finalizers and implementing the dispose pattern.
What I think must be happening is a new Oblong is created in the OblongCollection indexer, the Oblong then goes out of scope and the Reduce method is called on it. In the Reduce method a method is called on the sole instance variable, but before the method is executed the finalizer kicks into action. This sees that the Oblong is no longer in scope, no instance variables are being used so disposes of the Oblong and calls the finalizer, trashing the object that the Reduce method is calling.
Took me a while to figure out what was happening - I did not want to just blindly change the code. Fixed code below. (Yes, I know a foreach would be better here, if possible, but I want to keep this simple).
void ReduceCandidates(OblongCollection candidateOblongs)
{
for (int index = 0; index < candidateOblongs.Count; ++index)
{
using (Oblong currentCandidate = candidateOblongs[index])
{
Oblong fittedOblong = currentCandidate.Reduce();
}
}
}
Time to throw the Exception ElectrocuteCoder in every finalizer I guess
|
|
|
|
|
Is OblongCollection your class? If so then the method should be a member of that. Otherwise, I'll just STFU.
Panic, Chaos, Destruction. My work here is done.
Drink. Get drunk. Fall over - P O'H
OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre
I cannot live by bread alone. Bacon and ketchup are needed as well. - Trollslayer
Have a bit more patience with newbies. Of course some of them act dumb - they're often *students*, for heaven's sake - Terry Pratchett
|
|
|
|
|
OblongCollection and Oblong are both my classes that wrap different unmanaged objects. If I put the Reduce() method on OblongCollection then it would return another OblongCollection and still need a loop to process the code that has been snipped out.
Although saying that it should also be a method on OblongCollection , I have just not got around to doing it, as I have never needed it.
|
|
|
|
|
Member 2053006 wrote: If I put the Reduce() method on OblongCollection then it would return another OblongCollection and still need a loop to process the code that has been snipped out.
Sure?
public void ReduceCandidates()
{
for (int index = 0; index < this.Count; ++index)
{
using (Oblong currentCandidate = this[index])
{
Oblong fittedOblong = currentCandidate.Reduce();
}
}
}
Your original never returned a new collection, so why should this?
Panic, Chaos, Destruction. My work here is done.
Drink. Get drunk. Fall over - P O'H
OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre
I cannot live by bread alone. Bacon and ketchup are needed as well. - Trollslayer
Have a bit more patience with newbies. Of course some of them act dumb - they're often *students*, for heaven's sake - Terry Pratchett
|
|
|
|
|
Ah, I think I see what you are saying there, move the entire method into OblongCollection . A good idea, but OblongCollection is in a shared library (mixed mode C++) and the snipped code is application specific, in C# and refers to some more instance members. I would rather keep application code out of the shared library unless it is generic enough to be useful in more then one application, which the snipped code is not.
|
|
|
|
|
If that is the case, I'd wrap OblongCollection within an application class. It is, generally speaking, a good idea to always wrap external API's in a class as it hides the API specifics from the developer.
Panic, Chaos, Destruction. My work here is done.
Drink. Get drunk. Fall over - P O'H
OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre
I cannot live by bread alone. Bacon and ketchup are needed as well. - Trollslayer
Have a bit more patience with newbies. Of course some of them act dumb - they're often *students*, for heaven's sake - Terry Pratchett
|
|
|
|
|
Couldn't you use a delegate here? Pass a delegate for a void function taking an Oblong to ReduceCandidates(). Then you could implement the ReduceCandidates() in the library as a member of OblongCollection and the application specific code inside the delegate definition in the application.
|
|
|
|
|
It's a pretty big WTF that something can get disposed by the framework when you still have a reference to it.
|
|
|
|
|
There is no longer a reference to it when it is finalized. The indexer returns a new object which is not stored anywhere, so goes out of scope as soon as it is created. As soon as non of the instance fields of that object can not be accessed by any code the object is eligible for finalization.
I could (and should) have prevented the finalizer from running by putting a GC.KeepAlive(this) at the end of the Reduce() method, yep, finialzers are nasty. The real code horror was not disposing of disposable objects. My bad.
A good article I found on here that mentions GC.KeepAlive is Implementing IDisposable and the Dispose Pattern Properly[^]
See point 2 in this article (OK, so this is for an old version of .net)
(MSDN article) 3.9 Automatic memory management
|
|
|
|
|
There is still a reference – this within the executing Reduce method. While one could argue that you should have worked around it, the framework really shouldn't finalise something on which code is currently executing under any circumstances (well, unless you call Dispose before you try to do something else, I suppose).
|
|
|
|
|
|
Take a peek at some VB. That should at least replace the horror with one that is at least expected.
I wasn't, now I am, then I won't be anymore.
|
|
|
|
|
As a VB programmer I see my share of VB... Seen many horrors as well (VB6 and even pre-VB4 practices in .NET)
I have no doubt there is legacy C# code that is as bad as legacy VB code or even worse though.
In the end code is as good as the programmer writes it, independent of the language
It's an OO world.
public class Naerling : Lazy<Person>{}
|
|
|
|
|
touche.... There is definitely some horrible C# code out there. For that matter, horrible code exists in every language.
I equate that to people of any given language who have no understanding of grammar, syntax or punctuation. I have a lot of vb and vb.net systems that I work with daily as well. I tend to find that vb lends itself to being ugly by nature which is why I prefer C# and C++ to vb.
I wasn't, now I am, then I won't be anymore.
|
|
|
|