Draw “marching ants” dashed lines in C#

[dashed lines]

The example Draw dashed lines that are visible on any background in C# shows how to draw lines that are visible above any backgrounds. If the background is very cluttered, however, it may still be somewhat hard to see the lines. This example shows another approach. It draws two-colored dashed lines where the dashes move like a line of marching ants.

The basic idea is simple. The program uses a Pen that has a dash pattern. When a Timer fires, the program sets the Pen object’s DashOffset to a new value and redraws the dashed lines.

The DashOffset value gives the distance from the beginning of the line to the start of the first dash. All of the later dashes follow the first one so they are also offset. The DashOffset value increases over time making it look like the dashes are moving.

The following code shows the variables that the program uses to manage its rectangles.

// Previously selected rectangles.
private List<Rectangle> Rectangles = new List<Rectangle>();

// The rectangle we are selecting.
private Rectangle NewRectangle;

// Variables used to select a new rectangle.
private int StartX, StartY, EndX, EndY;
private bool SelectingRectangle = false;

The Rectangles list holds Rectangle objects that have already been drawn. The program uses the other variables to let the user select a new Rectangle by clicking and dragging.

The following code shows how the program lets the user select a new rectangle.

// Start selecting a rectangle.
private void picCanvas_MouseDown(object sender, MouseEventArgs e)
{
    // Save the current point.
    StartX = e.X;
    StartY = e.Y;
    EndX = e.X;
    EndY = e.Y;

    // Make a new selection rectangle.
    NewRectangle = new Rectangle(
        Math.Min(StartX, EndX),
        Math.Min(StartY, EndY),
        Math.Abs(StartX - EndX),
        Math.Abs(StartY - EndY));

    // Start marching.
    SelectingRectangle = true;
    tmrMarch.Enabled = true;
}

// Continue selecting a rectangle.
private void picCanvas_MouseMove(object sender, MouseEventArgs e)
{
    if (!SelectingRectangle) return;

    // Save the current point.
    EndX = e.X;
    EndY = e.Y;

    // Make a new selection rectangle.
    NewRectangle = new Rectangle(
        Math.Min(StartX, EndX),
        Math.Min(StartY, EndY),
        Math.Abs(StartX - EndX),
        Math.Abs(StartY - EndY));
    
    // Redraw.
    Refresh();
}

// Finish selecting a rectangle.
private void picCanvas_MouseUp(object sender, MouseEventArgs e)
{
    if (!SelectingRectangle) return;
    SelectingRectangle = false;
    tmrMarch.Enabled = false;
    if ((StartX == EndX) || (StartY == EndY)) return;

    // Save the newly selected rectangle.
    Rectangles.Add(new Rectangle(
        Math.Min(StartX, EndX),
        Math.Min(StartY, EndY),
        Math.Abs(StartX - EndX),
        Math.Abs(StartY - EndY)));

    // Redraw.
    Refresh();
}

When the user presses the mouse down, the MouseDown event handler records the mouse’s position, sets SelectingRectangle to true, and enables the Timer named tmrMarch.

When the mouse moves, the MouseMove event handler records the mouse’s new position, updates the NewRectangle variable to represent the newly selected rectangle, and refreshes the form to make the program execute the Paint event handler described shortly.

When the user releases the mouse, the MouseUp event handler adds the newly selected rectangle to the Rectangles list and refresh the form.

The following code shows the tmrMarch Timer component’s Tick event handler.

// Redraw.
private void tmrMarch_Tick(object sender, EventArgs e)
{
    Refresh();
}

This event handler simply refreshes the form to make it execute the following Paint event handler.

// Parameters for drawing the dashed rectangle.
private int Offset = 0;
private int OffsetDelta = 2;
private float[] DashPattern = { 5, 5 };

// Draw the rectangles.
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
    Offset += OffsetDelta;

    // Draw previously selected rectangles.
    for (int i = 0; i < Rectangles.Count; i++)
    {
        e.Graphics.FillRectangle(
            RectangleBrushes[i % RectangleBrushes.Length],
            Rectangles[i]);
        e.Graphics.DrawRectangle(Pens.Black, Rectangles[i]);
    }

    // Draw the new rectangle.
    if (SelectingRectangle)
    {
        e.Graphics.DrawRectangle(NewRectangle, Color.Yellow,
            Color.Red, 2f, Offset, DashPattern);
    }
}

This is where the most interesting code is. The variable Offset holds the dashed line’s current DashOffset value. The value OffsetDelta gives the amount by which Offset is adjusted every time the Paint event handler executes. You can change OffsetDelta and the Timer component’s Interval property (I have it set to 250 milliseconds or 1/4 second) to change the speed of the ants.

