BLOG.CSHARPHELPER.COM

BOOK DRAWING: MCSD Certification Toolkit (Exam 70-483: Programming in C#)

MCSD Certification Toolkit
Exam 70-483: Programming in C#
$59.99, 648 pages, 2013
Wrox
ISBN 13: 978-1118612095
By: Tiberiu Covaci, Rod Stephens, Vincent Varallo, Gerry O'Brien
Book sales these days are heavily driven by reviews and this book hasn't gotten enough. In an attempt to generate more reviews, I'm giving away 5 copies of the book. Here are some details:

  • By entering the drawing, you promise to post a review. I don't want you to promise a good review, just an honest review. I think this is a good book and it can stand for itself.
  • This is not a beginner's book. It covers just about the entire C# language in the depth necessary to take the MCSD Certification Exam 70-483: Programming in C#. If you don't have some experience with C#, you may find it a difficult book.
  • I am not the sole author of this book. I wrote these chapters:
    • Chapter 4: Using Types - Converting between types, casting, manipulating strings, formatting values.
    • Chapter 5: Creating and Implementing Class Hierarchies - Deriving one class from another, using the base and this keywords, defining and using interfaces, implementing important interfaces, managing resources.
    • Chapter 6: Working with Delegates, Events and Exceptions - Understanding delegates and predefined delegate types, anonymous methods, publishing and subscribing to events, catching and re-throwing exceptions, creating custom exceptions.
    • Chapter 11: Input Validation, Debugging and Instrumentation - Input validation, regular expressions, preprocessor directives and symbols, tracing, logging, profiling.
  • Each chapter includes exercises that you can use to test yourself.
  • You can download additional exercises, cheat sheets, and sample code at this page.
  • This book focuses on the C# programming language not on the IDE, user interface design, or Metro issues (or whatever Microsoft is calling the new Windows 8 style today).
  • My chapters use Visual Studio Express 2012 for Windows Desktop, which you can download for free here.
  • To enter, send an email containing your name and snail mail address with the subject "C# book drawing" to [email protected] by June 4, 2013.

If you want to hone your C# skills and see if you have what it takes to take the MCSD certification exam, sign up for the drawing and you might win a free book!

(If by some chance you already own a copy of this book, or a copy of any of my books, please post a review! Reviews are what sells books and without sales I can't keep writing. Please don't make me get a real job!)

How to find the Immediate window in Visual Studio Express Edition

Some versions of Visual Studio Express Edition come with various tools deactivated or hidden. It's not clear whether Microsoft does this to gently encourage you to buy a more expensive version or whether they truly don't think Express Edition users would want those tools, but it can be quite annoying. One particularly annoying omission is the Immediate window.

There are a couple ways you can retrieve the Immediate window. To open the window once, you can:

  1. Open the View menu.
  2. Open the Other Windows sub menu.
  3. Select Command.
  4. In the Command window, type "immed" and press Enter.

When you close and reopen Visual Studio, the Immediate window should still be visible.

You can also add the Immediate Window command to a menu. For example, in non-Express editions it appears in the Debug menu's Windows submenu.

The exact details for customizing Visual Studio varies by version. In Visual Studio 2008 try these steps:

  1. Open the Tools menu.
  2. Select the Customize command to see the dialog shown above.
  3. On the Commands tab, select the Debug Category.
  4. In the Commands list, find the Immediate command and drag it into the toolbar or menu where you want to put it.
  5. (As long as you're in there, you can look at the other commands that are available and place any that you think might be useful in menus or toolbars. Tip: Make new toolbar to hold your favorites.)

Now you can find the Immediate window when you want it!

Calculate the formula for an ellipse selected by the user in C#

This is example is very similar to Calculate the formula for a circle selected by the user in C# except it allows the user to pick an ellipse instead of a circle. See that post or download the example program for information about how the program lets the user select the shape and how the program draws the selected shape.


The big difference between the two examples is that this example uses the equation for an ellipse not a circle. The picture on the right shows the geometry for an ellipse with major and minor axes parallel to the X and Y axes.

After the user selects an ellipse, the program averages the minimum and maximum X and Y coordinates to find the center of the ellipse (Dx, Dy). It then divides the ellipse's width and height by two to get the parameters Rx and Ry. It can then display the equation shown in the top picture above.


The equations on the left show how you can convert the normal form of the equation into a function that gives y in terms of x. As is the case with a circle, the result includes two equations that use positive and negative square roots.

Like the previous example that draws circles, this example verifies the function by using normal Graphics objects to fill the ellipse and then drawing the points on the ellipse calculated by using the function.


Any conic section (circle, ellipse, parabola, or hyperbola) can be represented as an equation of the form:

Where A, B, C, D, E, and F are constants. To find this form of an equation for an ellipse, start with the following equation:

Multiply this out to get:

Rearrange the result to group related terms and get:

This is the desired format where:

Where:

   

Calculate the formula for a circle selected by the user in C#

This example lets the user click and drag to select a circle. The program uses the usual MouseDown, MouseMove, and MouseUp event handlers to let the user draw a circle. The only trick is that it makes the width and height of the object it draws the same so it draw a circle and not an ellipse.

The following code shows the part of the form's Paint event handler that draws a new circle while the user is selecting it.

// Draw the new circle if we are drawing one.
if (DrawingCircle)
{
    // Make it a circle.
    int diameter = Math.Max(
        Math.Abs(StartX - EndX), Math.Abs(StartY - EndY));
    e.Graphics.DrawEllipse(Pens.Red,
        Math.Min(StartX, EndX), Math.Min(StartY, EndY),
        diameter, diameter);
}

Here StartX and StartY are the coordinates of the point where the user pressed the mouse down. EndX and EndY are the mouse's current location during the click-and-drag.

This code uses the maximum of the selected area's width and height as the circle's diameter. It uses the minimum X and Y coordinates of the selected area as the upper left corner for the circle and draws the circle.

The rest of the code that selects a circle is fairly straightforward. Download the example to see the details.

The more interesting code is in the following GetCircleFormula method.

// Get the equation for this circle.
private void GetCircleFormula(RectangleF rect,
    out float dx, out float dy, out float r)
{
    dx = rect.X + rect.Width / 2f;
    dy = rect.Y + rect.Height / 2f;
    r = rect.Width / 2f;
}

As its name implies, this method calculates the circle's formula. The figure to the right shows the geometry of a circle.

The GetCircleFormula method finds the center of the circle (Dx, Dy). It divides the circle's width by 2 to get the radius.

Now the program can display the equation in a Label.

Sometimes it's convenient to use another type of equation for a circle. Using the equations on the right, you can solve the original equation to make an equation giving y as a function of x.

Note that taking the square root means there are two possible solutions to the equation, one where you take the positive root and one where you take the negative root. (Because when you square a negative number, you get a positive number.) The positive and negative roots give the upper and lower halves of the circle.

The following code shows how the program draws the circle after the user selects it.


// Draw the circle if we have one.
if (GotCircle)
{
    // Fill the circle.
    e.Graphics.FillEllipse(Brushes.LightBlue, Circle);

    // Plot the circle's equation.
    List points = new List();
    for (float x = Dx - R; x <= Dx + R; x++)
    {
        float radicand = x - Dx;
        radicand = R * R - radicand * radicand;
        if (radicand >= 0f)
        {
            points.Add(new PointF(
                x, (float)(Dy + Math.Sqrt(radicand))));
        }
    }
    for (float x = Dx + R; x >= Dx - R; x--)
    {
        float radicand = x - Dx;
        radicand = R * R - radicand * radicand;
        if (radicand > 0f)
        {
            points.Add(new PointF(
                x, (float)(Dy - Math.Sqrt(radicand))));
        }
    }
    e.Graphics.DrawPolygon(Pens.Blue, points.ToArray());
}

First the code fills the circle with light blue.

Next the program makes variable x go from the circle's minimum X value to its maximum X value. It uses the function with the positive root to generate points along the top half of the circle. (Vocabulary word for the day: A radicand is the value inside a radical symbol.)

Then the program makes variable x go from the circle's maximum X value down to its minimum X value. It uses the function with the negative root to generate points along the bottom half of the circle. The program generates these points in order of decreasing X value so they begin where the points on the top half of the circle left off.

Finally the program draws the points as a polygon. You can see in the first picture that the blue outline fits the light blue interior of the circle so we know the formula is correct.

   

Map device coordinates to world coordinates in C#

Sometimes it's convenient to draw in one coordinate system (called world coordinates) and map those coordinates to the screen's device coordinates. The example, Use transformations to map points from one coordinate system to another when drawing in C# shows how to do this in C#.

For example, the picture shown here draws ellipses. The axes show the X and Y coordinate systems used. For example, the purple ellipse is about 7.75 units wide and 1 unit tall.

The program uses a transformation to scale and translate the drawing so the ellipses are centered and drawn at a reasonable size. Without the transformation, the ellipses would be tiny little marks just a few pixels in size in the PictureBox's upper left corner.

That much is described by the earlier example. The new feature here is that the program allows the user to click and drag to define new ellipses. The reason this is not trivial is that the picture is drawn with the transformation but the PictureBox's mouse events use normal device coordinates. If you use those coordinates, then any new ellipses would be huge and not centered property after they were transformed.

The solution to this problem is to transform the mouse coordinates by using the inverse of the transformation used to draw the ellipses. For example, the drawing transformation enlarges the ellipses so they have a reasonable size. The inverse transformation reduces the mouse coordinates during a click and drag so the resulting ellipse is small enough to draw correctly when modified by the drawing transformation.

That's the theory. Here's the code.

The following code shows how the program stores information about the ellipses.

// The user's ellipses.
private List Ellipses = new List();
private List Colors = new List();

// Used while drawing a new ellipse.
private bool Drawing = false;
private PointF StartPoint, EndPoint;

// The transformations.
private Matrix Transform = null, InverseTransform = null;
private const float DrawingScale = 50;

// The world coordinate bounds.
private float Wxmin, Wxmax, Wymin, Wymax;

This code defines lists to hold the ellipses and their colors. The Drawing, StartPoint, and EndPoint variables are used to let the user click and drag to create a new ellipse.

The Transform and InverseTransform variables are the matrices used to transform the drawing and inverse transform mouse coordinates.

Finally Wxmin, Wxmax, Wymin, and Wymax store the world coordinates used to draw the ellipses.

When the picCanvas PictureBox resizes, the following code executes.

// Create new transformations to center the drawing.
private void picCanvas_Resize(object sender, EventArgs e)
{
    CreateTransforms();
    picCanvas.Refresh();
}

This code calls the following CreateTransforms method and then refreshes the PictureBox.

// Create the transforms.
private void CreateTransforms()
{
    // Make the draw transformation. (World --> Device)
    Transform = new Matrix();
    Transform.Scale(DrawingScale, DrawingScale);
    float cx = picCanvas.ClientSize.Width / 2;
    float cy = picCanvas.ClientSize.Height / 2;
    Transform.Translate(cx, cy, MatrixOrder.Append);

    // Make the inverse transformation. (Device --> World)
    InverseTransform = Transform.Clone();
    InverseTransform.Invert();

    // Calculate the world coordinate bounds.
    Wxmin = -cx / DrawingScale;
    Wxmax = cx / DrawingScale;
    Wymin = -cy / DrawingScale;
    Wymax = cy / DrawingScale;
}

This method makes a new Matrix object named Transform. It uses the object's Scale method to apply a scaling transformation to enlarge the drawing. The code then uses the object's Translate method to add another transformation to the Matrix to center the drawing in the PictureBox.

That completes the drawing transformation. It first scales and then translates the drawing.

Now the code makes a clone of the drawing transformation and calls the new Matrix object's Invert method to invert it. This is the transformation that maps from mouse coordinates into world coordinates. (Basically it does the opposite of whatever the drawing transformation does.)

The method finishes by calculating the minimum and maximum X and Y coordinates that will appear in the drawing area. (It uses them to decide how long to draw the axes.)

The following code shows how the program uses the drawing transformation.

// Draw.
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
    // If we don't have the transforms yet, get them.
    if (Transform == null) CreateTransforms();

    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.Transform = Transform;

    // Use a pen that isn't scaled.
    using (Pen thin_pen = new Pen(Color.Black, 0))
    {
        // Draw the axes.
        float tic = 0.25f;
        thin_pen.Width = 2 / DrawingScale;
        e.Graphics.DrawLine(thin_pen, Wxmin, 0, Wxmax, 0);
        for (int x = (int)Wxmin; x <= Wxmax; x++)
            e.Graphics.DrawLine(thin_pen, x, -tic, x, tic);
        e.Graphics.DrawLine(thin_pen, 0, Wymin, 0, Wymax);
        for (int y = (int)Wymin; y <= Wymax; y++)
            e.Graphics.DrawLine(thin_pen, -tic, y, tic, y);

        // Draw the ellipses.
        thin_pen.Width = 0;
        for (int i = 0; i < Ellipses.Count; i++)
        {
            using (Brush brush = new SolidBrush(Color.FromArgb(128, Colors[i])))
            {
                e.Graphics.FillEllipse(brush, Ellipses[i]);
            }
            thin_pen.Color = Colors[i];
            e.Graphics.DrawEllipse(thin_pen, Ellipses[i]);
        }

        // Draw the new ellipse.
        if (Drawing)
        {
            thin_pen.Color = Color.Black;
            e.Graphics.DrawEllipse(thin_pen,
                Math.Min(StartPoint.X, EndPoint.X),
                Math.Min(StartPoint.Y, EndPoint.Y),
                Math.Abs(StartPoint.X - EndPoint.X),
                Math.Abs(StartPoint.Y - EndPoint.Y));
        }
    }
}

If the program hasn't created the transformations yet, it calls CreateTransforms to do so now.

Next the program sets the Graphics object's SmoothingMode property to get a smooth picture. It also sets the object's Transform property to the drawing transformation matrix.

The code then creates a Pen with width 0. That width tells the program to draw with the thinnest line possible. (If you don't do this, then the pen is scaled by the drawing transformation so the ellipses are drawn with huge edges.)

The rest of the method is reasonably straightforward. It draws the axes and then loops through the ellipses drawing them. If the program is in the middle of drawing a new ellipse, the method finishes by drawing it.

The following code shows how the program uses the inverse transformation to map from mouse (device) coordinates to world coordinates.

// Convert from device coordinates to world coordinates.
private PointF DeviceToWorld(PointF point)
{
    PointF[] points = { point };
    InverseTransform.TransformPoints(points);
    return points[0];
}

The Matrix class provides a TransformPoints method that transforms an array of points by applying the its transformation. The DeviceToWorld method takes a point in device coordinates as a parameter. It creates an array holding that point and calls the inverse transformation matrix's TransformPoints method to transform the point to world coordinates. It then returns the converted point.

The rest of the program's code is fairly straightforward. The mouse events that let the user click and drag call the DeviceToWorld method to convert mouse coordinates into device coordinates. For example, the following code shows the PictureBox's MouseDown event handler.

// Let the user draw a new ellipse.
private void picCanvas_MouseDown(object sender, MouseEventArgs e)
{
    Drawing = true;

    // Get the start and end points.
    StartPoint = DeviceToWorld(e.Location);
    EndPoint = StartPoint;
}

This is just like any other click and drag mouse event except it calls DeviceToWorld.

Download the example to see the rest of the code.

   

How to send an SMS text message in C#

This example finishes the series showing how to make a C# program send an SMS (Short Message Service) message. You could use this technique to make a program monitor some sort of ongoing process and send you a message if there is a problem.

The example How to extract only some of the information from a JSON file in C# demonstrates how you can download a JSON file that contains information about SMS gateway email addresses and extract the carrier and email information. The example How to send an email message in C# shows how to send an email.

This example combines the techniques demonstrated in those examples to send an SMS message.

When the program starts, it uses the techniques demonstrates by the first example to get the SMS carrier information.

The program executes the following code when you fill in the information and click Send.


// Send the message.
private void btnSend_Click(object sender, EventArgs e)
{
    try
    {
        string carrier_email = cboEmail.SelectedItem.ToString();
        string phone = txtPhone.Text.Trim().Replace("-", "");
        phone = phone.Replace("(", "").Replace(")", "").Replace("+", "");
        string to_email = phone + "@" + carrier_email;

        SendEmail(txtToName.Text, to_email,
            txtFromName.Text, txtFromEmail.Text,
            txtHost.Text, int.Parse(txtPort.Text),
            chkEnableSSL.Checked, txtPassword.Text,
            txtSubject.Text, txtBody.Text);

        MessageBox.Show("Message sent");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The program first gets the SMS carrier's email address from the cboEmail ComboBox. The email address the program uses must not contain any characters other than digits so the code trims the address to remove spaces. It also removes the -, (, ), and + characters.

Next the code appends the @ symbol and the carrier's SMS gateway email address. The result should look something like this:

    [email protected]

The program finishes by calling the SendEmail method described in the second post mentioned above to send the message to this email address.

That's all there is to it! The tools you need to download the JSON carrier data, parse the data, and send the email message are somewhat involved. Once you've built those tools, however, sending the SMS message is comparatively easy.

   

How to send an email message in C#

The next tool you need to make a program send an SMS message is a method for sending an email. In recent years this has become harder because there are few email providers willing to let you send anonymous emails. Now to send an email, you need to create a NetworkCredential object that specifies your email server, user name, and password.

This example uses the following SendEmail method to send an email.


using System.Net;
using System.Net.Mail;

// Send an email message.
private void SendEmail(string to_name, string to_email,
    string from_name, string from_email,
    string host, int port, bool enable_ssl, string password,
    string subject, string body)
{
    // Make the mail message.
    MailAddress from_address = new MailAddress(from_email, from_name);
    MailAddress to_address = new MailAddress(to_email, to_name);
    MailMessage message = new MailMessage(from_address, to_address);
    message.Subject = subject;
    message.Body = body;

    // Get the SMTP client.
    SmtpClient client = new SmtpClient()
    {
        Host = host,
        Port = port,
        EnableSsl = enable_ssl,
        UseDefaultCredentials = false,
        Credentials = new NetworkCredential(from_address.Address, password),
    };

    // Send the message.
    client.Send(message);
}

The method creates MailAddress objects to represent the From and To email addresses. You can use strings for these when you make the MailMessage but then you can't associate a human-friendly name (like "Rod Stephens") with the email addresses.

Next the program creates a MailMessage, passing its constructor the From and To addresses. The code then sets the MailMessage's Subject and Body fields.

The method then creates an SmtpClient object. It sets the client's host and port so it knows where to send the email. The method sets the EnableSsl property according to the value it was passed as a parameter. If your email server uses SSL (Secure Sockets Layer--Gmail uses this), check the box on the form so this is set to true.

The code also sets UseDefaultCredentials to false and sets the client's Credentials property to a new NetworkCredential object containing your email user name and password.

Finally the method calls the SmtpClient's Send method to send the email.

The System.Net.Mail.MailMessage class supports some other features that you might want to use. For example, it has CC and Bcc properties that let you send courtesy copies and blind courtesy copies. The version shown here is good enough for the next post, which sends an SMS message.

The following code shows how the program calls the SendEmail method.

// Send the message.
private void btnSend_Click(object sender, EventArgs e)
{
    try
    {
        SendEmail(txtToName.Text, txtToEmail.Text,
            txtFromName.Text, txtFromEmail.Text,
            txtHost.Text, int.Parse(txtPort.Text),
            chkEnableSSL.Checked, txtPassword.Text,
            txtSubject.Text, txtBody.Text);
        MessageBox.Show("Message sent");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

This code simply calls SendEmail passing it the values you entered on the form.

Note that the form's initial values for host and port (smtp.gmail.com and 587) work for Gmail. If you want to use some other email host, you'll need to change those values.

The next post will bring together the results of this post and the previous JSON posts to send an SMS message.

   

How to extract only some of the information from a JSON file in C#

Recall from my post Anatomy of an example that my original goal with this series of articles was to write a program to send an SMS message. To do that, I want to download a file listing SMS gateway email addresses. The example Download and display a text file whenever a program starts in C# shows how to download the file.

That file is a JSON format file. The example Use JSON to serialize and deserialize objects in C# shows how to use JSON to serialize and deserialize objects. You could use that technique to deserialize the SMS gateway file. You would define an object that represents the kinds of data contained in the SMS gateway file and then simply deserialize the file.

After staring for quite a while at the file, I decided that this would be fairly hard. It's a big file with a complex structure and it contains lots of information that I don't really need, so I decided on a different approach. (One that also makes a good blog post.)

Instead of trying to understand the structure of the entire JSON file, this example reads it without really knowing what it represents and then sifts through it to find the data it needs.

The following text shows the structure of the JSON file (with LOTS of data omitted).

{
        "info" : "JSON array ...",
        "license" : "MIT or LGPL...",
        "lastupdated" : "2012-07-01",
        "countries" : {
                "us" : "United States",
                "ca" : "Canada",
                "ar" : "Argentina",
                "aw" : "Aruba",
            ...
        },
        "sms_carriers" : {
                "us" : {
                        "airfire_mobile" : ["Airfire Mobile",
                            "{number}@sms.airfiremobile.com"],
                        "alltel" : ["Alltel", "{number}@message.alltel.com"],
                        ...
                        "at_and_t_mobility" : ["AT&T Mobility (Cingular)",
                            "{number}@txt.att.net",
                            "{number}@cingularme.com",
                            "{number}@mobile.mycingular.com"],
                        ...
                },
                "ca" : {
                        "aliant" : ["Aliant", "{number}@chat.wirefree.ca"],
                        ...
                },
            ...
        },
        "mms_carriers" : {
                "us" : {
                        "alltel" : ["Alltel", "{number}@mms.alltel.com"],
                        ...
                },
                ...
        }
}

Notice that the us/at_and_t_mobility carrier supports three email addresses. Normally you can use the first one if a carrier has more than one email address, but the program displays them all in case you know which one you want to use.

The following code shows the two classes that the program uses to store the information it gets from the file.

public class CarrierInfo
{
    public string CarrierAbbreviation, CarrierName;
    public List<string> Emails = new List<string>();

    public override string ToString()
    {
        return CarrierName;
    }
}

public class CountryInfo
{
    public string CountryAbbreviation, CountryName;
    public List<CarrierInfo> Carriers = new List<CarrierInfo>();

    public override string ToString()
    {
        return CountryName;
    }
}

The CarrierInfo class stores a cell phone carrier's abbreviation and name, and a list of supported SMS gateway email addresses.

The CountryInfo class stores a country's abbreviation and name, and a list of carriers that are available in that country.

The code that reads the data is fairly long but not super complicated. Basically it loads the data into a dictionary where the keys are strings and the values are objects. Many of the values are also dictionaries with a similar structure.

For example, the file's top-level data is stored in a dictionary with keys info, license, lastupdated, countries, sms_carriers, and mms_carriers. The sms_carriers entry is a dictionary with keys us, ca, and other country abbreviations. Each of the entries in the sms_carriers dictionary is another dictionary with keys that are carrier abbreviations and with values that are arrays holding a carrier's name and email addresses.

The following code shows how the program reads the data.

// Add a reference to System.Web.Extensions.dll.
using System.Web.Script.Serialization;

using System.IO;
using System.Net;
...
private void Form1_Load(object sender, EventArgs e)
{
    // Get the data file.
    const string url = "https://raw.github.com/cubiclesoft/" +
        "email_sms_mms_gateways/master/sms_mms_gateways.txt";
    string serialization = GetTextFile(url);

    // Add a reference to System.Web.Extensions.dll.
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    Dictionary<string, object> dict =
        (Dictionary<string, object>)serializer.DeserializeObject(serialization);

    // Get the countries.
    Dictionary<string, CountryInfo> country_infos =
        new Dictionary<string, CountryInfo>();
    Dictionary<string, object> countries =
        (Dictionary<string, object>)dict["countries"];
    foreach (KeyValuePair<string, object> pair in countries)
    {
        CountryInfo country_info = new CountryInfo()
            { CountryAbbreviation = pair.Key, CountryName = (string)pair.Value };
        country_infos.Add(country_info.CountryAbbreviation, country_info);
    }

    // Get the SMS carriers.
    Dictionary<string, object> sms_carriers =
        (Dictionary<string, object>)dict["sms_carriers"];
    foreach (KeyValuePair<string, object> pair in sms_carriers)
    {
        // Get the corresponding CountryInfo.
        CountryInfo country_info = country_infos[pair.Key];

        // Get the country's carriers.
        Dictionary<string, object> carriers =
            (Dictionary<string, object>)pair.Value;
        foreach (KeyValuePair<string, object> carrier_pair in carriers)
        {
            // Create a CarrierInfo for this carrier.
            CarrierInfo carrier_info = new CarrierInfo()
                { CarrierAbbreviation = carrier_pair.Key };
            country_info.Carriers.Add(carrier_info);
            object[] carrier_values = (object[])carrier_pair.Value;
            carrier_info.CarrierName = (string)carrier_values[0];
            for (int email_index = 1; email_index <
                carrier_values.Length; email_index++)
            {
                string email = (string)carrier_values[email_index];
                carrier_info.Emails.Add(email.Replace("{number}", ""));
            }
        }
    }

    // Display the countries.
    cboCountry.Items.Clear();
    foreach (CountryInfo country in country_infos.Values)
    {
        cboCountry.Items.Add(country);
    }

    // Make an initial selection.
    cboCountry.SelectedIndex = 0;
}

The program defines the URL where it will get the file. It then uses the GetTextFile method described in Download and display a text file whenever a program starts in C# to get the file.

Next the code creates a JavaScriptSerializer. Unlike the serializers described in the previous example that serializes and deserializes objects, this serializer doesn't know what kind of object it is deserializing.

The program call's the serializer's DeserializeObject method. That method returns a Dictionary that contains strings associated with objects. The objects hold various kinds of data depending on what's in the JSON file.

This example gets the "countries" entry from the dictionary. This entry is another dictionary that contains the abbreviations and names of countries used by the JSON file.

The program loops through the country dictionary's key/value pairs. For each country pair, the program stores the country's abbreviation and name in a CountryInfo object. It stores the new CountryInfo object in a dictionary named country_infos using the country's abbreviation as the key.

Next the program returns to the dictionary that represents the JSON file's highest level of data and gets the sms_carriers entry in that dictionary. This entry is another dictionary that holds information about the carriers that are represented in the file.

The program loops over the carrier information. Because the carriers are grouped by country, the keys in the key/value pairs are country abbreviations. The program uses those to look up the corresponding CountryInfo object in the country_infos dictionary.

Next the program uses the value part of the carrier's key/value pair to get the information about the carrier. It creates a CarrierInfo object and adds it to the appropriate CountryInfo object's Carriers list. Finally the code adds the email addresses for the carrier to the CarrierInfo object's Emails list.

The method then displays the names of the countries it loaded in the cboCountry ComboBox. It finishes by selecting the first country in the ComboBox so the program always has country selected.

The program's remaining code updates its ComboBox's when the user makes a selection. When the user selects a country, the carriers ComboBox displays a list of the carriers available in that country. When the user selects a carrier, the email addresses ComboBox displays a list of email addresses provided by that carrier.

The following code shows how the program responds when the user picks a country.

// Display the selected country's carriers.
private void cboCountry_SelectedIndexChanged(object sender, EventArgs e)
{
    if (cboCountry.SelectedIndex < 0)
    {
        cboCarrier.SelectedIndex = -1;
    }
    else
    {
        // Get the selected CountryInfo object.
        CountryInfo country = cboCountry.SelectedItem as CountryInfo;
        Console.WriteLine("Country: " + country.CountryAbbreviation +
            ": " + country.CountryName);

        // Display the CountryCarrier's carriers.
        cboCarrier.DataSource = country.Carriers;
    }
}

If the user has selected a country, the program converts the selected country into its corresponding CountryInfo object. It then sets the cboCarrier ComboBox's DataSource property to the country's Carriers property. That property is a list of CarrierInfo objects so the ComboBox displays them.

When the user picks a carrier from the carriers ComboBox, the following code executes.

// Display the selected carrier's emails addresses.
private void cboCarrier_SelectedIndexChanged(object sender, EventArgs e)
{
    if (cboCarrier.SelectedIndex < 0)
    {
        cboEmail.SelectedIndex = -1;
    }
    else
    {
        // Get the selected CarrierInfo object.
        CarrierInfo carrier = cboCarrier.SelectedItem as CarrierInfo;
        Console.WriteLine("Carrier: " + carrier.CarrierName);

        // Display the Carrier's email addresses.
        cboEmail.DataSource = carrier.Emails;
    }
}

This code converts the selected carrier into its CarrierInfo object. It then sets the email address ComboBox's DataSource property to the CarrierInfo object's Emails property so it displays the list of available email addresses.

The program doesn't do anything when the user selects an email address.

At this point, you can download the SMS gateway file and get the information you need out of it to find an SMS gateway email address. The next step is to send email to that address. The next two posts explain how to do that.

   

Use JSON to serialize and deserialize objects in C#

JSON (JavaScript Object Notation) is a standard for textual storage and interchange of information, much as XML is. Before you roll your eyes and ask if we really need another language to do what XML does, consider how verbose XML is. Even relatively simple object hierarchies can take up a considerable amount of space when represented by XML. JSON is a more compact format that stores the same kinds of information in less space.

When XML first came out, I thought to myself, "This is a really verbose language. It could be so much more concise. I guess people are willing to spend the extra space to get a more readable format. And after all, storage space is cheaper and network speed is faster than ever before." If you remember having similar thoughts, *now* you can roll your eyes.

JSON's basic data types are:

  • Number
  • String
  • Boolean
  • Array (sequence of values separated by commas and enclosed in brackets [ ])
  • Object (a collection of key:value pairs with pairs separated by commas and the whole collection surrounded by braces { })
  • null

For example, the following text shows a JSON representation of a Customer object.

{
  "City":"Bugsville",
  "EmailAddresses":
  [
    "[email protected]",
    "[email protected]",
    "[email protected]"
  ],
  "Name":"Rod Stephens",
  "Orders":
  [
    {"Description":"Pencils, dozen","Price":1.13,"Quantity":10},
    {"Description":"Notepad","Price":0.99,"Quantity":3},
    {"Description":"Cookies","Price":3.75,"Quantity":1}
  ],
  "PhoneNumbers":
  [
    {"Key":"Home","Value":"111-222-3333"},
    {"Key":"Cell","Value":"222-333-4444"},
    {"Key":"Work","Value":"333-444-5555"}
  ],
  "State":"CA",
  "Street":"1337 Leet St",
  "Zip":"98765"
}

Some of the fields such as City, Name, and State are simple string values. The EmailAddresses value is an array of strings.

The Orders value is an array of Order objects, each of which has a Description, Price, and Quantity. (Really this should be a collection of Order objects, each of which has a collection of OrderItem objects that have Description, Price, and Quantity properties, but the structure shown here is complicated enough already.)

The PhoneNumbers value is a dictionary where the keys are phone number types (Home, Cell, Work) and the values are the phone number strings.

This example builds a Customer object. It then serializes it into a JSON format and then deserializes it to re-create the original object.

The following code shows the Order class.

// Add a reference to System.Runtime.Serialization.
using System.Runtime.Serialization;

namespace howto_use_json
{
    [Serializable]
    public class Order
    {
        [DataMember]
        public string Description;

        [DataMember]
        public int Quantity;

        [DataMember]
        public decimal Price;

        // Return a textual representation of the order.
        public override string ToString()
        {
            decimal total = Quantity * Price;
            return Description + ": " +
                Quantity.ToString() + " @ " +
                Price.ToString("C") + " = " +
                total.ToString("C");
        }
    }
}

To allow the program to serialize Order objects, the class must be marked with the Serializable attribute. That attributes is defined in the System.Runtime.Serialization namespace so the code includes a using statement to make using that namespace easier. To use the namespace, you also need to add a reference to System.Runtime.Serialization at design time.

The fields within the class are marked with the DataMember attribute so the serializer knows to serialize them.

The last part of the class is a ToString method that simply returns a textual representation of the object's values.

The following code shows the Customer class.

// Add a reference to System.Runtime.Serialization.
using System.Runtime.Serialization;

// Add a reference to System.ServiceModel.Web.
using System.Runtime.Serialization.Json;

using System.IO;

namespace howto_use_json
{
    [Serializable]
    public class Customer
    {
        [DataMember]
        public string Name = "";

        [DataMember]
        public string Street = "";

        [DataMember]
        public string City = "";

        [DataMember]
        public string State = "";

        [DataMember]
        public string Zip = "";

        [DataMember]
        public Dictionary<string, string> PhoneNumbers = new Dictionary<string, string>();

        [DataMember]
        public List<string> EmailAddresses = new List<string>();

        [DataMember]
        public Order[] Orders = null;

        ... (Other code shown later) ...
    }
}

This class starts much as the Order class does. It also uses the System.Runtime.Serialization namespace so it includes the corresponding using statement.

The class also uses the System.Runtime.Serialization.Json namespace (which requires you to add a reference to System.ServiceModel.Web) and the System.IO namespaces so it also includes using statements for them.

The class's definition starts by defining some simple properties such as Name, Street, and City.

The class defines the PhoneNumbers member as a Dictionary<string, string>. It defines the EmailAddresses member as List<string>. The serializer automatically converts these into the appropriate JSON types.

To make serializing and deserializing objects easier, the class includes the methods ToJson and FromJson. The ToJson method shown in the following code returns a JSON representation of an object.

// Return the JSON serialization of the object.
public string ToJson()
{
    // Make a stream to serialize into.
    using (MemoryStream stream = new System.IO.MemoryStream())
    {
        // Serialize into the stream.
        DataContractJsonSerializer serializer
            = new DataContractJsonSerializer(typeof(Customer));
        serializer.WriteObject(stream, this);
        stream.Flush();

        // Get the result as text.
        stream.Seek(0, SeekOrigin.Begin);
        using (StreamReader reader = new StreamReader(stream))
        {
            return reader.ReadToEnd();
        }
    }
}

This method creates a MemoryStream in which to write. It then creates a DataContractJsonSerializer object to serialize the Customer object. It calls the serializer's WriteObject method to write the current Customer object into the stream and flushes the stream.

Next the code rewinds the stream to the beginning, creates an associated StreamReader, and uses the reader's ReadToEnd method to get the serialization out of the stream and return it. (This seems needlessly awkward to me. If you find a simpler method, email me.

The static FromJson method shown in the following code takes a JSON serialization of a Customer object, uses it to re-create the object, and returns the new object.

// Create a new Customer from a JSON serialization.
public static Customer FromJson(string json)
{
    // Make a stream to read from.
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(json);
    writer.Flush();
    stream.Position = 0;

    // Deserialize from the stream.
    DataContractJsonSerializer serializer
        = new DataContractJsonSerializer(typeof(Customer));
    Customer cust = (Customer)serializer.ReadObject(stream);

    // Return the result.
    return cust;
}

This method creates a MemoryStream and an associated StreamWriter. It uses the writer to write the JSON serialization into the stream. It then flushes the writer and rewinds the stream.

Next the code creates a DataContractJsonSerializer object to deserialize the Customer object. It calls the serializer's ReadObject to read the object's serialization from the stream, casts the result into a Customer object, and returns the object.

The ToString method shown in the following code is the last piece of the Customer class. It simply returns a textual representation of the Customer object.

// Return a newline separated text representation.
public override string ToString()
{
    // Display the basic information.
    string result = "Customer" + Environment.NewLine;
    result += "    " + Name + Environment.NewLine;
    result += "    " + Street + Environment.NewLine;
    result += "    " + City + " " + State + " " + Zip + Environment.NewLine;

    // Display phone numbers.
    result += "    Phone Numbers:" + Environment.NewLine;
    foreach (KeyValuePair pair in PhoneNumbers)
    {
        result += "        " + pair.Key + ": " + pair.Value + Environment.NewLine;
    }

    // Display email addresses.
    result += "    Email Addresses:" + Environment.NewLine;
    foreach (string address in EmailAddresses)
    {
        result += "        " + address + Environment.NewLine;
    }

    // Display orders.
    result += "    Orders:" + Environment.NewLine;
    foreach (Order order in Orders)
    {
        result += "        " + order.ToString() + Environment.NewLine;
    }

    return result;
}

The ToString method is a bit long but reasonably straightforward.

The following shows how the main program demonstrates JSON serialization by serializing and deserializing a Customer object.

private void Form1_Load(object sender, EventArgs e)
{
    // Make an object to serialize.
    Customer cust = new Customer()
    {
        Name = "Rod Stephens",
        Street = "1337 Leet St",
        City = "Bugsville",
        State = "CA",
        Zip = "98765",
        PhoneNumbers = new Dictionary()
        {
            {"Home", "111-222-3333"},
            {"Cell", "222-333-4444"},
            {"Work", "333-444-5555"},
        },
        EmailAddresses = new List()
        {
            "[email protected]",
            "[email protected]",
            "[email protected]",
        },
    };
    cust.Orders = new Order[3];
    cust.Orders[0] = new Order()
    {
        Description = "Pencils, dozen",
        Quantity = 10,
        Price = 1.13m
    };
    cust.Orders[1] = new Order()
    {
        Description = "Notepad",
        Quantity = 3,
        Price = 0.99m
    };
    cust.Orders[2] = new Order()
    {
        Description = "Cookies",
        Quantity = 1,
        Price = 3.75m
    };

    // Display the serialization.
    string serialization = cust.ToJson();
    txtJson.Text = serialization;
    txtJson.Select(0, 0);

    // Deserialize.
    Customer new_cust = Customer.FromJson(serialization);
    txtProperties.Text = new_cust.ToString();
    txtProperties.Select(0, 0);
}

The form's Load event handler starts by initializing a Customer object complete with phone numbers, email addresses, and Order objects.

Next the code calls the Customer object's ToJson method to get its serialization and display it in the txtJson TextBox. (Notice that the JSON serialization in the picture doesn't include nice line breaks and formatting to align the data nicely. It sacrifices that to save space.)

The code then calls the Customer class's FromJson method to deserialize the serialization and create a new Customer object. It finishes by using the new object's ToString method to show its properties in the txtProperties TextBox.

This example may seem complicated but it's actually not too bad. It's a bit longer than it might otherwise be because the Customer and Order classes include several useful data types such as a list, dictionary, and array of objects. The actual JSON code in the ToJson and FromJson methods is somewhat confusing but it's fairly short. Those methods also make using JSON to serialize and deserialize objects easy in practice.

   

Download and display a text file whenever a program starts in C#

When this example program starts, it downloads a file from the internet and displays it. You could do something similar to display a message of the day or announcements for your program.

The program uses the following Load event handler to start the process.


// Download and display the text file.
private void Form1_Load(object sender, EventArgs e)
{
    const string url = "https://raw.github.com/cubiclesoft/email_sms_mms_gateways/master/sms_mms_gateways.txt";
    txtFile.Text = GetTextFile(url);
    txtFile.Select(0, 0);
}

The code defines the URL of the file to download. It then calls the GetTextFile method described next to download the file and displays the result in the txtFile TextBox.

The following code shows the GetTextFile method.

using System.IO;
using System.Net;
...
// Get the text file at a given URL.
private string GetTextFile(string url)
{
    try
    {
        url = url.Trim();
        if (!url.ToLower().StartsWith("http")) url = "http://" + url;
        WebClient web_client = new WebClient();
        MemoryStream image_stream = new MemoryStream(web_client.DownloadData(url));
        StreamReader reader = new StreamReader(image_stream);
        string result = reader.ReadToEnd();
        reader.Close();
        return result;
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error downloading file " +
            url + '\n' + ex.Message,
            "Download Error",
            MessageBoxButtons.OK,
            MessageBoxIcon.Error);
    }
    return "";
}

The code first uses the string class's Trim method to remove leading and trailing whitespace from the URL (just in case). Then if the URL doesn't begin with "http," the code adds "http://" to the beginning of the URL. (The code only checks for "http" so it can also handle "https" as in the URL used by this example.)

Next the code creates a WebClient object. It uses the client's DownloadData method to get the file. DownloadData returns an array of bytes so the program creates a memory stream associated with the array and then makes a StreamReader to read the memory stream. The code calls the reader's ReadToEnd method and closes the reader. Finally it returns the string read by the StreamReader.

There are several ways this method could fail (file doesn't exist, improper permissions, not connected to the network, network failure, and so on) so the code does all its work within a try-catch block.

With a it more work, you can download other files such as pictures or RTF format files to display more than just text. If you really want a fancy announcement page downloaded from the internet, you might be better off placing a WebBrowser control on the form and making it navigate to a web page.

If you examine the file that this example downloads, you'll see that it contains JSON-formatted data listing SMS gateway email addresses. My next blog post provides a brief introduction to JSON. The post after that explains how to read the data in the text file that this example downloads and displays.

   

Calendar

May 2013
SuMoTuWeThFrSa
1234
567891011
12131415161718
19202122232425
262728293031

Subscribe


Blog Software
Blog Software