Quick tip: Use pinvoke.net to get information about API functions for use in C#

[API functions]

Platform Invocation Services (pinvoke) lets allows managed .NET code call unmanaged API functions that are implemented in a DLL. The functions in a DLL are exposed via an Application Programming Interface (API).

The enormous .NET runtime library wraps many API functions in managed code so you don’t need to use pinvoke to call them for many everyday tasks. Occasionally, however, you’ll stumble across some API function that you want to use and that is not available via managed code.

When that happens, first check again to make sure the function isn’t available through managed code. Often you won’t find the managed version of an API call until you dig a bit.

If you’re pretty sure the function is only available via an API, go to pinvoke.net. This site contains the C# and Visual Basic declarations for thousands of API function calls and the constants and structures you need to use them. It includes examples that can save you a lot of time and trouble trying to figure out how to use the functions on your own.

Use the search box in the upper left corner to find the API function you need to use. On the results page, click the link to the correct entry (often a search finds multiple possible matches) to get more detailed information.


Follow me on Twitter   RSS feed   Donate




Posted in API | Tagged , , , , , , , , , , | Leave a comment

Find Visual Studio’s Immediate window


[Immediate window]

For some reason, the Immediate window seems to be a never-ending subject for Microsoft treasure hunts. It seems like each new release of Visual Studio moves the Immediate window to a new location buried deep within the menu hierarchy. In some versions, it isn’t even present in the menus, so you need to resort to trickery to make it reappear.

There are a couple ways you can retrieve the Immediate window. The most straightforward is to scour the menu hierarchy looking for it. Unfortunately, the menus that you need are sometimes hidden. For example, you can find the Immediate window in Visual Studio 2017 by using Debug > Windows > Immediate. Unfortunately the Debug > Windows menu only contains the Immediate command if you have a project loaded. (It’s also annoying that the Immediate window cannot evaluate simple expressions such as 1 + 1 unless a project is loaded, but that’s a whole different rant.)

This almost makes sense because you can’t debug a project if you don’t have a project loaded. That doesn’t mean you can’t display the Immediate window, however. It just makes it harder to find.

This also violates a key principle of discoverable user interface design: don’t hide unavailable commands, disable them. That way the user can find the command and knows that it is unavailable. If you hide the command, users may waste a lot of time trying to find a command that they saw before but can’t find now.

Anyway, if you can’t find the Immediate window anywhere, you can try the following.

  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. More on that in a moment.

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 use the command in the toolbar to open the Immediate window when you want it.

There’s one more oddity that I want to talk about in this post. The first times I launched Visual Studio 2017, it told me that the Immediate window was taking 21 seconds to load and asked if I wanted to disable it at startup. I said yes and, as I expected, the next time I started Visual Studio 2017 it took just as long. The cause of the missing 21 seconds has nothing to do with the Immediate window. That’s just the window that Visual Studio opened just before it started doing something else that takes a long time. Lesson learned.

Unfortunately, now the Immediate window didn’t appear at startup and I couldn’t find an easy way to bring it back. I use that window a lot (does Microsoft ever use Visual Studio?), so that is a problem. (This is another classic Visual Studio user interface problem. It makes it easy for you to perform an action that is hard to undo.)

After much digging, I figured out how to restore the window. Select Help > Manage Visual Studio Performance to see the following dialog.


[Immediate window]

Select the Tool Windows branch, click on Immediate Window, and select Use Default Behavior. Now the window appears when you start Visual Studio.

Follow me on Twitter   RSS feed   Donate




Posted in IDE, Windows Forms programming | Tagged , , , , , , , | Leave a comment

Map device coordinates to world coordinates in C#

[coordinates]

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 Map points between coordinate systems 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 blue ellipse is about 1 unit wide and 3 units 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 control’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 control’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 shows how the program stores information about the ellipses.

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

// 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 to find the inverse points for mouse coordinates.

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

When the form resizes, the following code executes.

