Click here to Skip to main content
15,884,176 members
Articles / Programming Languages / C#

Building custom email client in WPF using C#

Rate me:
Please Sign up or sign in to vote.
4.58/5 (6 votes)
5 Mar 2016CPOL12 min read 28.6K   12   1
In this post I have talked about building an IMAP client using WPF framework and ImapX library.

Introduction and Background

Yesterday, I was talking to myself about writing another post for C# programming; I agree I am addicted to programming now. So, I wanted to write a blog post and I found that I had a topic on my “write list”. Just not that I wrote about that topic, I added a bit more of programming to it to support another feature. So basically what I had on my writing list was an email client in C# programming language. In this blog post, I will share the basics for developing an email client in C# using Windows Presentation Foundation framework. The source code is very much shorter and compact that building your own basic client takes no time at all. However, adding a few more features would take some time. I will point out those features later in the post itself, and you may add them later.

You are required to have the basic understanding of Windows Presentation Foundation framework and how C# language can be used. Typically, C# can be used to create an application of any kind. However, WPF is a framework that can be extended to any limit and can be used to create a graphical application of high-performance and maximum extensibility. WPF power would be used to build this one SMTP + IMAP application to develop a simple email client.

Understanding the protocols

Before I dig deeper and start to write the source code and explain the methods to build the application. I would try to explain the protocols that would be used in this application. The two of them are:

  1. IMAP: Internet message access protocol.
  2. SMTP: Simple mail transfer protocol.

These protocols are typically used to send and receive email messages. Other than IMAP, POP and POP3 are two protocols used to receive the emails, or to keep the emails on your devices. But, I won’t talk about them.

.NET framework provides native support for SMTP protocol for sending emails through your .NET applications like Console, WinForms or WPF etc. However, .NET framework doesn’t provide any implementation for IMAP protocol. Instead, they provide you with TCP clients and listeners. Since, IMAP and SMTP etc. are all protocols running on TCP protocol; or any other transmission layer protocol like UDP, implementing the native protocols like IMAP is very simple and easy in .NET framework.

For more of such packages and namespace to manage the networking capabilities of an application, read System.Net namespaces on MSDN.

Instead, I will use a library to get myself started in no time. Very long time ago, I came up to a library, “ImapX”, which is a wonderful tool for implementing the IMAP protocol in C# applications. You can get ImapX from NuGet galleries by executing the following NuGet package manager command:

Install-Package Imapx

This would add the package. Remember, Imapx works in only selected environments and not all. You should read more about it, on the CodePlex website.

The IMAP protocol is used to fetch the emails, to read, rather than downloading the entire mailbox and storing it on your own device. It is very flexible protocol because the email is still present on the server, and is accessible on every device that you have. Network latency is not a bad factor here, because you don’t have to download the email entirely. You just download the content that you need right now.

3dUC5U4Ly
Figure 1: IMAP protocol usage on the network.

Building the application

Now that I have pointed out a few of the things that will be used in this application, I guess the time is to continue the programming part and develop the application itself. First of all, create a new application of WPF framework. It is always a good approach to write the concerns and different sections of your applications.

Our application would have the following modules:

  1. Authentication module.
    • Initially we will ask the users for authentication.
    • Due to complexity level, I have only supported Google Mail at the moment.
      • That is why, application only asks for username and password combination.
    • You definitely should leave the maximum fields at the user’s will to be filled so that they can connect to their own IMAP service; other than Google Mail.
  2. Folder view
    • Viewing the folders and their messages.
  3. Message view
    • Viewing the messages and its details.
  4. Create a new message.

Separating these concerns would help us build the application in a much agile way so that when we have to update or create a new feature in the application, doing so won’t take any longer. However, if you hard-code everything in the same page. Then things get really very difficult. In this post, I will also give you a few tips to ensure that things are not made difficult than they can be.

Managing the “MainWindow”

Each WPF application would contain a MainWindow window, which is the default window that gets rendered on the screen. But, my recommendation is that you only create a Frame object in that window. Nothing else. That frame object would be used to navigate to multiple pages and different views of the applications depending on the user and application interactions.

