Sep 09 Tommy | CodeBits

Save All Attachemnts In A Folder In Outlook

Here is macro code for outlook to save all attachments of all selected email. It will prompt for a save directory and allow renaming of duplicate files. This just saved me probably a full day at work of saving all files since Outlook does not have this feature built in. Special thanks to Arcane Code

Option Explicit

Public Sub SaveAttachments()

  'Note, this assumes you are in the a folder with e-mail messages when you run it.
  'It does not have to be the inbox, simply any folder with e-mail messages
  
  Dim App As New Outlook.Application
  Dim Exp As Outlook.Explorer
  Dim Sel As Outlook.Selection
  
  Dim AttachmentCnt As Integer
  Dim AttTotal As Integer
  Dim MsgTotal As Integer
  
  Dim outputDir As String
  Dim outputFile As String
  Dim fileExists As Boolean
  Dim cnt As Integer
  
  'Requires reference to Microsoft Scripting Runtime (SCRRUN.DLL)
  Dim fso As FileSystemObject
    
  Set Exp = App.ActiveExplorer
  Set Sel = Exp.Selection
  Set fso = New FileSystemObject

  outputDir = GetOutputDirectory()
  If outputDir = "" Then
    MsgBox "You must pick an directory to save your files to. Exiting SaveAttachments.", vbCritical, "SaveAttachments"
    Exit Sub
  End If
    
  'Loop thru each selected item in the inbox
  For cnt = 1 To Sel.Count
    'If the e-mail has attachments...
    If Sel.Item(cnt).Attachments.Count > 0 Then
      MsgTotal = MsgTotal + 1
      'For each attachment on the message...
      For AttachmentCnt = 1 To Sel.Item(cnt).Attachments.Count
        'Get the attachment
        Dim att As Attachment
        Set att = Sel.Item(cnt).Attachments.Item(AttachmentCnt)
        outputFile = att.fileName
        fileExists = fso.fileExists(outputDir + outputFile)
        Do While fileExists = True
          outputFile = InputBox("The file " + outputFile _
            + " already exists in the destination directory of " _
            + outputDir + ". Please enter a new name, or hit cancel to skip this one file.", "File Exists", outputFile)
          'If user hit cancel
          If outputFile = "" Then
            'Exit leaving fileexists true. That will be a flag not to write the file
            Exit Do
          End If
          fileExists = fso.fileExists(outputDir + outputFile)
        Loop
        
        'Save it to disk if the file does not exist
        If fileExists = False Then
          att.SaveAsFile (outputDir + outputFile)
          AttTotal = AttTotal + 1
        End If
      Next
    End If
  Next
  
  'Clean up
  Set Sel = Nothing
  Set Exp = Nothing
  Set App = Nothing
  Set fso = Nothing
  
  'Let user know we are done
  Dim doneMsg As String
  doneMsg = "Completed saving " + Format$(AttTotal, "#,0") + " attachments in " + Format$(MsgTotal, "#,0") + " Messages."
  MsgBox doneMsg, vbOKOnly, "Save Attachments"
  
  Exit Sub
  
ErrorHandler:

  Dim errMsg As String
  errMsg = "An error has occurred. Error " + Err.Number + " " + Err.Description
  Dim errResult As VbMsgBoxResult
  errResult = MsgBox(errMsg, vbAbortRetryIgnore, "Error in Save Attachments")
  Select Case errResult
    Case vbAbort
      Exit Sub
      
    Case vbRetry
      Resume
      
    Case vbIgnore
      Resume Next
      
  End Select
    
End Sub