// Create new transformations to center the drawing.
private void Form1_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 device (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 one-pixel-wide lines no matter what transformations are in effect. (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 because the mouse is down, 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 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 into 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 use the DeviceToWorld method to convert mouse coordinates into device coordinates. For example, the following code shows the PictureBox control’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.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in drawing, graphics, transformations | Tagged , , , , , , , , , , | Leave a comment

How to send an SMS text message in C#

[SMS]

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 first time I saw this sort of thing, the program monitored a temperature sensor in a computer lab. If the temperature exceeded a certain safe value, the system sent a message to the operator.)

The example Extract part of 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 Send email 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 remaining 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:

2345678901@sms.airfiremobile.com

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.

Probably the hardest part is figuring out what email hot and port to use. Those depend on your email system. The ones shown in the picture work for Hotmail/Live. The following links give information about Hotmail and Gmail.

You may need to do some research for other mail services.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in miscellany, system | Tagged , , , , , , , , , , , , , , , , , | Leave a comment

Calculate credit payments in WPF and C#

[example]

This is a WPF version of the example Calculate credit payments in C#. The calculations are the same. Unfortunately building and using the user interface is much harder in WPF, particularly working with the ListView control. (The unofficial WPF slogan: Twice as flexible and only five times as hard!)

The program’s XAML code uses grid rows and columns together with labels, text boxes, buttons, and other controls. Building most of the interface is relatively slow but straightforward. Download the example to see how most of the controls are created.

This post focuses on the ListView control. The program uses the following XAML code to define the control.

<ListView Grid.Row="0" Grid.Column="2" Grid.RowSpan="6"
    Name="lvwPayments">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment"
            Value="Stretch" />
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Period" Width="50"
                DisplayMemberBinding="{Binding Path=Period}"/>

            <GridViewColumn Header="Payment" Width="75">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Payment}"
                            TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn Header="Interest" Width="75">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Interest}"
                            TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn Header="Balance" Width="75">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Balance}"
                            TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

There are several important points in this code.

In basic structure, the ListView contains a GridView control that provides the rows and columns. The GridView contains GridViewColumn objects to define its columns. More on thos shortly.

By default, the item containers inside the GridView that display the data do not stretch their contents to fill the available area. I wanted to right-align the currency columns. Because the containers don’t stretch their contents, no matter what I did, the contents of each column were placed at the left of the column. To allow the contents to be right aligned (although this isn’t the only step), I gave the ListView an ItemContainerStyle that makes the containers stretch their contents. (Note that you could set this property to Right instead of Stretch to align all of the columns.)

The ListView.View property indicates how items are displayed by the control. This example uses a Grid control. That control contains a collection of GridViewColumn objects that define the data’s columns.

The first GridViewColumn displays the payment period. Its DisplayBindingMember property indicates what piece of data from the control’s data binding this column should display. This column displays the Period property of the data object. You’ll see how that value is defined shortly.

The second GridViewColumn displays the payment amount. It uses a CellTemplate to define how its data should be displayed.

Important: The GridViewColumn must not have a DisplayBindingMember value set or it sill ignore its CellTemplate! I (and many others) have wasted a lot of time trying to figure out why a CellTemplate didn’t work because its GridViewColumn also included a DisplayBindingMember.

The payment column’s CellTemplate tells the control to display its data in a TextBlock. That control should set its Text property to the data binding value Payment and it should align its text on the right. (This is the magic to aligning values in a column.)

The rest of the ListView control’s columns are defined similarly.

The remaining interesting part of this example is how the code manages the ListView control. The program uses the following PaymentData class to hold data about a credit card payment.

public class PaymentData
{
    public string Period { get; set; }
    public string Payment { get; set; }
    public string Interest { get; set; }
    public string Balance { get; set; }
}

This class simply holds payment information for a single payment. It stores the values as text so I can easily use blank and formatted values.