The DashPattern array determines the dash pattern used by the dashed lines. In this example, the pattern draws 5 units, skips 5 units, and then repeats.

The Paint event handler first increaes Offset by the value OffsetDelta. It then draws the previously selected rectangles. It uses the brushes in the RectangleBrushes array to give the rectangles colors.

If the user is currently selecting a rectangle, the program then calls the following DrawRectangle extension method to draw a rectangle with two-color dashed lines. (As you can probably guess, I made the extension method to make drawing rectangles with two-color dashed lines a bit easier.)

// Draw a two-color dashed rectangle.
public static void DrawRectangle(this Graphics gr,
    Rectangle rect, Color color1, Color color2,
    float thickness, float offset, float[] dash_pattern)
{
    using (Pen pen = new Pen(color1, thickness))
    {
        gr.DrawRectangle(pen, rect);

        pen.DashPattern = dash_pattern;
        pen.DashOffset = offset;
        pen.Color = color2;
        gr.DrawRectangle(pen, rect);
    }
}

This method begins by drawing the rectangle in its first color parameter. Next it creates a Pen, gives if the DashPattern defined by its dash_pattern parameter, and sets its DashOffset. It then draws the new rectangle over the old one, this time using the dashed pen.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in animation, drawing, graphics | Tagged , , , , , , , , , , , , , | 4 Comments

Draw dashed lines that are visible on any background in C#

[dashed lines]

This example shows how to draw dashed lines that are visible on any background. Sometimes it’s hard to draw lines that are visible on top of a photo or other complicated background. For example, when the user is clicking and dragging to select an area in a photograph, you need the user to be able to see the area selected. If the background image is complicated, then you may be unable to find a color to draw the lines so they will be easily visible.

Some older drawing systems could draw in XOR (exclusive-or) mode so a pixel’s color was the reverse of the color behind it, making it visible. That doesn’t work in .NET, at least not easily.

Another approach is to draw dashed lines consisting of two colors. The bad news is that dashed lines can only have a single color in .NET. The good news is that it’s easy enough to fake it.

To draw two-color dashed lines, first draw the lines using a solid pen. Then draw them again using a dashed pen with a different color. This example uses the following code to draw two-color dashed lines.

// Fill four rectangles and draw a two-color dashed rectangle.
private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics gr = e.Graphics;

    // Fill the rectangles.
    int wid = ClientSize.Width / 2;
    int hgr = ClientSize.Height / 2;
    gr.FillRectangle(Brushes.Green, 0, 0, wid, hgr);
    gr.FillRectangle(Brushes.Orange, 0, hgr, wid, hgr);
    gr.FillRectangle(Brushes.Yellow, wid, 0, wid, hgr);
    gr.FillRectangle(Brushes.Blue, wid, hgr, wid, hgr);

    // Draw the dashed rectangle.
    Rectangle rect = new Rectangle(
        20, 20,
        ClientSize.Width - 40, ClientSize.Height - 40);
    using (Pen pen1 = new Pen(Color.Black, 2))
    {
        gr.DrawRectangle(pen1, rect);
    }
    using (Pen pen2 = new Pen(Color.White, 2))
    {
        pen2.DashPattern = new float[] { 5, 5 };
        gr.DrawRectangle(pen2, rect);
    }
}

The code first makes variable gr refer to the event handler’s Graphics object to make the following code a bit easier to read.

Next the code fills the form with four rectangles. It then draws a black rectangle with line thickness 2 and then draws over the rectangle with a dashed white pen. The result is a dashed black and white box that’s easy to see over all of the colored rectangles.

In this example a black box would have worked reasonably well (although it would be somewhat hard to see over the green and blue rectangles) and a white box would also have worked reasonably well (although it would be hard to see over the yellow rectangle). Neither black nor white would have worked if one of the rectangles was black and another was white.

This method can produce some annoying results depending on the colors involved. For example, blue and red dashes over blue and red rectangles can give you headache. The method usually works well if the box’s dash colors don’t interact too strongly with each other (like blue and red) and they contrast with the background. For many photographs, black and white work pretty well.

The example also makes the rectangle a bit easier to see by using lines with thickness 2.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in drawing, graphics | Tagged , , , , , , , , , , , | 1 Comment

Produce a repeatable series of “random” numbers in C#

[random numbers]

The Random class can generate a series of pseudo-random numbers. They’re called “pseudo-random” rather than “random” because the numbers are generated by a mathematical algorithm rather than by some completely unpredictable process such as observing a source of radioactive decay or the static in radio waves. Given enough information about the algorithm used by the class and a Random object’s current state, you could predict the next number (in fact all following numbers) in a sequence.