Public Function GetOutputDirectory() As String
 
  Dim retval As String 'Return Value
  
  Dim sMsg As String
  Dim cBits As Integer
  Dim xRoot As Integer
  
  Dim oShell As Object
  Set oShell = CreateObject("shell.application")

  sMsg = "Select a Folder To Output The Attachments To"
  cBits = 1
  xRoot = 17
  
  On Error Resume Next
      Dim oBFF
      Set oBFF = oShell.BrowseForFolder(0, sMsg, cBits, xRoot)
      If Err Then
        Err.Clear
        GetOutputDirectory = ""
        Exit Function
      End If
  On Error GoTo 0
  
  If Not IsObject(oBFF) Then
    GetOutputDirectory = ""
    Exit Function
  End If
  
  If Not (LCase(Left(Trim(TypeName(oBFF)), 6)) = "folder") Then
    retval = ""
  Else
    retval = oBFF.self.Path
    
    'Make sure there's a \ on the end
    If Right(retval, 1) <> "\" Then
      retval = retval + "\"
    End If
  End If
  
  GetOutputDirectory = retval
  
End Function

Jun 16 Tommy | CodeBits

CodeBits: Export ICollection To Excel Compatable Format

I've expanded the generic collection handler to include allowing export to excel without the need for the gridview hack.

 

Little background: The gridview hack is to save a collection to the page first in a gridview. From there it is pretty easy to post the data back to the page as a cvs file. However that requires the gridview to be loaded first with all the data and a second post to occur. It is also not user friendly to display the gridview on a page if it has more then 15 columns or so. This function will help avoid that. Just add it to any class that inherits the CollectionBase class.

 

Side Note: A full postback must occur. So if you're going to do the export from a button within an AJAX update panel, make sure the panel has the button set for a full postback.

      
    public void ExportToXls(string fileName)
    {
        HttpContext.Current.Response.Buffer = true;
        HttpContext.Current.Response.Clear();

        // Force as inline, otherwise browsers will think this is an .aspx file or not give it a filename
        HttpContext.Current.Response.AddHeader("content-disposition", "inline; filename=" + fileName);
        HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
        HttpContext.Current.Response.Charset = string.Empty;

        HttpContext.Current.Response.Flush();

        using (StringWriter sw = new StringWriter())
        {
            using (HtmlTextWriter htw = new HtmlTextWriter(sw))
            {
                Table table = new Table();
                table.GridLines = GridLines.Both;

                TableRow header = new TableRow();

                // Use refection to give us a header row with the property names
                foreach (PropertyInfo propertyInfo in this[0].GetType().GetProperties())
                {
                    TableCell cell = new TableCell();
                    cell.Text = propertyInfo.Name;
                    header.Cells.Add(cell);                       
                }

                // Add some style to the header
                header.Style.Add(HtmlTextWriterStyle.BackgroundColor, "#008080");
                header.Style.Add(HtmlTextWriterStyle.Color, "#ffffff");
                header.Style.Add(HtmlTextWriterStyle.TextAlign, "center");

                table.Rows.Add(header);

                for (int i = 0; i < this.Count; i++)
                {
                    TableRow dataRow = new TableRow();

                    foreach (PropertyInfo propertyInfo in this[i].GetType().GetProperties())
                    {
                        TableCell cell = new TableCell();

                        // Error check incase the property is null
                        if (propertyInfo.GetValue(this[i], null) == null)
                        {
                            cell.Text = string.Empty;
                        }
                        else
                        {
                            cell.Text = propertyInfo.GetValue(this[i], null).ToString();
                        }

                        dataRow.Cells.Add(cell);
                       
                    }

                    table.Rows.Add(dataRow);
                }

                table.RenderControl(htw);
                HttpContext.Current.Response.Write(sw.ToString());
            }
        }

        HttpContext.Current.Response.End();
    }  

DataCollectionV2.cs

Jul 23 Tommy | CodeBits

CodeBits: Using IComparer with Generics Collection

.Net is a really cool language. Two things I love about them are Generics and Collections. Now I will combine them and enhance it with IComparer and reflection to create the collection to end all collections. First we will create a generic collections class. Here is our collection:

public class DataCollection : CollectionBase
{
    public virtual T this[int index]
    {
        get { return (T)this.List[index]; }
        set { this.List[index] = value; }
    } 