The bindings inside the ListView and its contained objects use the class’s property names to determine what values to display. For example, the second column’s CellTemplate object’s DataTemplate uses {Binding Payment} to find its data. That value means it should look in the bound object (which is a PaymentData object in this example) for its Payment property and then display that.

The following code shows how the program calculates and displays the payments. The code that deals with the ListView is highlighted in blue.

private void btnGo_Click(object sender, RoutedEventArgs e)
{
    // Get the parameters.
    decimal balance =
        decimal.Parse(txtInitialBalance.Text, NumberStyles.Any);
    decimal interest_rate =
        decimal.Parse(txtInterestRate.Text.Replace("%", "")) / 100;
    decimal payment_percent =
        decimal.Parse(txtPaymentPercent.Text.Replace("%", "")) / 100;
    decimal min_payment =
        decimal.Parse(txtMinPayment.Text, NumberStyles.Any);
    interest_rate /= 12;

    lblTotalPayments.Content = null;
    decimal total_payments = 0;

    // Display the initial balance.
    lvwPayments.Items.Clear();
    PaymentData data = new PaymentData()
    {
        Period = "0",
        Payment = null,
        Interest = null,
        Balance = balance.ToString("c"),
    };
    lvwPayments.Items.Add(data);

    // Loop until balance == 0.
    for (int i = 1; balance > 0; i++)
    {
        // Calculate the payment.
        decimal payment = balance * payment_percent;
        if (payment < min_payment) payment = min_payment;

        // Calculate interest.
        decimal interest = balance * interest_rate;
        balance += interest;

        // See if we can pay off the balance.
        if (payment > balance) payment = balance;
        total_payments += payment;
        balance -= payment;

        // Display results.
        data = new PaymentData()
        {
            Period = i.ToString(),
            Payment = payment.ToString("c"),
            Interest = interest.ToString("c"),
            Balance = balance.ToString("c"),
        };
        lvwPayments.Items.Add(data);
    }

    // Display the total payments.
    lblTotalPayments.Content = total_payments.ToString("c");
}

This code calculates the payments as in the previous example. Look at it to see how that works.

To add a row to the ListView control, the program creates a PaymentData object containing the row’s data values. It then simply adds that object to the ListView control’s list of items. The grid’s columns dig through the object to find the pieces of data that they should display. if a column has a CellTemplate, that object figures out how to display the data.

Download the example for more details.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, calculations, finance, mathematics | Tagged , , , , , , , , , , , , , | Leave a comment

Extract part of a JSON file in C#

[JSON]

This example shows how to read a JSON file and pick out only the pieces that are interesting.

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 that lists 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 in JSON format. 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 at the file for quite a while, 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 that 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.

This example uses classes that are defined in the System.Web.Script.Serialization namespace. To use that namespace, you need to add a reference to the System.Web.Extensions.dll library. (I don’t know why Microsoft can’t make namespaces and library names match.) Unfortunately, that library may not be visible in your Add References dialog.

If you are using a more recent version of Visual Studio, open Project > Properties > Application and make sure that you are targeting the .NET 4 Framework. Then use Project > Add Reference to open the Add Reference dialog, and look on the .NET tab for the library.

If you still can’t find it, or if you are using an older version of Visual Studio (like I am), open the Add Reference dialog, click on the Browse tab, and browse for the library. Depending on which versions of Visual Studio you have installed, you may find it in one of the following places.

  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Extensions.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Web.Extensions.dll

After you add the reference, you can use the System.Web.Script.Serialization namespace.

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 serialize and deserialize specific object types, 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. That 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.

The program then 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 a country selected.

The program’s remaining code updates its ComboBox control’s when you make a selection. When you select a country, the carriers ComboBox displays a list of the carriers available in that country. When you select 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 you 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 you have selected a country, the program converts the selected country into its corresponding CountryInfo object. It then sets the cboCarrier ComboBox control’s DataSource property to the country’s Carriers property. That property is a list of CarrierInfo objects, so the ComboBox displays them.