<Grid>
   <Frame Name="mainFrame" NavigationUIVisibility="Hidden" />
</Grid>

One thing to note here is that Frame has a (IMO) really annoying navigation UI. You should keep that hidden. Apply the same setting to frames, or use this in the application resources and apply is application-wide.

This is helpful, in changing the views instead of changing the visibilities of the grids and stack panel objects. However, on the runtime exchange, this won’t be available. To be able to use it, you would have to store the frame instance at a location which can be accessed from external objects too. I created the following property in the class, to hold the reference to this frame.

public static Frame MainFrame { get; set; }

Static fields would be accessible from the out objects, without having to require an instance. It would be helpful and you will find it being helpful in the later source samples below. The class itself is like this:

public partial class MainWindow : Window
{
    public static Frame MainFrame { get; set; }
    public static bool LoggedIn { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        MainFrame = mainFrame;

        // Chance are, its not logged in.
        MainFrame.Content = new LoginPage();

        // Initialize the Imap
        ImapService.Initialize();
    }
}

Pretty much simple, developing the building block of the application. This would allow us to have separate UIs for our separate concerns and features. The main things would be done, rendered and handled by the separate pages that we will have for our application.

Before I get started with rendering the later sections of pages, I would need to explain the IMAP base controller that I had developed to provide and serve me with the basic functionality and features of the IMAP protocol.

ImapService object

It is better to keep things allocated at the same place so that when we need to make a chance we can make change right from there, instead of having to search, “Where I wrote that part of API?” That is why, this service object would contain all of the functions that are going to be used in the application.

We need 4 functions and an event handler (for IDLE support; discussed later)

  1. Initialization function; which is a requirement by design.
  2. Login and logout functions.
  3. Function to get all folders
  4. Function to fetch the email messages of a function.

The event is used to notify when a new message comes.

The object is defined as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ImapX;
using ImapX.Collections;
using System.Windows;

namespace ImapPackage
{
    class ImapService
    {
         private static ImapClient client { get; set; }
  
         public static void Initialize()
         {
              client = new ImapClient("imap.gmail.com", true); 
 
              if(!client.Connect())
              {
                   throw new Exception("Error connecting to the client.");
              }
         }


         public static bool Login(string u, string p)
         {
              return client.Login(u, p);
         }

         public static void Logout()
         {
              // Remove the login value from the client.
              if(client.IsAuthenticated) { client.Logout(); }
       
              // Clear the logged in value.
              MainWindow.LoggedIn = false;
         } 

         public static List<EmailFolder> GetFolders()
         {
              var folders = new List<EmailFolder>();

              foreach (var folder in client.Folders)
              {
                  folders.Add(new EmailFolder { Title = folder.Name });
              }

              // Before returning start the idling
              client.Folders.Inbox.StartIdling(); // And continue to listen for more.

              client.Folders.Inbox.OnNewMessagesArrived += Inbox_OnNewMessagesArrived;
              return folders;
         }

         private static void Inbox_OnNewMessagesArrived(object sender, IdleEventArgs e)
         {
              // Show a dialog
              MessageBox.Show($"A new message was downloaded in {e.Folder.Name} folder.");
         }

         public static MessageCollection GetMessagesForFolder(string name)
         {
              client.Folders[name].Messages.Download(); // Start the download process;
              return client.Folders[name].Messages;
         }
    }
}

This basic class would be used throughout the application to load the resources and messages. I won’t go in the depth of this library because I will just explain a few points and how to use the basic functions and features. However, the library is very strong and powerful and provides you with every tool and service required to build a full featured email client.

Login page

The first step would be to authenticate the users by asking them for their username and passwords. Typically, your application would cache the user authentication details; in most cases username, and in some cases if user allows, then the password too.

In a real application you are going to have a full authentication page, which would provide the fields such as input for username, port, IMAP hosts and passwords etc. But however in my application I have not used any of such fields because I wanted to keep things really simple. I just asked user to username and password, and used other settings hard coded in the application’s source code.

The XAML of the page looks like this:

