Exporting a datagrid to excel (or rather a csv file) in c#

January 7th, 2009

There are numerous articles out there that address this popular feature, and i’m not duplicating their solutions, which all work just fine, although they have their limitations.

What i don’t really like in a lot of the proposed solutions, is that they iterate over the datagrid, loosing all the controls, and replacing them with a literal control (have a look at this blog from Mitchell Sellers: http://www.mitchelsellers.com/blogs/articletype/articleview/articleid/210/exporting-a-datagrid-to-excel-the-easy-way.aspx )

I provide you with my own way for doing this, which i find easy to understand, easy to program, and very versatile.

Let’s say you are binding a list of persons to a datagrid, and that you show their firstname, lastname and date of birth. In your export however, you want a whole bunch of other properties, like address, city and postalcode!

Here is my solution:

1. Define an attribute class

using System;

namespace Lbk
{
    public class Exportable : Attribute
    {
    }
}

2. Create a class, PersonExport in my case. The properties that you want to export, are decorated with the attribute you just defined.

Notice that in the constructor, we get a person object. Our PersonExport object just delegates his properties to this object. You could have done this in the Person class too, and decorate your properties there, but i chose not to. Keep it tidy and simple, Person doesn’t really need to know what properties are exportable.

namespace Lbk
{
    public class PersonExport
    {
        private Person person;
        public PersonExport(Person person)
        {
            this.person = person;
        }

        [Exportable]
        public string LastName
        {
            get
            {
                return person.LastName;
            }
        }

        [Exportable]
        public string FirstName
        {
            get
            {
                return person.FirstName;
            }
        }

        [Exportable]
        public string Street
        {
            get
            {
                return person.Street;
            }
        }

        [Exportable]
        public string PostalCode
        {
            get
            {
                return person.PostalCode;
            }
        }
        [Exportable]
        public string City
        {
            get
            {
                return person.City;
            }
        }

        [Exportable]
        public string Country
        {
            get
            {
                return person.Country;
            }
        }

        [Exportable]
        public string BirthDate
        {
            get
            {
                if (person.ExistBirthDate)
                {
                    return person.BirthDate.ToString("yyyy/MM/dd");
                }
                return string.Empty;
            }
        }
    }
}

3. Next, you see what happens when someone clicks on the export button in my datagrid: It does something to the Http Response, and writes out a string that contains the content of the file.

private void ExportGridView()
{
    string attachment = "attachment; filename=leden.csv";
    Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.AddHeader("content-disposition", attachment);
    Response.ContentType = "application/ms-excel";
    StringBuilder sb = new StringBuilder();
    this.Club.AddPersonContent(sb);
    Response.Write(sb.ToString());
    Response.End();
}

Look at  this.Club.AddPersonContent() now. That method appends the content to the stringbuilder.  It iterates over all persons in the Club. But before that, it does something else :-)

Remember that we have an PersonExport class, decorated with the [Exportable] attribute on each property. First, we need a list of these properties. These are the properties you want to export, right!

    Type persontype = typeof(PersonExport);
    PropertyInfo[] properties = persontype.GetProperties();

    List<PropertyInfo> list = new List<PropertyInfo>();
    foreach(PropertyInfo property in properties)
    {
        object[] exportables = property.GetCustomAttributes(typeof (Exportable), false);
        if(exportables != null && exportables.Length>0)
        {
            list.Add(property);
        }
    }

Now in my csv file, i want a header row with all the names of the properties, so i append that to my stringbuilder.

    foreach (PropertyInfo property in list)
    {
        sb.AppendFormat("{0},", property.Name);
    }
    sb = sb.Remove(sb.Length-1, 1);
    sb.AppendLine();

Finally, i iterate over all persons ( == this.PersonExtent), retrieve the values for each property, and append it to the stringbuilder.

    foreach(Person person in this.PersonExtent(this.AllorsSession))
    {
        PersonExport export = new PersonExport(person);
        foreach (PropertyInfo property in list)
        {
            object value = property.GetValue(export, null);
            if(value == null)
            {
                value = string.Empty;
            }
            sb.AppendFormat("{0},", value);
        }
        sb = sb.Remove(sb.Length-1, 1);
        sb.AppendLine();
    }

Done! Here is the complete method again:

public void AddPersonContent(StringBuilder sb)
{
    Type persontype = typeof(PersonExport);
    PropertyInfo[] properties = persontype.GetProperties();

    List<PropertyInfo> list = new List<PropertyInfo>();
    foreach(PropertyInfo property in properties)
    {
        object[] exportables = property.GetCustomAttributes(typeof (Exportable), false);
        if(exportables != null && exportables.Length>0)
        {
            list.Add(property);
        }
    }

    foreach (PropertyInfo property in list)
    {
        sb.AppendFormat("{0},", property.Name);
    }
    sb = sb.Remove(sb.Length-1, 1);
    sb.AppendLine();

    foreach(Person person in this.PersonExtent(this.AllorsSession))
    {
        PersonExport export = new PersonExport(person);
        foreach (PropertyInfo property in list)
        {
            object value = property.GetValue(export, null);
            if(value == null)
            {
                value = string.Empty;
            }
            sb.AppendFormat("{0},", value);
        }
        sb = sb.Remove(sb.Length-1, 1);
        sb.AppendLine();
    }
}