When you pick 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 control’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 you select 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 in this series explain how to do that.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, files, serialization | Tagged , , , , , , , , , , , , , | 1 Comment

Write a TreeView into a tab-delimited file in C#

[example]

The example Load a TreeView from a tab-delimited file in C# shows how to load a TreeView control from a file. This example shows how to do the converse: write a TreeView control’s values into a file.



The following SaveTreeViewIntoFile method starts the process.

// Write the TreeView's values into a file that uses tabs
// to show indentation.
private void SaveTreeViewIntoFile(string file_name, TreeView trv)
{
    // Build a string containing the TreeView's contents.
    StringBuilder sb = new StringBuilder();
    foreach (TreeNode node in trv.Nodes)
        WriteNodeIntoString(0, node, sb);

    // Write the result into the file.
    File.WriteAllText(file_name, sb.ToString());
}

This method creates a StringBuilder and uses it to build the text that the file will contain. It then loops through the TreeView control’s top-level nodes and calls WriteNodeIntoString (described next) for each.

After it finishes looping through the top-level TreeView nodes, the code writes the StringBuilder object’s contents into the file.

The following code shows the WriteNodeIntoString method.

// Write this node's subtree into the StringBuilder.
private void WriteNodeIntoString(int level, TreeNode node,
    StringBuilder sb)
{
    // Append the correct number of tabs and the node's text.
    sb.AppendLine(new string('\t', level) + node.Text);

    // Recursively add children with one greater level of tabs.
    foreach (TreeNode child in node.Nodes)
        WriteNodeIntoString(level + 1, child, sb);
}

This method recursively writes a node and its subtree into a StringBuidler. The method’s first parameter is the number of tabs that should come before this node’s data. The method starts by appending that number of tabs and the node’s text onto the StringBuilder.

The method then loops through the node’s child nodes. For each child, it calls this method recursively, adding 1 to the number of tabs.

Download the example and the previous example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, controls, files, recursion | Tagged , , , , , , , , , , , | Leave a comment

Use JSON to serialize and deserialize objects in C#

[JSON]

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":
  [
    "somewhere@someplace.com",
    "nowhere@noplace.com",
    "root@everywhere.org"
  ],
  "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 for this example 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 attribute is defined in the System.Runtime.Serialization namespace, so the code includes a using directive 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 directive.

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 directives for them.

The class 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, please post it in the comments below.)

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 code 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<string, string>()
        {
            {"Home", "111-222-3333"},
            {"Cell", "222-333-4444"},
            {"Work", "333-444-5555"},
        },
        EmailAddresses = new List<string>()
        {
            "somewhere@someplace.com",
            "nowhere@noplace.com",
            "root@everywhere.org",
        },
    };
    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 Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, serialization | Tagged , , , , , , , , , , , , | 1 Comment

Display tooltips on a graph of an equation entered by the user in C#


[tooltip]

This example draws a graph of an equation entered by the user and the displays a tooltip on it when you move the mouse over the graph. See the post Graph equations entered by the user in C# for information about how the program draws the graph.

The idea isn’t too difficult. When the mouse moves over the graph, see what point it is over in world coordinates. If that point is close to the curve, display a tooltip. The details are a bit trickier. To make the new example, I made the following changes.

First, I added a ToolTip control named tipPoint to the form. That control provides the ability to display tooltips for other controls.

The original program uses a transformation and its inverse to map points between world coordinates and the device coordinates used by the PictureBox. This example needs to use those transformations later so it can convert the mouse’s location into a point on the graph. To do that, I modified the code so it saves the transformation and its inverse in class-level variables. The following code shows how they are declared.

// The transformation and inverse.
private Matrix Transform = null, Inverse = null;

Later, when the program draws the graph, the following lines save those matrices.

Transform = gr.Transform;
Inverse = gr.Transform;
Inverse.Invert();