However, the Random class does produce numbers that look random enough for most applications. Currently the class uses Donald Knuth’s subtractive random number generator algorithm described in his book The Art of Computer Programming, Volume 2: Seminumerical Algorithms, Addison-Wesley, 1981. Look it up for more details.

The obvious time when the Random class isn’t “random enough” is in cryptography of important information. If you need to encrypt financial information, passwords, or other really sensitive information, use the cryptographic library instead of Random.

Generating pseudo-random numbers is often useful in programs, but sometimes it’s useful to be able to generate the same pseudo-random set of numbers repeatedly. For example, suppose you want a demonstration program to display a graph of some pseudo-random data. If might look weird if it displayed a different graph every time you ran the program.

For another example, suppose you have a program that uses randomization techniques to look for a good solution of a hard problem such as the Traveling Salesperson Problem (TSP). Now suppose there’s a bug in the program. It can be very hard to find this kind of bug because every time you run the program you get a different series of pseudo-random numbers so the bug may occur at a different place or not at all.

Fortunately it’s easy to generate a repeatable series of pseudo-random numbers. Simply pass the Random class’s constructor an integer seed value to use when initializing the pseudo-random number generating algorithm. If you omit this number, the object uses the system’s time to initialize the generator, so if you run the program again later at a different time, you get a different sequence of numbers.

This brings up an important issue: If you create two Random objects at roughly the same time, the system’s time may not have changed so you may end up with two objects initialized with the same seed value and therefore producing the same pseudo-random numbers. To avoid this, create a single Random object and then use it whenever you need to generate pseudo-random numbers.

This example uses the following code to add 100 pseudo-random numbers to a ListBox.

// Initialize the random number generator and generate some numbers.
private void GenerateNumbers(TextBox seed_textbox, ListBox lst)
{
    // Get the seed.
    int seed = int.Parse(seed_textbox.Text);

    // Initialize the random number generator.
    Random Rand = new Random(seed);

    // Generate numbers.
    lst.Items.Clear();
    for (int i = 1; i < 100; i++)
    {
        lst.Items.Add(Rand.Next(0, 10000));
    }
}

The code takes as parameters a TextBox holding a seed value and the ListBox where it should place the pseudo-random numbers. It parses the seed value and uses it to initialize a new Random object. It then clears the ListBox and uses the Random object to fill it with 100 pseudo-random numbers between 0 and 9999.

Run the program, enter seed values in the TextBox controls, and click the Generate buttons. If the two seed values are the same, the lists will show the same pseudo-random numbers. If the seed values are different, even by a tiny amount, the values will be very different.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Safely manage documents in C#

[example]

This is a pretty involved example that shows how to safely manage documents. The pieces are all simple but there are a lot of tightly integrated pieces.

This example lets you create, edit, open, and save files in text and Rich Text formats. It provides New, Open, Save, Save As, and Exit commands. The main purpose of the example is to coordinate all of those commands so the document is always safe.

For example, if you open a file, make changes, and then try to open another file, the program says there are unsaved changes asks if you want to save them. If you click Yes, the program either saves the file with its current file name or prompts you for a file name if it has not been saved before.

The example also updates the program’s title bar to display the name of the currently loaded file (if it has a name–new files have no names until they are saved) and displays an asterisk if the loaded document has unsaved changes.

The program uses two key properties to ensure that changes to the loaded document are not lost and to update the program’s title bar: FileName and DocumentChanged. The following code shows these two properties.

// The document's file name.
private string _FileName = "";
private string FileName
{
    get { return _FileName; }
    set
    {
        if (value == _FileName) return;
        _FileName = value; 
        SetFormCaption(); 
    }
}

// True if the document was changed since opening/creation.
private bool _DocumentChanged = false;
private bool DocumentChanged
{
    get { return _DocumentChanged; }
    set
    {
        if (value == _DocumentChanged) return;
        _DocumentChanged = value;
        SetFormCaption();
    }
}

These properties use backing variables _FileName and _DocumentChanged to store their values. Each property’s getter simply returns the value of the backing variable.

Each property’s setter determines whether its value is changing and exits if it is not. It then saves the new value and calls the SetFormCaption method described next to update the form’s title bar.

Note that the RichTextBox control has a Modified property that also tracks whether the document has been modified much as the DocumentChanged property does. The program uses its own property instead of the control’s Modified property so that property can call SetFormCaption.

The following code shows the SetFormCaption method.