<StackPanel Width="500" Height="300">
    <TextBlock Text="Login to continue" FontSize="25" />
    <TextBlock Text="We support Google Mail at the moment, only." />
    <Grid Height="150" Margin="0, 30, 0, 0">
       <Grid.ColumnDefinitions>
           <ColumnDefinition />
           <ColumnDefinition Width="3*" />
       </Grid.ColumnDefinitions>
       <StackPanel Grid.Column="0">
           <TextBlock Text="Username" Margin="0, 5, 0, 15" />
           <TextBlock Text="Password" />
       </StackPanel>
       <StackPanel Grid.Column="1">
           <TextBox Name="username" Margin="0, 0, 0, 5" Padding="4" />
           <PasswordBox Name="password" Padding="4" />
       </StackPanel>
    </Grid>
    <Button Width="150" Name="loginBtn" Click="loginBtn_Click">Login</Button>
    <TextBlock Name="error" Margin="10" Foreground="Red" />
</StackPanel>

This content would be loaded and since in the constructor for the MainWindow we had loaded this page, we will see this page initially.

Screenshot (2082)
Figure 2: Authentication page for the application.

Definitely, you are going to provide extra sections and fields for the users to configure the way your application is going to connect to their IMAP service provider. However, this would also work. Mozilla’s Thunderbird does the same, they just ask for the username and password and find the correct settings and configurations themselves. But, however you want, you can write the application in your own way.

We just need to handle the event of the button,

private void loginBtn_Click(object sender, RoutedEventArgs e)
{
    MainWindow.LoggedIn = ImapService.Login(username.Text, password.Password);

    // Also navigate the user
    if(MainWindow.LoggedIn)
    {
        // Logged in
        MainWindow.MainFrame.Content = new HomePage();
    }
    else
    {
        // Problem
        error.Text = "There was a problem logging you in to Google Mail.";
    }
}

Clearly, this would just make the calls to the service that we created previously. The benefit is that we just need to add the validation and verification code here. We  don’t need to write anything new here because we have done all of that in the previously set up service named, “ImapService”. At the end, if everything went correct, it would navigate the user to the home page.

Note: There is no asynchrony in the application and that is why, it may “freeze” sometimes. To overcome this problem you may want to rewrite the entire service provided to develop it with async/await.

Home page

In this application the major component is the home page, the home page is where the folders and messages would be loaded. There is no difficulty in loading those, because we have already created the function to execute to get the folders and messages in those folders (read the ImapService class once again).

The basic XAML controls that are being used here, are the following ones:

<ListView Name="foldersList" SelectionChanged="foldersList_SelectionChanged">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Margin="10" Cursor="Hand" Text="{Binding Title}" Name="folderTitle" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<Frame Grid.Column="1" Name="contentFrame" NavigationUIVisibility="Hidden" />

A list view that would render the folders and another frame that would be used to load multiple pages. I used the same method to hold a reference to this frame, in my class object and I loaded it using the contructor itself.

public HomePage()
{
    InitializeComponent();
    ContentFrame = contentFrame;

    foldersList.ItemsSource = ImapService.GetFolders();

    ClearRoom();
}

Just using the same small steps, I created the page. Which would then have its own functionality and would allow the features to handle their interaction with the users. 

Screenshot (2052)Figure 3: Folders listed in the application.

We can load the folder messages by selecting a folder. That would show the messages in the right side and we can then read a message. The code for which is provided in the application itself.

private void foldersList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var item = foldersList.SelectedItem as EmailFolder;

    if(item != null)
    {
        // Load the folder for its messages.
        loadFolder(item.Title);
    }
}

private void loadFolder(string name)
{
    ContentFrame.Content = new FolderMessagesPage(name);
}

This way, we load the folder in the content frame that we are having in the application’s home page.

Sending the emails

We have talked a lot about the IMAP protocol and I guess I should also share a few points about the SMTP protocol support in this application. SMTP protocol support is provided natively in .NET framework.

I have written an article for SMTP protocol support in .NET framework, which you may be interested in,  Sending emails over .NET framework, and general problems – using C# code. That article discusses a lot about doing so, and guess what, I used the same code from that article and wrote the entire feature for sending the emails on it.