Similarly the tooltip code will need to be able to call the compiled function entered by the user. The following code shows how the program declares the function’s MethodInfo object at the class level.

// The compiled function.
private MethodInfo Function = null;

When it draws the graph, the program uses the following statement to save the compiled function’s information in this variable.

Function = CompileFunction(txtEquation.Text);

Now the program can use the function, the world-to-device transformation, and the inverse transformation.

When the mouse moves over the graph PictureBox, the following event handler executes.

// If the mouse is over the curve,
// display a tooltip showing the curve's value.
private void picGraph_MouseMove(object sender, MouseEventArgs e)
{
    // Get the tooltip that we should display.
    string tooltip = GetGraphToolTip(e.Location);

    // See if the tooltip has changed.
    string old_tooltip = tipPoint.GetToolTip(picGraph);
    if (old_tooltip == tooltip) return;

    // Display the new tooltip.
    tipPoint.SetToolTip(picGraph, tooltip);
}

This code calls the GetGraphToolTip method described shortly to get the tooltip that it should display. It then uses the ToolTip object’s GetToolTip method to get the PictureBox control’s current tooltip and compares them. If the tooltip hasn’t changed, the method exits.

If the tooltip has changed, the code uses the ToolTip control’s SetToolTip method to give the PictureBox the new tooltip.

The following code shows the GetGraphToolTip method.

// Get the tooltip for the point in device coordinates.
// Return null if the point isn't on the curve.
private string GetGraphToolTip(Point point)
{
    if (Function == null) return null;

    // Convert the mouse's location into world coordinates.
    PointF[] world_points = { point };
    Inverse.TransformPoints(world_points);

    // Find the Y coordinate in device coordinates.
    float x = world_points[0].X;
    float y = F(Function, x);
    PointF[] device_points = { new PointF(x, y) };
    Transform.TransformPoints(device_points);

    // See if the mouse's position is within
    // five pixels of this point's location.
    if (Math.Abs(point.Y - device_points[0].Y) > 10) return null;

    // Compose the tooltip.
    return "(" + x.ToString("0.00") +
        ", " + y.ToString("0.00") + ")";
}

If Function is null, then the user has not yet entered and compiled a function so the method exits.

The method then makes an array containing the mouse’s position. It uses the Inverse transformation matrix to map the device coordinates on the PictureBox into world coordinates.

Next, the code passes the world coordinate’s X value into the function to get the corresponding Y value. This gives a point on the curve at the mouse’s X coordinate.

The method then uses the Transform transformation to map the point on the curve back to device coordinates. Now the method has the mouse’s original location and the location of a point on the curve in device coordinates. It compares the Y coordinates of the points to see if they are within 10 pixels of each other. If the points are within 10 pixels, the method returns a tooltip giving the world coordinates of the point on the curve.

See the previous example for more details.

There are other approaches that you could take to this problem. For example, you could save all of the points generated on the curve and then compare the mouse’s position to them (in world coordinates) to see which was closest. That would probably provide a better result, particularly in places where the graph is mostly vertical. That would have been a bigger change from the previous example, however, so I used this version.

You could also convert the curve’s points into device coordinates before drawing and then compare the mouse position to the converted points. See the example Draw a labeled line graph that displays value tooltips in C# for information on that approach.

Another approach would be to track the mouse’s X coordinate and display the corresponding Y coordinate.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, drawing, graphics, mathematics, reflection | Tagged , , , , , , , , , , , | Leave a comment

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

[download]

When this example 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 (if the file doesn’t exist, it has improper permissions, the program fails to connect to the network, there’s a network failure, and so forth), so the code does all of its work within a try-catch block.

With a bit more work, you could 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. Then you wouldn’t even need to download the file explicitly. The WebBrowser would simply display the latest version of the 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 post will provide a brief introduction to JSON. The post after that will explain how to read the data in the text file that this example downloads and displays.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in files, internet, web | Tagged , , , , , , , , , | Leave a comment