// Set the form's caption to indicate the file
// name and whether there are unsaved changes.
private void SetFormCaption()
{
    string caption = "howto_save_as ";

    // If there are changes, add an asterisk.
    if (DocumentChanged) caption += "* ";
    else caption += " ";

    // Add the file name without its path.
    if (FileName == "") caption += "[ ]";
    else caption += "[" + Path.GetFileName(FileName) + "]";

    // Display the result.
    this.Text = caption;
}

This method simply composes the form’s caption using the document’s current file name (if it has one) and adding an asterisk if the document is modified.

That takes care of updating the form’s caption. Ensuring the document’s safety is a bit more involved but the pieces are still fairly simple. The key is the IsDocumentSafe method shown in the following code.

// Return true if it is safe to discard the current document.
private bool IsDocumentSafe()
{
    // If there are no current changes to the document, it's safe.
    if (!DocumentChanged) return true;

    // See if the user wants to save the changes.
    switch (MessageBox.Show(
        "There are unsaved changes. Do you want to save the document?",
        "Save Changes?", MessageBoxButtons.YesNoCancel))
    {
        case DialogResult.Yes:
            // Save the changes.
            SaveDocument();

            // If the document still has unsaved changes,
            // then we didn't save so the document is not safe.
            return (!DocumentChanged);

        case DialogResult.No:
            // It's safe to lose the current changes.
            return true;

        default:
            // Cancel. It's not safe to lose the changes.
            return false;
    }
}

This method returns true if it is safe to discard the current document.

First the method checks the DocumentChanged property and, if there are no unsaved changes, the method returns true. This happens if there have been no changes since the document was last loaded, saved, or created.

If there are unsaved changes, the method warns the user and asks if it should save the changes.

If the user clicks Yes, the program calls SaveDocument to try to save the changes. This might fail (you’ll see why when you look at the SaveDocument method) so IsDocumentSafe returns true only if there are no unsaved changes after the call to SaveDocument returns.

If the user clicks No, then the user doesn’t want to save the current changes so IsDocumentSafe returns true to indicate that the program can discard the changes.

If the user clicks Cancel, then the user decided not to do whatever made the program call IsDocumentSafe. This might have been creating a new document, opening an existing document, or exiting the program. (You’ll see those calls to IsDocumentSafe shortly.) In that case, IsDocumentSafe returns false to tell the calling code that it is not safe to discard the current changes.

There are two ways the user might try to directly save the current document: by using the File menu’s Save or Save As commands. The following code shows those menus’ event handlers.

// Save using the current document name.
private void mnuFileSave_Click(object sender, EventArgs e)
{
    SaveDocument();
}

// Save the document with a new name.
private void mnuFileSaveAs_Click(object sender, EventArgs e)
{
    SaveDocumentAs();
}

These event handlers simply call the SaveDocument and SaveDocumentAs methods respectively.

The following code shows the SaveDocumentAs method.

// Attempt to save the document with a new file name.
private void SaveDocumentAs()
{
    // Let the user pick the file in which to save.
    if (sfdDocument.ShowDialog() == DialogResult.OK)
    {
        // Save using the selected file name.
        SaveDocumentAs(sfdDocument.FileName);
    }
}

This method displays a SaveFileDialog to the user to let the user select the file in which to save the document. If the user picks a file and clicks Save, the code calls an overloaded version of SaveDocumentAs that takes a file name as a parameter, passing that method the file name selected by the user. The following code shows the overloaded version of SaveDocumentAs.