I created the “send email” form as follows:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0">
        <TextBlock Text="To" Margin="0, 5" />
        <TextBlock Text="Subject" Margin="0, 5" />
        <TextBlock Text="Body" Margin="0, 5" />
    </StackPanel>
    <StackPanel Grid.Column="1">
        <TextBox Margin="0, 3" Padding="1" Name="to" />
        <TextBox Margin="0, 3" Padding="1" Name="subject" />
        <TextBox Margin="0, 3" Padding="1" Height="130" Name="body" 
                 AcceptsReturn="True"
                 ScrollViewer.CanContentScroll="True" 
                 ScrollViewer.VerticalScrollBarVisibility="Auto" />
        <Button Width="50" Name="sendBtn" Click="sendBtn_Click">Send</Button>
        <Button Width="50" Name="cancelBtn" 
               Click="cancelBtn_Click" Margin="130, -20, 0, 0">Cancel</Button>
    </StackPanel>
</Grid>

Although, I understand that is not the best of what I could have made, but, for the sake of this post, that would (should!) suffice. This would allow the user to enter the details for a very basic email.

Screenshot (2056)
Figure 4: SMTP protocol test, email form. 

Upon clicking the send button, the email would be sent. The code to do so is really straight-forward and (as mentioned ago) available on that article I wrote about sending the emails in C#.

Bonus: IDLE support for folders

Before I wind things up, I wanted to enlighten the topic of IDLE support. In IMAP, IDLE support means that your application don’t need to send the request to the server in order to fetch new messages. Instead, the server would itself send the data to the client, whenever a new email is received.

What I did was, that I wrote that code to support IDLE feature in the ImapService because that was a service of IMAP and not the application itself. I updated the folder fetching code and added the following statements, (or since I haven’t shared the code at all, the code is like the following),

public static List<EmailFolder> GetFolders()
{
     var folders = new List<EmailFolder>();
 
     foreach (var folder in client.Folders)
     {
          folders.Add(new EmailFolder { Title = folder.Name });
     }

     // Before returning start the idling
     client.Folders.Inbox.StartIdling(); // And continue to listen for more.

     client.Folders.Inbox.OnNewMessagesArrived += Inbox_OnNewMessagesArrived;

     return folders;
}

private static void Inbox_OnNewMessagesArrived(object sender, IdleEventArgs e)
{
     // Show a dialog
     MessageBox.Show($"A new message was downloaded in {e.Folder.Name} folder.");
}

The handler and the event would then work as a counter-part and would provide us with a very beautiful service in which our application would be able to fetch the messages when a message is received!

We can use a folder, to start IDLE for. In my case, I used Inbox folder. Inbox folder would let us know for any new email message that we may receive and we can then use the properties of the event arguments to find out which folder received which message.

Points of Interest

In .NET framework, SMTP protocol is supported natively, implementing the IMAP protocol is a bit of a task. For that, developers may use ImapX, which is a free and open source package and library for C# applications that may want to support email messages.

In this blog, I have explained the methods used to build the applications that can receive and send email messages on the internet to the clients. IMAP protocol is much preferred and loved protocol because it doesn’t need you to download the emails, you can just download the stuff that you need.

If I write something extra, I will update the post or create a new one. Do leave me a message if you find something missing. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Pakistan Pakistan
Afzaal Ahmad Zeeshan is a computer programmer from Rabwah, Pakistan, currently living in The Netherlands, likes .NET Core and Node.js for regular everyday development. Afzaal Ahmad works at Adyen as a Developer Advocate.

He is an expert with Cloud, Mobile, and API development. Afzaal has experience with the Azure platform and likes to build cross-platform libraries/software with .NET Core. Afzaal is an Alibaba Cloud MVP, twice he has been awarded Microsoft MVP status for his community leadership in software development, four times CodeProject MVP status for technical writing and mentoring, and 4 times C# Corner MVP status in the same field.

Comments and Discussions

 
QuestionType EmailFolder is not defined Pin
Member 1333688210-Jan-18 17:25
Member 1333688210-Jan-18 17:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.