    public virtual int IndexOf(T item)
    {
        return this.List.IndexOf(item);
    }

    public virtual int Add(T item)
    {
        return this.List.Add(item);
    }

    public virtual void Remove(T item)
    {
        this.List.Remove(item);
    }
 
    public virtual void CopyTo(Array array, int index)
    {
        this.List.CopyTo(array, index);
    }

    public virtual void AddRange(DataCollection collection)
    {
        this.InnerList.AddRange(collection);
    } 

    public virtual void AddRange(T[] collection)
    {
        this.InnerList.AddRange(collection);
    }

    public virtual bool Contains(T item)
    {
        return this.List.Contains(item);
    }

    public virtual void Insert(int index, T item)
    {
        this.List.Insert(index, item);
    }
}

Next we will add a general function to use for sorting objects in our collection.

*Note that this code is part of the collection class.

public void Sort(string sortExpression, SortDirection sortDirection)
{            
    if (_SortField == sortExpression)
    {                
        if (_SortDirection == SortDirection.Ascending)
        {
            _SortDirection = SortDirection.Descending;
        }
        else
        {
            _SortDirection = SortDirection.Ascending;
        }
    }
    else
    {
        _SortDirection = sortDirection;
        _SortField = sortExpression;
    }

    InnerList.Sort(new Comparer(_SortField, _SortDirection));
}

public void Sort(string sortExpression)
{
    _SortDirection = SortDirection.Ascending;            
    _SortField = sortExpression;
    InnerList.Sort(new Comparer(sortExpression));
}

private SortDirection _SortDirection = SortDirection.Ascending;
private String _SortField = string.Empty;

public SortDirection GetSortDirection { get { return _SortDirection; } }
public String GetSortField { get { return _SortField; } }

 

I have seen many sites have a collection like this. Then they implement an IComparer for each object in a collection so that you can sort the collection based on an member of the object in the collection. This is good if you were building something like a Data Access Layer for your application. However these require strong typed collections and objects. That would require a lot of coping and pasting if we wanted to implement an IComparer for each member of the object class. Or we can just use reflection and have one IComparer for all class members.

* Note that this code is a seperate class

public class Comparer : IComparer
{
    string m_SortPropertyName;
    SortDirection m_SortDirection;
    int direction;

    public Comparer(string sortPropertyName)
    {
        this.m_SortPropertyName = sortPropertyName;
        this.m_SortDirection = SortDirection.Ascending;
        direction = -1;
        // default to ascending order
    }

    public Comparer(string sortPropertyName, SortDirection sortDirection)
    {
        this.m_SortPropertyName = sortPropertyName;
        this.m_SortDirection = sortDirection;
        direction = -1;

        if (sortDirection == SortDirection.Descending) { direction = 1; }
    }
 
    public int Compare(object x, object y)
    {

        // Get the values of the relevant property on the x and y objects

        object valueOfX = x.GetType().GetProperty(m_SortPropertyName).GetValue(x, null);
        object valueOfY = y.GetType().GetProperty(m_SortPropertyName).GetValue(y, null);

        IComparable comp = valueOfY as IComparable;

        // Flip the value from whatever it was to the opposite so it sorts correctly.

        return Flip(comp.CompareTo(valueOfX));
    }

 
    private int Flip(int i)
    {
        return (i * direction);
    }
}

 

Now I know whare you are thinking. What would you do with this? Simple:

public void Test
{

    DataCollection collection = new DataCollection();
    //fill collection with dataset here.

    collection.Sort((fieldNameString), SortDirection)
}

 

We can now sort any collection of objects on any member of that object. There is something I like about not having to ever write another collection or sort function ever again, period.

DataCollection.cs

About

Mooglegiant.net is a site maintained by me (mooglegiant).  I occasionally put together blogengine.net themes, and random posts about tech/geek things.  If you like the site, or my work, don't forget to support me.  I'm sure you know where to click.