// Save the document with the indicated name.
private void SaveDocumentAs(string file_name)
{
    // Save with the indicated name.
    try
    {
        // Save the file.
        if (Path.GetExtension(sfdDocument.FileName).ToLower() == "txt")
        {
            // Save as a text file.
            File.WriteAllText(rchDocument.Text, rchDocument.Text);
        }
        else
        {
            // Save as an RTF file.
            rchDocument.SaveFile(file_name);
        }

        // Update the document's file name.
        FileName = file_name;

        // There are no unsaved changed now.
        DocumentChanged = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

This version of SaveDocumentAs is the only piece of code that actually saves a file. First it checks the file name’s extension. If the extension is .txt, the code uses the File class’s WriteAllText method to save the current document’s text into the indicated file. If the extension is not .txt, the code uses the RichTextBox control’s SaveFile method to save the control’s contents in Rich Text format.

If the code successfully saves the file, it updates the FileName and DocumentChanged properties.

The other method that saves files is the following SaveDocument method.

// Attempt to save the document.
// If we don't have a file name, treat this as Save As.
private void SaveDocument()
{
    // See if we have a file name.
    if (FileName == "")
    {
        // We have no file name. Treat as Save As.
        SaveDocumentAs();
    }
    else
    {
        // Save with the current name.
        SaveDocumentAs(FileName);
    }
}

This method checks the FileName property. If the program does not have a file name for the current document (because it created a new file and hasn’t saved it yet), then the program calls SaveDocumentAs to let the user select the file in which to save the document. If the program does have a file name for the document, the method calls SaveDocumentAs passing it the current file name to make the program save the document in that file.

Those are the trickiest parts of the program. The rest of the code just calls those methods when appropriate.

The following code shows how the program creates a new file when the user selects the File menu’s New command.

// Create a new document.
private void mnuFileNew_Click(object sender, EventArgs e)
{
    // Make sure it's safe to discard the current document.
    if (!IsDocumentSafe()) return;

    // Clear the document.
    rchDocument.Clear();

    // There are no changes.
    DocumentChanged = false;

    // We have no file name yet.
    FileName = "";
}

This code checks IsDocumentSafe and returns if it is not safe to discard the current changes. If it is safe to continue, the method clears the RichTextBox, sets DocumentChanged to false, and clears the file name.

The following code shows how the program opens a file when the user selects the File menu’s Open command.

// Open an existing file.
private void mnuFileOpen_Click(object sender, EventArgs e)
{
    // Make sure it's safe to discard the current document.
    if (!IsDocumentSafe()) return;

    // Let the user pick the file.
    if (ofdDocument.ShowDialog() == DialogResult.OK)
    {
        // Open the file.
        OpenFile(ofdDocument.FileName);
    }
}

Like the code used to create a new document, this code checks IsDocumentSafe to see if it is safe to proceed. If it is safe, the code displays an OpenFileDialog to let the user pick the file. If the user picks a file and clicks OK, the code calls the OpenFile method passing it the name of the selected file. The following code shows the OpenFile method.

// Open the indicated file.
private void OpenFile(string file_name)
{
    try
    {
        // Load the file.
        if (Path.GetExtension(file_name).ToLower() == ".txt")
        {
            // Read as a text file.
            rchDocument.Text = File.ReadAllText(file_name);
        }
        else
        {
            // Read as an RTF file.
            rchDocument.LoadFile(file_name);
        }

        // There are no changes.
        DocumentChanged = false;

        // Save the file's name.
        FileName = file_name;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The OpenFile method checks the file name’s extension and tries to open either a text file or a Rich Text file accordingly. If the code successfully opens the file, it updates the DocumentChanged and FileName properties.

The following code shows the event handlers that execute when the user selects the File menu’s Save, Save As, and Exit commands.

// Save using the current document name.
private void mnuFileSave_Click(object sender, EventArgs e)
{
    SaveDocument();
}

// Save the document with a new name.
private void mnuFileSaveAs_Click(object sender, EventArgs e)
{
    SaveDocumentAs();
}

// Exit. Just try to close.
private void mnuFileExit_Click(object sender, EventArgs e)
{
    Close();
}

The Save and Save As commands call the SaveDocument and SaveDocumentAs methods. The Exit menu command simply tries to close the form. When the form tries to close, the following FormClosing event handler executes.

// If it's not safe to close, cancel the close.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    e.Cancel = !IsDocumentSafe();
}

The FormClosing event handler just calls IsDocumentSafe and, if it is not safe to discard the current document, sets e.Cancel to true to make the form stay open.

The last two pieces of code are the RichTextBox control’s TextChanged event handler and the form’s Load event handler.

// Flag the document as changed.
private void rchDocument_TextChanged(object sender, EventArgs e)
{
    DocumentChanged = true;
}

// Initially we have a new unchanged document.
private void Form1_Load(object sender, EventArgs e)
{
    mnuFileNew_Click(null, null);
}

The TextChanged event handler sets DocumentChanged to true. The form’s Load event handler calls the mnuFileNew_Click event handler to start a new document just as if the user had clicked the File menu’s New command.

That’s all there is to it! Simple isn’t it? 😉 There are a lot of pieces because there are a lot of possible sequences of events, but individually each of the pieces is relatively straightforward.

A natural addition to this program would be to add toolbar buttons to let the user create, open, and save documents. You could also add an MRU (Most Recently Used) list.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in files, forms, user interface | Tagged , , , , , , , , , , , , , , , , | 1 Comment

Get font size in pixels in C#

[font size]

One oddity of the Font class is that its Size property returns font size in the units that were used to create the font. The Font class’s Unit property tells you the units used to create the font, but if you need the font’s size in a particular units (such as pixels) you need to handle several possible cases when converting the font’s size.

The example Draw smooth text in a GraphicsPath in C# uses the following code to avoid having to convert the font’s size.

// Use a big font.
private void Form1_Load(object sender, EventArgs e)
{
    this.AutoScaleMode = AutoScaleMode.None;
    this.Font = new Font("Times New Roman", 30,
        FontStyle.Bold, GraphicsUnit.Pixel);
}

This code uses pixels to define the form’s font. Later the program uses the following code to add a string to a GraphicsPath object.

path.AddString("DrawPath Normal", this.Font.FontFamily,
    (int)this.Font.Style, this.Font.Size, new Point(10, y),
    string_format);

The AddString method defines the font’s size in pixels. Because the form’s Load event handler defined the font’s size in pixels, this code can use this.Font.Size for the sized passed to the AddString method.

This example uses a different technique. Instead of creating the font using the unit pixels, it uses the Font object’s SizeInPoints property to get the font’s size in points and then converts those to pixels.

// Draw text samples.
private void Form1_Paint(object sender, PaintEventArgs e)
{
    const string txt = "Sample Text";

    int y = 10;
    e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
    e.Graphics.DrawString(txt, this.Font, Brushes.Green, 10, y);
    y += this.Font.Height;

    using (StringFormat string_format = new StringFormat())
    {
        string_format.Alignment = StringAlignment.Near;
        string_format.LineAlignment = StringAlignment.Near;

        // Get the form's font size in pixels no matter
        // what unit was used to create the font.
        float size_in_pixels =
            this.Font.SizeInPoints / 72 * e.Graphics.DpiX;

        using (GraphicsPath path = new GraphicsPath())
        {
            path.AddString(txt, this.Font.FontFamily,
                (int)this.Font.Style, size_in_pixels, new Point(10, y),
                string_format);
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.FillPath(Brushes.Green, path);
        }
    }
}

The key line of code above is highlighted in blue. By definition a printer’s point is 1/72nd of an inch. The code gets the font’s size in points and divides by 72 to get inches. It then multiplies by the screen’s resolution in dots (pixels) per inch to get the size in pixels. It uses that size to add the string to the GraphicsPath object.

The results given by the Graphics object’s DrawString method and the GraphicsPath object are almost exactly alike. In this example the DrawString method uses the TextRenderingHint value AntiAlias. If you use AntiAliasGridFit, you get slightly better kerning (inter-character spacing) so that gives a nicer result, but then a string’s width doesn’t match the result given by the GraphicsPath object as well.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Draw smooth text in a GraphicsPath in C#

[GraphicsPath]

You can set a Graphics object’s TextRenderingHint property to AntiAliasGridFit to draw smooth text. However, if you place text in a GraphicsPath object and draw the GraphicsPath, then the TextRenderingHint property doesn’t give you smooth text. Instead you need to set the Graphics object’s SmoothingMode property when drawing the GraphicsPath. This example demonstrates these properties.

When the form loads, the following code gives the form a big font.

using System.Drawing.Text;
using System.Drawing.Drawing2D;
...
// Use a big font.
private void Form1_Load(object sender, EventArgs e)
{
    this.AutoScaleMode = AutoScaleMode.None;
    this.Font = new Font("Times New Roman", 30,
        FontStyle.Bold, GraphicsUnit.Pixel);
}

This code sets the form’s AutoScale property to None so the form doesn’t resize itself when the code changes the font. It then sets the font to be 30 pixel Times New Roman bold. (I measure the font in pixels here so the font size matches the size used by the GraphicsPath described shortly.)

The following Paint event handler draws the sample text.

// Draw text samples.
private void Form1_Paint(object sender, PaintEventArgs e)
{
    int y = 10;
    e.Graphics.DrawString("DrawString Normal", this.Font,
        Brushes.Blue, 10, y);
    y += this.Font.Height;

    e.Graphics.TextRenderingHint =
        TextRenderingHint.AntiAliasGridFit;
    e.Graphics.DrawString("DrawString Smooth", this.Font,
        Brushes.Blue, 10, y);
    y += this.Font.Height;

    using (StringFormat string_format = new StringFormat())
    {
        string_format.Alignment = StringAlignment.Near;
        string_format.LineAlignment = StringAlignment.Near;

        using (GraphicsPath path = new GraphicsPath())
        {
            path.AddString("DrawPath Normal", this.Font.FontFamily,
                (int)this.Font.Style, this.Font.Size, new Point(10, y),
                string_format);
            e.Graphics.FillPath(Brushes.Green, path);
            y += this.Font.Height;
        }

        using (GraphicsPath path = new GraphicsPath())
        {
            path.AddString("DrawPath Smooth", this.Font.FontFamily,
                (int)this.Font.Style, this.Font.Size, new Point(10, y),
                string_format);
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.FillPath(Brushes.Green, path);
            y += this.Font.Height;
        }
    }
}

First the code draws some text while using the default value for TextRenderingHint. The result is pretty good.

Next the code sets TextRenderingHint to AntiAliasGridFit and draws some more text. This produces the best result but the result given by the default setting is also very good. In fact, you need to look very closely at the picture shown here to notice any difference at all. Look closely at the top of the D and S characters to see a slight difference.

Also notice that changing the TextRenderingHint changed the way the text was positioned so the text “DrawString” has a different width in the two samples. In general changing the drawing parameters may change the exact positioning of the text.

Next the program makes a StringFormat object, which is needed to draw text on a GraphicsPath. The code then makes a GraphicsPath object, adds sample text to it, and then draws the GraphicsPath. This first sample, which uses the default SmoothingMode value, produces a fairly ugly result.

Finally the program repeats these steps to draw another sample in a GraphicsPath, this time after setting SmoothingMode to AntiAlias. That result is excellent.

Again if you look closely you’ll see that the character spacing has changed slightly. The text drawn by the GraphicsPath seems to use spacing similar to that used by DrawString with the default TextRenderingHint.

The moral is, if you want to draw smooth text in a GraphicsPath, you need to set SmoothingMode instead of TextRenderingHint.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in drawing, fonts, graphics | Tagged , , , , , , , , , , , , , | 1 Comment

Create a class outside of any namespace statement in C#

[namespace]

When you create a class in C#, Visual Studio automatically puts it inside a namespace statement. If your project’s name is howto_remove_namespace, then by default that namespace is howto_remove_namespace.

But did you know that you can create classes outside of any namespace? The following code shows the file Person.cs used by this example.

namespace howto_remove_namespace
{
    public class Person1
    {
        public string FirstName, LastName;
        public override string ToString()
        {
            return FirstName + " " + LastName;
        }
    }
}

public class Person1
{
    public string FirstName, LastName;
    public override string ToString()
    {
        return "[" + FirstName + " " + LastName + "]";
    }
}

public class Person2
{
    public string FirstName, LastName;
    public override string ToString()
    {
        return "[" + FirstName + " " + LastName + "]";
    }
}

The file begins with a typical namespace statement. Inside the namespace, the file defines the Person1 class with FirstName and LastName properties, and a ToString method.

After the namespace’s closing bracket, the file defines another Person1 class. This version’s ToString method puts brackets around the name so you can tell it’s the second version of the class.

The file finishes by defining a Person2 class, again outside of the namespace block.

If you use the View menu’s Object Browser command, you can use the Object Browser to search for the classes. If you search for Person1, the Object Browser shows only one version of the Person1 class and it says it’s in the howto_remove_namespace namespace. (It’s weird that the Object Browser doesn’t show both versions of the class somewhere.)

Here’s where it gets a little weird. If you comment out either of the versions of the class, the Object Browser still finds the other version and still says it’s in the same namespace. The class that is not inside the namespace block is still in the namespace because that is the default namespace for the project.

Both versions of the class seem to be in the same namespace, which you would think would be a name conflict, but C# seems to be okay with that. If you run the program with both classes defined, C# uses the one that’s inside the namespace block. If you remove that version, then C# uses the other version.

The main program uses the following code to demonstrate the two versions of the Person1 class and the Person2 class.

// Make some people.
private void Form1_Load(object sender, EventArgs e)
{
    Person1 rod = new Person1()
        { FirstName = "Rod", LastName = "Stephens" };
    txtPerson1.Text = rod.ToString();

    Person2 zaphod = new Person2()
        { FirstName = "Zaphod", LastName = "Beeblebrox" };
    txtPerson2.Text = zaphod.ToString();
}

If you look closely at the picture at the top of the post, you can see that the program uses the first definition of Person1. It also uses Person2, which is not contained in the namespace block.

So what’s the point? If you do a lot of copying of code from one project to another, as I sometimes do with these examples, you can remove the namespace statement from a file to make copying the code easier. If you make a Person class and remove the namespace statement from its file, then you can copy and paste the whole file into another project and not need to worry about messing up the namespace statement. Without that statement, the class gets the new project’s default namespace and everything works just fine.

This still leaves a few unanswered questions. When you have both versions defined, where is the one outside of the namespace block? Is it replaced by the one inside the namespace block and thus inaccessible? Is there a way to explicitly access the hidden version? If you know the answer, please post a comment.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in classes, syntax | Tagged , , , , , , , | 2 Comments

Book Review: The Manga Guide to Linear Algebra

(Occasionally I post reviews of book that I think may interest programmers, in this case admittedly only those with a mathematical inclination.)

[Linear Algebra]


The Manga Guide to Linear Algebra  
$24.95, 264 pages, 2012
By Shin Takahashi
No Starch Press
ISBN 13: 978-1593274139

I’ve always avoided the Manga series (The Manga Guide to Neurosurgery, The Manga Guide to Xenolinguistics, and so forth), but I use a lot of math in my programming, including linear algebra, so when I saw The Manga Guide to Linear Algebra, I thought I’d give it a try.

The book teaches the basics of linear algebra from the ground up. It starts with the definitions from set theory that you need to know to understand the underpinnings of linear algebra and works up from there so you don’t need any previous background in any sort of mathematics.

The book’s format is a bit quirky, with one manga character teaching another about linear algebra, but the result is surprisingly effective. In the real world, different students learn best with different teaching methods. For example, some learn best by reading a textbook, others by working through exercises, and still others by listening to someone explain a topic. I fall into the last group and usually learn the most by watching lectures. Surprisingly this book seemed to fit my learning style fairly well. Even though the “lectures” are given by manga characters, it seemed easier for me to digest than a normal textbook.

The material is presented clearly, logically, and in an easy-to-follow style. I also admit I got hooked on the corny teen romance storyline running throughout the book.

After I finished the book, a friend of mine in middle school took it and used it to teach himself linear algebra! I doubt he was confident of the most advanced material at the very end of the book, but I’m sure the book gave him a great head start. Later he took the book on vacation with him and gave it to his cousins who also read it. This particular copy of the book has seen plenty of use!

My only real complaint about this book is that it doesn’t include many exercises. The characters work through enough examples to give you a decent understanding of the material, but a bunch of exercises would let readers use another method for cementing the information in their minds.

This book also doesn’t cover the full breadth of linear algebra and I vaguely remember some topics from my undergraduate days that weren’t fully explored in this book, but it does provide a good introduction so you’ll be ready for further research into linear algebra either in a class or in a more advanced book.


Follow me on Twitter   RSS feed   Donate




Posted in books, mathematics | Tagged , , , , , , , , | Leave a comment

Get the screen’s resolution in C#

[resolution]

Normally a monitor displays 96 logical (or “notional”) dots per inch (dpi) but it’s not necessarily a good idea to assume that value. When this example starts, the following code displays the logical resolution of the screen in dpi.

private void Form1_Load(object sender, EventArgs e)
{
    using (Graphics gr = this.CreateGraphics())
    {
        txtScreenHorizontal.Text = gr.DpiX.ToString() + " dpi";
        txtScreenVertical.Text = gr.DpiY.ToString() + " dpi";
    }
    txtScreenHorizontal.Select(0, 0);
}

The code creates a Graphics object and then uses its DpiX and DpiY properties to get the horizontal and vertical resolution.

Note that logical dots per inch is not necessarily the same as actual dots per inch on the screen. That will also depend on the screen size and the resolution that you have selected. For example, my 15″ screen can use resolutions between 800 x 600 pixels and 1366 x 768 pixels but they all report 96 dpi.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Make Ctrl+A select all of the text in a TextBox in C#

[Ctrl+A]

Often it’s convenient for the user to be able to press Ctrl+A to select all of the text in the TextBox that has the focus. Strangely that’s not the default behavior for the TextBox. Perhaps the TextBox doesn’t handle Ctrl+A so the program can use Ctrl+A as an accelerator.

This example uses the following KeyPress event handler to select all of the text in a TextBox when the user presses Ctrl+A in it.

// On Ctrl+A, select all of the TextBox's text.
private void CtrlA_KeyPress(object sender, KeyPressEventArgs e)
{
    if (e.KeyChar == Convert.ToChar(1))
    {
        TextBox txt = sender as TextBox;
        txt.SelectAll();
        e.Handled = true;
    }
}

If the key pressed is Ctrl+A, the code converts the sender parameter into a TextBox and calls that control’s SelectAll method to select its text. The code uses the sender parameter instead of a specific hard-wired TextBox so you can use the same event handler for any number of TextBox controls.

After selecting the text, the code sets the e.Handled parameter to true to indicate that the event has been handled and the program should not try to handle it further. If you don’t do that, the event passes to the TextBox control’s default event handler, which beeps when it sees Ctrl+A.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, user interface | Tagged , , , , , , , , , , | Leave a comment