That is my solution, where you can export as many properties to your csv file as you want, because you are no longer limited to what is in your datagrid.

I find this an elegant solution, so please comment!

How to Write your own DotNetNuke Scheduler in c#?

December 10th, 2008

In this article, you’ll see how to write your own scheduler for DotNetNuke.

I am using dnn 4.9.0 version, all standard. To make this easy, i create my scheduler in a separate project. The purpose of my scheduler is to send emails, so i called it EmailSchedulerClient.

The project itself is called Scheduler, so no surprises here! :-). This project compiles to an assembly Inxin.Dnn.Scheduler. Be sure to reference this in your DotNetNuke website (so it should be in the /bin folder)

Let’s show you everything that’s to it: (for those that don’t know what AllorsSession and all is, please visit their website)

So here it is, the entire class EmailSchedulerClient:

using System;
using Allors;
using AllorsDomains;
using DotNetNuke.Services.Scheduling;
using Zintec;

namespace Inxin.Dnn.Scheduler
{
    public class EmailSchedulerClient : SchedulerClient
    {
        private AllorsSession session;

        public AllorsSession AllorsSession
        {
            get
            {
                if (session == null)
                {
                    session = AllorsConfiguration.connectedPopulation.CreateSession();
                }
                return session;
            }
        }

        public EmailSchedulerClient(ScheduleHistoryItem historyItem)
        {
            this.ScheduleHistoryItem = historyItem;
        }

        public override void DoWork()
        {
            try
            {
                this.Progressing();

                this.HandleScheduler();

                this.ScheduleHistoryItem.Succeeded = true;
            }
            catch(Exception ex)
            {
                this.ScheduleHistoryItem.Succeeded = false;
                this.ScheduleHistoryItem.AddLogNote("Exception: " + ex);
                this.Errored(ref ex);
            }
        }

        private void HandleScheduler()
        {
            // Get all message that have not been sent
            AllorsExtent<IEmailable> allorsExtent = this.AllorsSession.Extent<IEmailable>();
            AllorsCompositePredicate orFilter = allorsExtent.Filter.AddOr();
            orFilter.AddNotExists(MyDomain.IEmailableIsEmailed.Role);
            orFilter.AddEquals(MyDomain.IEmailableIsEmailed.Role, false);

            this.ScheduleHistoryItem.AddLogNote("Number of Emails to Send: " + allorsExtent.Count );

            SmtpSettings settings = new SmtpSettings();

            foreach(IEmailable email in allorsExtent)
            {
                try
                {
                    email.SendByMail(settings);
                    email.IsEmailed = true;
                    email.SendDate = DateTime.Now;
                    this.ScheduleHistoryItem.AddLogNote("OK: Send mail to " + email.MailTo);
                }
                catch(Exception ex)
                {
                    this.ScheduleHistoryItem.AddLogNote(string.Format("FAIL : Send mail to " + email.MailTo));
                    email.IsEmailed = false;
                }
                finally
                {
                    AllorsSession.Commit();
                }
            }
        }
    }
}

Here is some explanation to what this all is.

public class EmailSchedulerClient : SchedulerClient

You must inherit from DotNetNuke.Services.Scheduling.SchedulerClient

private AllorsSession session;

As said before, this is allors territory, but an allorssession is your link to your domain (objects and  persistancy)

public EmailSchedulerClient(ScheduleHistoryItem historyItem)
{
this.ScheduleHistoryItem = historyItem;
}

The constructor must have this signature! DotNetNuke keeps track of what happens in your scheduler thru this ScheduleHistoryItem object.

public override void DoWork()

Here the actual work is done, you can do whatever you want, but in short, i do this:

this.Progressing(); // Inform the scheduler that i am running.

Retrieve all objects that are of type IEMailable (which is an interface btw), and that are not yet emailed (IsEmailed is false or null).

foreach emailable object, i call it’s method SendByMail. If all is well, i set the IsEmailed Property to true, and i commit this to the database  (AllorsSession.Commit())

this.ScheduleHistoryItem.Succeeded = true; //

That’s about it.

So how do you add this to the DotNetNuke Scheduler?

Login as Host, and go to Schedule.

Here you see my settings:

My EmailSchedulerClient can be found in assembly Inxin.Dnn.Scheduler.dll and the  full class name (namespace + classname) is Inxin.dnn.Scheduler.EmailSchedulerClient.

If you want your scheduler to run in this way, then don’t forget to set the Scheduler Mode in your hostsettings. This should be Timer Method. This apparantly is not the default setting, so when your scheduler only runs when you update it, then this is probably the reason why. It was in my case :-)

Dimensional Planning on Fixed Price Projects

November 27th, 2008

Thursday, october 20th Martien and I gave a session about ‘Dimensional Planning on Fixed Price Projects’ at the XPDays Benelux.

The slides are already available on our wiki and we will add more content in the coming weeks.