Use VBA code to pick random cells from a filtered selection in an Excel workbook

pick random elements

This example shows how you can use VBA code to pick random cells in an Excel workbook when the selected cells are filtered.

The post Use VBA code to pick random cells from the currently selected cells in an Excel workbook shows how you can pick random cells from a selection. Unfortunately that method only works if the data is unfiltered. If you add a filter, then the method works but it includes rows that are filtered out.

This post shows how to pick random cells while excluding those that are filtered out.

Picking Items

The original method looped used Application.Selection to look at the selected cells. Unfortunately that method includes cells that should be filtered out.

You can consider only the non-filtered cells by looking at Application.Selection.SpecialCells(xlCellTypeVisible). That produces a range that includes only the selected cells. Unfortunately if you then look at this range’s cells as in Application.Selection.SpecialCells(xlCellTypeVisible).Cells(1), the range again includes cells that should be filtered out.

The problem is that the range’s Cells property treats the range as a contiguous area and does not exclude filtered out rows.

This example loops through the selected cells and copies them into an array. It then randomizes the array and selects the correct number of them from the array.

The most interesting piece of code is the following SelectRandom method.

' Select the indicated number of items from the
' currently selected cells.
Public Sub SelectRandom(ByVal num_to_select As Integer)
Dim num_items As Integer
Dim i As Integer
Dim j As Integer
Dim selected_range As Range
Dim temp As Range
Dim ranges() As Range

    ' Make sure the selection is a range.
    If Not (TypeOf Application.Selection Is Range) Then
        MsgBox "The current selection is not a range."
        Exit Sub
    End If

    ' Make sure we're selecting at least 1 item.
    If num_to_select < 1 Then
        MsgBox "Cannot pick fewer than 1 item."
        Exit Sub
    End If

    ' See how many items are selected.
    Set selected_range = Application.Selection.SpecialCells(xlCellTypeVisible)
    num_items = selected_range.Count
    If num_to_select > num_items Then
        MsgBox "You cannot pick more items than there are in total."
        Exit Sub
    End If

    ' Make an array containing the selected visible cells.
    ReDim ranges(0 To num_items - 1)
    i = 0
    For Each temp In selected_range
        Set ranges(i) = temp
        i = i + 1
    Next temp

    ' Randomize the cells.
    For i = num_items - 1 To 1 Step -1
        ' Randomly pick an index at or below this one.
        j = Int((i + 1) * Rnd)
        
        ' Swap ranges(j) and ranges(i).
        Set temp = ranges(i)
        Set ranges(i) = ranges(j)
        Set ranges(j) = temp
    Next i
    
    ' Deselect all items.
    Application.Selection.Font.Bold = False
    Application.Selection.Font.Color = vbBlack

    ' Select the first items.
    For i = 0 To num_to_select - 1
        Debug.Print ranges(i)
        ranges(i).Font.Bold = True
        ranges(i).Font.Color = vbRed
    Next i
End Sub

This method does some error checking and then creates a range named selected_range that holds the selected cells that are not filtered out. It then makes an array of Range and loops through selected_range copying the selected cells into the array.

Next the code randomizes the values in the array.

The program then makes all of the cells in the original selection use black, non-bold text. It then loops through the desired number of cells in the randomized array and makes those cells bold and red to indicate that they are selected.

Picking From Disconnected Ranges

The original post mentioned above picks random cells from a single contiguous block of cells. If you try to use that method to pick cells from a selection that includes multiple disjoint areas, the method fails.

The post Use VBA to randomize Excel selections avoids that problem by making an array holding the values in all of the cells in the selected range. It randomizes those values and then copies them back into the selected cells.

This post also works with the individual values in the selected range, so it also avoids the disconnected selection problem. That means the code can select random items from a selection that includes multiple disconnected regions with no additional code.

Conclusion

Download the example to try it and to see additional details. For example, add some values in the second column, control-click to select disconnected cells, and click the button to make random selections.


Download Example   Follow me on Twitter   RSS feed




Posted in Excel, VBA | Tagged , , , , , , , , , , | Leave a comment

Make multi-image icons from files in C#

[multi-image icons]

The post Make multi-image icon files in C# showed how to create several bitmaps at runtime and then used them to create multi-image icons in C#. This example lets you load multiple saved image files and use them to create multi-image icons.

Adding Files

This program displays files in a ListView control named lvwFiles. It stores preview images of the image files that you load in a ImageList component named imlFiles. When the program starts, it uses the following code to set some ListView properties.

private void Form1_Load(object sender, EventArgs e)
{
    lvwFiles.LargeImageList = imlFiles;
    lvwFiles.View = View.Tile;
    imlFiles.ImageSize = new Size(64, 64);
}

This code makes the ListView control use the imlFiles ImageList for its large image list. It also sets the ListView control’s View property to Tile so the control displays item text with large images.

The code also sets the ImageList component’s Size property to 64 pixels by 64 pixels. If you add an image to this control, it will resize the image if necessary so it can store a 64 x 64 pixel image.

You could set all of these properties at design time in the Form Designer. I put the code here to make it more obvious how these properties are set.

When you select File menu’s Open command, the following code executes.

private void mnuFileOpen_Click(object sender, EventArgs e)
{
    if (ofdImages.ShowDialog() == DialogResult.OK)
    {
        foreach (string filename in ofdImages.FileNames)
        {
            Bitmap bm = new Bitmap(filename);
            imlFiles.Images.Add(bm);
            lvwFiles.Items.Add(filename, imlFiles.Images.Count - 1);
        }
        mnuFileSaveAs.Enabled = (lvwFiles.Items.Count > 0);
    }
}

This code displays the ofdImages Open File Dialog. Note that I set this control’s Multiselect property to true at design time so it lets you pick multiple files.

If you use the dialog to select one or more files and then click Open, the code loops through the dialog’s FileNames list to see which files you have selected.
For each selected file, the program uses the file to create a Bitmap and adds the Bitmap to the ImageList component. (Remember that the ImageList resizes the Bitmap if necessary so it is 64 x 64 pixels in size.) The code also adds the selected file’s name to the lvwFiles ListView control and sets that item’s image index to the index of the last image in the ImageList component. That makes the ListView display the file’s name (probably truncated) and the image that it contains.

After it has loaded the image files that you selected, the program enables the File menu’s Save As command if you have loaded at least one file.

Removing Files

The program lets you remove files from the ListView control in two ways. First, if you select the Items menu’s Clear command, the following code executes.

private void mnuItemsClear_Click(object sender, EventArgs e)
{
    lvwFiles.Items.Clear();
    imlFiles.Images.Clear();
    mnuFileSaveAs.Enabled = (lvwFiles.Items.Count > 0);
}

This code clears the ListView control and removes all of the images from the ImageList component. It then disables the File menu’s Save As command.

The second way you can remove files is to select one in the ListView control and then press Delete. When you press a key while the ListView has the input focus (and it always does because it’s the only control on the form), the following code executes.

private void lvwFiles_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Delete)
    {
        foreach (ListViewItem item in lvwFiles.SelectedItems)
        {
            item.Remove();
        }

        if (lvwFiles.Items.Count == 0)
        {
            mnuFileSaveAs.Enabled = false;
            imlFiles.Images.Clear();
        }
    }
}

If the key you pressed was Delete, the code loops through the ListView control’s selected items and removes them.

When it has finished removing items, the code checks whether the ListView is empty. If the ListView contains no items, then the code disables the File menu’s Save As command and empties the ImageList.

Notice that the code does not remove any images from the ImageList unless the ListView is completely empty. If you remove an item’s image from the list, then the other items’ ImageIndex properties are not automatically renumbered so they may point to the wrong images. All of this means that the ImageList may contain images that are no longer used, but that won’t be a problem unless you add and then remove a huge number of images. Don’t do that.

Saving the Icon File

If you have one or more images loaded and you select the File menu’s Save As command, the following code executes.

private void mnuFileSaveAs_Click(object sender, EventArgs e)
{
    if (sfdIcon.ShowDialog() == DialogResult.OK)
    {
        List<Bitmap> bitmaps = new List<Bitmap>();
        foreach (ListViewItem item in lvwFiles.Items)
        {
            bitmaps.Add(new Bitmap(item.Text));
        }

        IconFactory.SavePngsAsIcon(
            bitmaps, sfdIcon.FileName, true);
        this.Icon = new Icon(sfdIcon.FileName);
    }
}

This code displays a Save File Dialog. If you select an icon file and click Save, the program creates a list named bitmaps to hold Bitmap objects.

The program then loops through the items in the ListView control. For each item, the program uses the file name stored in the item to create a Bitmap and adds the Bitmap to the bitmaps list.

Notice that the code does not use the images stored in the ImageList. Remember that the ImageList resizes those images so they are all 64 x 64 pixels. If you put those images in the bitmaps list, then the resulting icon would contain only images with that size.

When it has finished building the bitmaps list, the program calls the IconFactory.SavePngAsIcon method described in the previous post. See that post for details about how the method works.

The event handler finishes by making the program’s form use the newly created icon loaded from the icon file. Now Windows will use the images stored in the icon resizing them as needed if a particular size is not contained in the icon. For example, look at the picture at the top of this post. The left end of form’s title bar displays a 16 x 16 pixel image. The icon that I created contained 32 x 32, 64 x 64, and 128 x 128 pixel images but no 16 x 16 pixel image. To create the 16 x 16 pixel image that it needed, Windows resized the 32 x 32 pixel image.

If you press Alt+Tab or Win+Tab, Windows will display the appropriate (possibly resized) image from the form’s icon.

In this example I used three PNG files that display their sizes so it’s easy to tell which image is being displayed. In a real icon file, you would probably want the different image files to contain similar images.

Application Icons

The code shown in the preceding section shows how a program can set a form’s icon at runtime. Of course you can also set the form’s icon at design time by using the From Designer. However, that doesn’t set the application’s icon. If you look at the compiled executable in File Explorer, you’ll see that it displays a default icon.

To set the application’s icon, open Visual Studio’s Project menu and select Properties. On the Application tab, click the ellipses next to the icon file text box as shown in the following picture.


[multi-image icons]

Select the icon file that you want the executable to display and click Open.

Now the executable will display images from the file you selected. For example, drag the executable around or use File Explorer’s different views to see different icon sizes.

Conclusion

The previous post shows how you can make multi-image icons. This example shows how you can use the methods described in that post to create icons from bitmap files that you can create using MS Paint or any other drawing program.

Download the example program to create your own icon files and to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Make multi-image icon files in C#

[icon files]

The most interesting thing about icon files is that they can contain several images with different sizes. When a program needs to display the icon, it can pick the size that is appropriate. For example, Windows 10 displays icons in various sizes including 16 x 16, 32 x 32, 48 x 48, and 256 x 256 pixels. It can even scale some icons to produce other sizes. And in “Classic Mode” Windows displays icons with sizes 16 x 16, 24 x 24, 32 x 32, 48 x 48 and 64 x 64 pixels.

The following section explains a basic approach for building multi-image icon files. The section after that describes some changes that I made to the basic approach.

IconFactory

A C# program can save an Icon object into an icon file, but it does not have a simple way to create multi-image icon files.

Fortunately an unnamed poster on Stack Overflow posted an IconFactory class that lets you create multi-image icons. The post is at How to create an Icon file that contains Multiple Sizes / Images in C#.

The basic approach is straightforward, just a lot of work. What this user did was study the specifications for icon files and then write binary data in that format.

Here’s the core of the Stack Overflow IconFactory class.

// Write an icon into a stream.
// Note that this closes the stream.
public static void SavePngsAsIcon(
    IEnumerable<Bitmap> images,
    Stream stream, bool notify_system)
{
    Bitmap[] ordered_images =
        images.OrderBy(i => i.Width).ThenBy(i => i.Height).ToArray();
    using (BinaryWriter writer = new BinaryWriter(stream))
    {
        writer.Write(HeaderReserved);
        writer.Write(HeaderIconType);
        writer.Write((ushort)ordered_images.Length);

        Dictionary<uint, byte[]> buffers =
            new Dictionary<uint, byte[]>();
        uint length_sum = 0;
        uint base_offset =
            (uint)(HeaderLength * EntryLength * ordered_images.Length);
        for (int i = 0; i < ordered_images.Length; i++)
        {
            Bitmap image = ordered_images[i];
            byte[] buffer = CreateImageBuffer(image);
            uint offset = base_offset + length_sum;

            writer.Write(GetIconWidth(image));
            writer.Write(GetIconHeight(image));
            writer.Write(PngColorsInPalette);
            writer.Write(EntryReserved);
            writer.Write(PngColorPlanes);
            writer.Write((ushort)Image.GetPixelFormatSize(
                image.PixelFormat));
            writer.Write((uint)buffer.Length);
            writer.Write(offset);

            length_sum += (uint)buffer.Length;
            buffers.Add(offset, buffer);
        }

        foreach (KeyValuePair<uint, byte[]> kvp in buffers)
        {
            writer.BaseStream.Seek(kvp.Key, SeekOrigin.Begin);
            writer.Write(kvp.Value);
        }
    }

    if (notify_system)
    {
        SHChangeNotify(
            HChangeNotifyEventID.SHCNE_ASSOCCHANGED,
            HChangeNotifyFlags.SHCNF_IDLIST,
            IntPtr.Zero, IntPtr.Zero);
    }
}

This method takes as a parameter an IEnumerable of bitmaps. It uses LINQ to sort the images by their sizes, first by width and then by height.

The code then creates a BinaryWriter to write results into the stream passed into the method. It writes some header information into the writer and then loops through the bitmaps. The method writes each bitmap’s width, height, and other data into the stream.

One of the more interesting pieces of information that the program writes into the stream is the bitmap’s image buffer. The code gets that from the CreateImageBuffer method, which I’ll describe shortly. That buffer contains the pixel information that defines the bitmap.

Note that the BinaryWriter closes the original stream when its using block ends. You’ll see why this matters later.

After the method has finished writing the icon data into the stream, it checks its notify_system parameter and calls SHChangeNotify if it is true. I’ll say more about that in the next section.

The following code shows the CreateImageBuffer method.

private static byte[] CreateImageBuffer(Bitmap image)
{
    using (MemoryStream stream = new MemoryStream())
    {
        image.Save(stream, ImageFormat.Png);
        return stream.ToArray();
    }
}

This method creates a MemoryStream object to hold data in memory. It then calls the bitmap’s Save method to make it save itself into the stream. The method returns the stream converted into an array. That becomes the buffer that the SavePngsAsIcon method uses to represent the bitmap’s pixel data.

Changes

That’s the basic approach, but I made a few changes to the original version posted on Stack Overflow.

First, I reformatted some of the code to make it easier to read. For example, the original version qualified method calls with the IconFactory class name even though they weren’t really necessary. For example, the code used IconFactory.HeaderReserved even though only HeaderReserved was necessary.

I also removed the error handling code that was in the original post. Not that error handling is unimportant, but I wanted to make the example easier to understand. You should definitely include error handling in anym program.

Some of the original error handling also seemed a bit excessive. For example, if you try to pass a null array of bitmaps into the original version, the code throws an appropriate exception. If you do that with this version of the class, you’ll get a “Value cannot be null” exception. It may be a little harder to track down exactly which value is null, but as I said I wanted to keep the example simpler.

The original version of the CreateImageBuffer method called the bitmap’s Save method to save the image in its raw data format. (See the Stack Overflow post to see that.) That only worked with bitmaps loaded from files not created at runtime, so I changed it to ask the bitmap to save itself in PNG format.

My example program saves icon files. Windows caches the images that it displays for icon files, so if you overwrite an existing file with a file that contains new images, Windows does not change the images that you see in places like the desktop and File Explorer. That made testing the program more confusing.

To handle this, I added the notify_system parameter and the call to the SHChangeNotify API function. The new version of the SavePngsAsIcon method uses that function to tell Windows that an icon has changed so it should reload its cache. That makes it display the images that it should for the modified icon file.

If your program is using the SavePngsAsIcon method to create multi-image icons that it uses at runtime, then you should set the notify_system parameter to false so Windows doesn’t need to rebuild its cache. If you save icons in icon files, then set thehwo parameter to true so Windows can update appropriately.

The last major change I made to the IconFactory class is to add two new overloaded versions of the SavePngsAsIcon method. The following code shows the first.

// Save an icon into a file.
public static void SavePngsAsIcon(
    IEnumerable<Bitmap> images,
    string filename, bool notify_system)
{
    using (FileStream stream = new FileStream(filename, FileMode.Create))
    {
        SavePngsAsIcon(images, stream, notify_system);
    }

}

This version takes as a parameter a file name. It creates a FileStream to create that file and then uses the previous version of SavePngsAsIcon to write the icon into the FileStream. That saves the icon into the indicated file.

The following code shows the second overloaded version of the SavePngsAsIcon method, which creates an icon an returns it as an Icon object.

// Return an icon as an Icon object.
public static Icon SavePngsAsIcon(
    IEnumerable<Bitmap> images)
{
    using (MemoryStream stream = new MemoryStream())
    {
        // Write the icon into the stream.
        SavePngsAsIcon(images, stream, false);

        // Create a new stream from the first one.
        using (MemoryStream stream2 =
            new MemoryStream(stream.ToArray()))
        {

            // Create and return an icon from the stream.
            return new Icon(stream2);
        }
    }
}

This code creates a MemoryStream and calls the first version of SavePngsAsIcon to write the icon into the stream. As I mentioned earlier, SavePngsAsIcon closes the stream, so we can’t just rewind it and use it as a stream. Instead this code creates a new stream, initializing it from the first one. It then uses that stream to create a new Icon and returns that Icon.

The Main Program

When the example program starts, it executes the following Load event handler.

private void Form1_Load(object sender, EventArgs e)
{
    Bitmap bm16 = MakeBitmap16();
    Bitmap bm24 = MakeBitmap24();
    Bitmap bm32 = MakeBitmap32();
    Bitmap bm48 = MakeBitmap48();
    Bitmap bm256 = MakeBitmap256();

    pic16.Image = bm16;
    pic24.Image = bm24;
    pic32.Image = bm32;
    pic48.Image = bm48;
    pic256.Image = bm256;

    Bitmap[] bitmaps = { bm16, bm32, bm24, bm48, bm256 };

    // Save the icon into a file.
    string filename = "result.ico";
    IconFactory.SavePngsAsIcon(bitmaps, filename, true);

    // Make this form use the icon.
    this.Icon = IconFactory.SavePngsAsIcon(bitmaps);
}

This code calls various MakeBitmapXx methods to create bitmaps in various sizes. Those methods are relatively straightforward so I won’t show them here. Download the example to see how they work.

The code then displays those bitmaps in a group of PictureBox controls. If you look closely at the picture at the top of this post, you’ll see that the different images are all arranged differently. That’s common for icons in various sizes. What looks good at one size would look bad at another. For example, if you simply shrank the 256 x 256 pixel icon to the 16 x 16 pixel size, you wouldn’t be able to read any of the text.

After it displays the bitmaps, the code makes an array containing them and passes them into IconFactory.SavePngsAsIcon. It passes that method a file name so the method creates the icon and saves it into the file result.ico. You can find the file in the bin/Debug subdirectory. File Explorer displays the file using an appropriate image depending on the View setting. (Details, Large icons, Extra large icons, etc.) If you drag the file around, Windows will display a 96 x 96 pixel version that it creates by scaling the 256 x 256 pixel image.

Finally, the event handler calls another overloaded version of SavePngsAsIcon to create an Icon object and makes the form use that icon. You can see the small verison of the icon in the picture at the top of this post.

[icon files]

If you use Alt+Tab or Win+Tab, you’ll see the 24 x 24 pixel version of the icon above and to the left of the program’s image as shown in the picture on the right.

Conclusion

C# does not provide a way to make multi-image icon files, but the IconFactory class does. Its overloaded versions of the SavePngsAsIcon method let you create an icon from a set of bitmaps and save the result into a stream, file, or Icon object. Download the example to experiment with it and to see the many additional details.

(Many thanks to the unnamed Stack Overflow user who did the research necessary to create the original version of the IconFactory class.)


Download Example   Follow me on Twitter   RSS feed   Donate




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

Use command line arguments to open files in C#


[command line arguments]

Whenever a C# program starts, it receives command line arguments. You can use those arguments to let your program open files that are associated with the program.

For example, when you double-click a .doc or .docx file, Microsoft Word opens and displays the file. You can use command line arguments to do something similar with your programs. Before you learn how to do that, you should know how you can send arguments to the program. (If you already know how you can send one or more files to a program, skip down to the next section.)

Opening With Arguments

Whenever a C# program starts, it receives command line arguments. The first argument is always the name of the program that is executing. Any remaining arguments depend on how you start the program. There are several ways that you can pass command line arguments to the program interactively.

If you double-click on the program, it the program’s name is the only argument that the program receives.

If you open a command window, you can type the program’s name followed by any arguments that you want to pass to the program. This is the most obvious but least convenient way to send arguments to the program.

If you drag-and-drop one or more files onto the program, then the program receives the files’ names as parameters.

In a similar technique, suppose you select one or more files in File Explorer. Then if you right-click the files, you can select Open With to see a sub-menu listing programs that the system thinks might be able to open the file. If you click Choose Another App, then you get a popup listing some apps that you might be able to use. Note that this popup also has a checkbox labeled “Always use this app to open .XXX files.” More on this later.

If you scroll to the bottom of the popup dialog, you’ll find a More Apps choice. (For some reason, Microsoft has made this option pale blue on a white background so it’s hard to see. Maybe it’s different if you’re using a different color scheme.) If you click this, you’ll get (you guessed it) more apps! At the bottom of the new list, there’s yet another pale blue option: Look for another app on this PC. If you get this far without growing bored and wandering away, you get a file selection dialog that lets you pick your program. If you select the program and click Open, then File Explorer opens your program and sends it (as command line arguments) the names of the files that you originally right-clicked so long ago.

Here’s a summary of this process.

  1. Select one or more files.
  2. Right-click.
  3. Select Open With > Choose Another App.
  4. Check the “Always use this app to open .XXX files” box if you like, scroll to the bottom, and select More Apps.
  5. Scroll to the bottom and select “Look for another app on this PC.”
  6. Use the file selection dialog to find the executable program, select it, and click Open.

Whew!

If you go through that whole process and you check the “Always use this app to open .XXX files” box, then later you can double-click on a .XXX file to open the program. In that case, the name of the file that you double-clicked is passed to the program as a command-line argument.

If you followed the process and checked the box, you can also select one or more files, right-click them, and select Open. Again your program starts and it receives the file name(s) in the command line arguments.

If you place a shortcut to your program in the SendTo directory, then you can right-click files and use SendTo to send them to your program.

If you create a shortcut to your program, you can right-click the shortcut, select Properties, and enter command line arguments in the Target text box after the file’s name.

There are probably many other methods for interactively launching a program with command line arguments. There are also some non-interactive methods. For example, you can use the System.Diagnostics.Process class to start a process. You can set the object’s StartInfo.Arguments property before you call the Start method to pass the program command line arguments.

There are probably many other ways to start programs interactively or non-interactively, but this is more than enough for now. WAY more than enough! There’s just one more way to do this that you really need to know about.

Command Line Arguments in Visual Studio

You can pass command line arguments to your program when you run it in Visual Studio. That’s important because that’s the easiest way to test and debug how your program handles those arguments.

To set the arguments, open the Project menu and select Properties. On the Properties window, select the Debug tab and enter values in the Command Line Arguments text box as shown in the following picture.


[command line arguments]

Separate the values with spaces. If a value should contain a space, enclose the value in quotes.

Now when you start the program inside Visual Studio, it will receive the command line arguments that you entered.

Loading Files

That’s enough ways to send arguments to the program. It’s time to talk about your program.

The program should be able to open some kind of file. It could be a serialization of some sort, a data file with a special format, whatever.

This example loads drawing serializations. It extends the example Save and restore line drawings in C#. The original program’s File > Open menu item loaded a file containing lines. The new version of the program moves the code that loads the file into the following method.

// Open the file.
private void LoadFile(string filename)
{
    try
    {
        XmlSerializer xml_serializer = new XmlSerializer(TheDrawing.GetType());
        using (FileStream file_stream = new FileStream(filename, FileMode.Open))
        {
            Drawing new_drawing =
                (Drawing)xml_serializer.Deserialize(file_stream);
            TheDrawing = new_drawing;
            picCanvas.Refresh();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

This code creates a serializer to work with the class named Drawing. The object TheDrawing is of that type. The program opens a file stream to the drawing file, deserializes the stream into a new Drawing object, saves the drawing in the variable TheDrawing, and redraws the program’s PictureBox to show the lines.

Now the File > Open menu command displays an open file dialog and calls the LoadFile method to load the selected file.

Your program may do something different, but it will be easier if you have a method similar to the one above that loads a file.

Processing Command Line Arguments

When the program starts, the following Load event handler executes to handle the command line arguments.

private void Form1_Load(object sender, EventArgs e)
{
    string[] args = Environment.GetCommandLineArgs();
    if (args.Length < 2)
    {
        // We have no command line arguments.
        // Do nothing.
        return;
    }

    // Open the first file.
    MessageBox.Show("Opening [" + args[1] + "]");
    LoadFile(args[1]);

    // If we have more than one file name,
    // load them in their own instances.
    for (int i = 2; i < args.Length; i++)
    {
        // See: Open a file with the system’s default application in C#
        // http://csharphelper.com/blog/2014/08/open-a-file-with-the-systems-default-application-in-c/
        try
        {
            MessageBox.Show("Spawning [" + args[i] + "]");
            System.Diagnostics.Process.Start(args[0], args[i]);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

This code first checks the length of the command argument array. If the array contains a single value, then it is the program’s name. In that case the program does not need to load any files so the event handler simply returns.

Next the code displays a message to let you know what it is doing and then calls the LoadFile method, passing it the first command line argument. That argument will be the name of the first file passed to the program.

The code then loops through any remaining command line arguments. For each argument the code displays a message box and uses System.Diagnostics.Process.Start to execute the program, which has the path stored in args[0]. It passes the value in args[i] in as a command line argument. When the new instance of the program starts, it has a single parameter so the program just loads it.

Now if you somehow pass multiple files to the program, the program loads the first one and launches copies of itself to load the others.

(After you have your program working correctly, remove the message boxes that tell you what the program is doing.)

Conclusion

It’s not too hard to make a program load multiple files when it starts. It checks its command line arguments and loads the file names in args[1]. Then if there are more arguments, the program can launch copies of itself to handle the other files.

Download the example program and give it a try. Try selecting the sample files test.lines, test2.lines, and test3.lines and then dragging them onto the executable program.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Make a digital clock in C#


[digital clock]

This example uses the techniques described in the example Draw LED-style letters in C# to draw a digital clock. That earlier example showed one way to draw LED-style letters. This example simply uses the LedText class from that post to draw the digital clock.

Drawing Colons

If you look at a real digital clock, you’ll see that it has several special-purpose LEDs. Most digital clocks use special LEDs to draw the colon between the hours, minutes, and seconds. Those positions are always colons, so they don’t need to hold a bunch of LEDs that you can turn on and off. Instead they simply hold two LEDs that are always bright. Some clocks also have periods, slashes, or other LEDs that never change. For this example I decided to only worry about the colons.

Because the colon (and other punctuation symbols) don’t need to use a bunch of LEDs, they are relatively thin. You could make a more flexible model of LED letters to include these special characters, but I decided to just make a method to draw LEDs.

The new example adds the following DrawColon method to the LedText class.

// Draw a colon.
public void DrawColon(Graphics gr,
    Brush bg_brush, Brush used_brush,
    Pen used_pen, Brush unused_brush,
    Pen unused_pen, PointF position)
{
    // Clear the background.
    gr.FillRectangle(bg_brush,
        position.X, position.Y,
        LedThickness, CellHeight);

    float y1 = position.Y + CellHeight / 4f;
    float y2 = y1 + CellHeight / 2f;

    RectangleF rect1 = new RectangleF(
        position.X, y1 - LedThickness / 2f,
        LedThickness, LedThickness);
    gr.FillRectangle(used_brush, rect1);
    gr.DrawRectangle(used_pen, rect1);

    RectangleF rect2 = new RectangleF(
        position.X, y2 - LedThickness / 2f,
        LedThickness, LedThickness);
    gr.FillRectangle(used_brush, rect2);
    gr.DrawRectangle(used_pen, rect2);
}

This method simply draws two rectangles that are as thick as the object’s LedThickness value and positioned 1/4 and 3/4 of the way down from the top of the letter’s cell.

Note that the Graphics class has a FillRectangle method that fills a RectangleF, but it does not have a corresponding DrawRectangle method to draw a RectangleF. To make drawing the rectangles easier, I created the following extension method.

public static class Extensions
{
    public static void DrawRectangle(this Graphics gr,
        Pen pen, RectangleF rect)
    {
        gr.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
    }
}

The LedText class needs only one other change to handle the colon. That change is shown in the following DrawText method.

// Draw a sequence of letters.
public void DrawText(Graphics gr, Brush bg_brush,
    Brush used_brush, Pen used_pen,
    Brush unused_brush, Pen unused_pen,
    PointF position, float h_spacing, string text)
{
    float cell_space = CellWidth * (h_spacing - 1);
    foreach (char ch in text)
    {
        if (ch == ':')
        {
            DrawColon(gr, bg_brush, used_brush, used_pen,
                unused_brush, unused_pen, position);
            position.X += LedThickness;
        }
        else
        {
            DrawLetter(gr, bg_brush, used_brush, used_pen,
                unused_brush, unused_pen, position, ch);
            position.X += CellWidth;
        }
        position.X += cell_space;
    }
}

This method first calculates the amount of horizontal space it should add between letters. The h_spacing value indicates the amount by which the cell width should be multiplied to move to the next letter. For example, this value might be 1.2. The method finds the amount of space between cells by multiplying this value by the cell width and then subtracting the cell width.

The code then loops through the text’s letters as before. This time if the letter is a colon, the method calls the DrawColon method and adds the width of the colon, which is LedThickness. If the letter is not a color, then the method calls the DrawLetter method as before and adds the letter’s width, which is CellWidth. In either case the code then adds the space between letters cell_space.

Drawing the Clock

When the program starts, the following code prepares the LedText objects that the program uses to draw the clock.

private LedText TimeLedText, DateLedText;
private void Form1_Load(object sender, EventArgs e)
{
    const float cell_width = 50;
    const float cell_height = 80;
    const float led_thickness = 7;
    const float gap = 1.5f;
    TimeLedText = new LedText(cell_width,
        cell_height, led_thickness, gap);

    const float scale = 0.95f;
    DateLedText = new LedText(scale * cell_width,
        scale * cell_height, scale * led_thickness,
        scale * gap);
}

This code declares the TimeLedText and DateLedText objects at the form level. The form’s Load event handler defines some constants and creates the objects. It scales the DateLedText object’s parameters by 0.95 (a number I found by trial and error) to make the date text as wide as the time text.

The program’s form contains a timer that fires every 500 milliseconds. When it does, the following code executes.

private void tmrTick_Tick(object sender, EventArgs e)
{
    picClock.Refresh();
}

private void picCanvas_Paint(object sender, PaintEventArgs e)
{
    ShowTime(e.Graphics);
}

// Display the time.
private void ShowTime(Graphics gr)
{
    gr.Clear(Color.Black);
    gr.SmoothingMode = SmoothingMode.AntiAlias;

    const float margin = 5f;
    PointF position = new PointF(margin, margin);
    using (Brush unused_brush = new SolidBrush(Color.FromArgb(0, 40, 0)))
    {
        // Draw the time.
        Brush bg_brush = Brushes.Black;
        Brush used_brush = Brushes.Lime;
        Pen used_pen = Pens.Transparent;
        Pen unused_pen = Pens.Transparent;

        TimeLedText.DrawText(gr, bg_brush,
            used_brush, used_pen,
            unused_brush, unused_pen,
            position, 1.2f,
            DateTime.Now.ToLongTimeString());
    }

    using (Brush unused_brush = new SolidBrush(Color.FromArgb(0, 0, 60)))
    {
        // Draw the time.
        Brush bg_brush = Brushes.Black;
        Brush used_brush = Brushes.Blue;
        Pen used_pen = Pens.Transparent;
        Pen unused_pen = Pens.Transparent;

        position.Y += TimeLedText.CellHeight +
            4 * TimeLedText.LedThickness;

        // Draw the day and date.
        string date_string =
            DateTime.Now.DayOfWeek.ToString();
        date_string = date_string.ToUpper().Substring(0, 3);
        date_string += " " +
            DateTime.Now.Day.ToString() + "/" +
            DateTime.Now.Year.ToString().Substring(0, 2);
        DateLedText.DrawText(gr, bg_brush,
            used_brush, used_pen,
            unused_brush, unused_pen,
            position, 1.2f,
            date_string);
    }
}

The Timer’s Tick event handler refreshes the picClock PictureBox. That control’s Paint event handler calls the ShowTime method to do all of the interesting work.

The ShowTime method is relatively straightforward. It defines some brushes and pens and then passes them to the TimeLedText object’s DrawText method to draw the time. It then repeats those steps to draw the date with different brushes and pens. The only really interesting step is where the code adds space to the Y coordinate of the position where it draws the text so the date is drawn far enough below the time.

Conclusion

This is a relatively simple example. It draws the time and date text at specific positions and sizes to make everything fit nicely. You could modify it to calculate the text sizes at runtime to fit the form, but seemed like more work than it was worth for such a basic example. Feel free to make that change it if you like.

The only other non-obvious detail is that I set the form’s FormBorderStyle property to FixedToolWindow at design time.

Download the example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Draw LED-style letters in C#


[LED-style letters]

Every now and then I’ve had the urge to write a digital clock program, and to do that, I need to be able to draw LED-style letters. (Some things are practically a right of passage for programmers. Writing a program that can reproduce its own code, prime factoring your Social Security Number, writing clocks…)

This program draws LED-style letters so I can make a digital clock in a later post. That post will be a lot easier than this one, but even this one isn’t terribly complicated, although some of it is rather long.

Most of the code consists of methods that draw the LEDs. Other pieces use those methods to the letters. The following sections describe the main pieces of the LedText class that does most of the interesting work.

The LedText Class

The LedText class draws LED-style letters. The following code shows the class’s declaration and constructor.

class LedText
{
    public float CellWidth, CellHeight, LedThickness, Gap;
    public Dictionary<char, bool[]> LetterLeds = null;

    public LedText(float cell_width, float cell_height,
        float led_thickness, float gap)
    {
        CellWidth = cell_width;
        CellHeight = cell_height;
        LedThickness = led_thickness;
        Gap = gap;

        // Define the functions that draw the LEDs.
        DefineLedFuncs();

        // Define the letters.
        DefineLetters();
    }
    ...
}

[LED-style letters]

The class defines several variables that define the text’s geometry. The picture on the right shows how those variables determine the size of the letters’ pieces.

The LetterLeds dictionary holds an array of Boolean variables for each letter. Those values indicate which of the LEDs should be turned on and off for the corresponding letter. The dictionary uses the letter as a key, so it’s easy to find the Boolean values that define the letter.

The class’s constructor saves the geometry values and then calls the DefineLedFuncs and DefineLetters methods. The next section describes DefineLedFuncs. I’ll describe the DefineLetters method a bit later.

DefineLedFuncs

The DefineLedFuncs method shown in the following code builds an array containing references to the methods that draw the LEDs.

// Make an array to hold the LED-drawing functions.
private delegate PointF[] LedFunc(PointF point);
private LedFunc[] LedFuncs = null;

private void DefineLedFuncs()
{
    LedFuncs = new LedFunc[]
    {
        MakeLed0,
        MakeLed1,
        MakeLed2,
        MakeLed3,
        MakeLed4,
        MakeLed5,
        MakeLed6,
        MakeLed7,
        MakeLed8,
        MakeLed9,
        MakeLed10,
        MakeLed11,
        MakeLed12,
        MakeLed13,
    };
}

[LED-style letters]

This code first declares a delegate data type named LedFunc. That type refers to a method that takes as a parameter a PointF and returns an array of PointF. The parameter will be the coordinate of the letter’s upper left corner, and the returned array will hold the points that define that LED’s polygon.

After it declares the LedFunc data type, the code declares an array of those named LedFuncs. The picture on the right shows how the LEDs are numbered. For example, the first entry in the LedFuncs array will hold a reference to the method that builds a polygon for LED number 0, which is a short, wide hexagon.

The DefineLedFuncs method creates the LedFuncs array, filling it with the LED-drawing methods.

Later the drawing methods will use the LedFuncs array to find the methods that they need to draw the LED-style letters.

LED-Drawing Methods

Most of the LED-drawing methods are relatively straightforward. They use fairly simple offsets from the letter’s upper left corner to figure out where each polygon’s vertices must be.

For example, starting at the letter’s upper left corner, the leftmost tip of LED 0 is shifted right and down by half of the LED thickness. The code also adds in the Gap value to provide some separation from the nearby LEDs. (Actually it should probably add the Gap divided by the square root of two or something, but I like the current result so I’m not going to mess with it.)

Several of the LEDs have this basic horizontal hexagon shape, so I wrote a helper method named MakeHLed to define them. I’ll show you that shortly, but first here’s the MakeLed0 method.

public PointF[] MakeLed0(PointF position)
{
    PointF p1 = new PointF(
        position.X + LedThickness / 2f + Gap,
        position.Y + LedThickness / 2f);
    PointF p2 = new PointF(
        position.X + CellWidth - LedThickness / 2f - Gap,
        p1.Y);
    return MakeHLed(p1, p2);
}

This method calculates the coordinates of the hexagon’s leftmost and rightmost points p1 and p2. It then calls the following MakeHLed method to create the LED’s hexagon. (The “H” in the middle of the name stands for “horizontal.”)

public PointF[] MakeHLed(PointF p1, PointF p2)
{
    PointF[] points =
    {
        new PointF(p1.X, p1.Y),
        new PointF(p1.X + LedThickness / 2f, p1.Y + LedThickness / 2f),
        new PointF(p2.X - LedThickness / 2f, p2.Y + LedThickness / 2f),
        new PointF(p2.X, p2.Y),
        new PointF(p2.X - LedThickness / 2f, p2.Y - LedThickness / 2f),
        new PointF(p1.X + LedThickness / 2f, p1.Y - LedThickness / 2f),
    };
    return points;
}

This adds and subtracts halves of the LED thickness to make the hexagon shape starting at the leftmost point p1, moving over to rightmost point p2, and returning to point p1. The code is straightforward, although you do need to be careful while figuring out how to generate the points.

The methods that draw LEDs 0, 6, 7, and 13 all follow this same basic approach.

The methods that draw LEDs 1, 5, 8, and 12 work similarly, although they call the MakeVLed method (the “V” stands for “vertical”) to draw their polygons.

The methods that draw LEDs 3 and 10 take a fairly similar approach, but they use two helper methods, MakeCtLed (“Ct” for “center top”) and MakeCbLed (“Cb” for “center bottom”).

So far these methods are fairly long but straightforward. The methods that draw the diagonal LEDs are more complicated.

Drawing Diagonal LEDs

[LED-style letters]

Unfortunately it’s not as easy to figure out where the vertices should go for the diagonal LEDs. You could use points that are the same distances vertically and horizontally from the inner corners of the existing LEDs, but then the LEDs along each diagonal wouldn’t line up properly.

Instead this program uses points that are offset from the letter cell’s outer corners. The offset is the LED thickness divided by the square root of two, as shown in the picture on the right. That makes the distance between the two points at each corner equal to the LED thickness.

(Note that this does not give the diagonal LEDs quite the same thickness as the others. If the letter cell were a square, the that would be the case. When the cell is taller than it is wide, as it is in this example, then the result is slightly thinner. As was the case with diagonal gaps, I like the result so I’m not going to mess with it.)

[LED-style letters]

The picture then intersects those red diagonal lines with vertical and horizontal lines that lie on the inside edges of the outer polygons drawn so far. The picture on the right shows how the red diagonal lines intersect the blue vertical and horizontal lines to define two of the vertices for LED number 2.

The basic approach used to define the diagonal LEDs is to find intersecting pairs of line segments that define the polygon’s vertices, and then pass them into a method that finds the intersections and uses the resulting points to define the polygon.

The following code shows the method that makes LED number 2.

public PointF[] MakeLed2(PointF position)
{
    float sqrt2 = (float)Math.Sqrt(2.0);
    float dx = LedThickness / sqrt2;
    float dy = dx;

    PointF u_diag_pt1 = new PointF(
        position.X + dx,
        position.Y);
    PointF u_diag_pt2 = new PointF(
        position.X + CellWidth,
        position.Y + CellHeight - dy);

    PointF l_diag_pt1 = new PointF(
        position.X,
        position.Y + dy);
    PointF l_diag_pt2 = new PointF(
        position.X + CellWidth - dx,
        position.Y + CellHeight);

    PointF u_horz_pt1 = new PointF(
        position.X,
        position.Y + LedThickness + Gap);
    PointF u_horz_pt2 = new PointF(
        position.X + CellWidth,
        position.Y + LedThickness + Gap);

    PointF l_horz_pt1 = new PointF(
        position.X,
        position.Y + CellHeight / 2f - LedThickness / 2f - Gap);
    PointF l_horz_pt2 = new PointF(
        position.X + CellWidth,
        position.Y + CellHeight / 2f - LedThickness / 2f - Gap);

    PointF l_vert_pt1 = new PointF(
        position.X + LedThickness + Gap,
        position.Y);
    PointF l_vert_pt2 = new PointF(
        position.X + LedThickness + Gap,
        position.Y + CellHeight);

    PointF r_vert_pt1 = new PointF(
        position.X + CellWidth / 2f - LedThickness / 2f - Gap,
        position.Y);
    PointF r_vert_pt2 = new PointF(
        position.X + CellWidth / 2f - LedThickness / 2f - Gap,
        position.Y + CellHeight);

    PointF[][] segs =
    {
        new PointF[] { l_vert_pt1, l_vert_pt2, u_horz_pt1, u_horz_pt2 },
        new PointF[] { u_horz_pt1, u_horz_pt2, u_diag_pt1, u_diag_pt2 },
        new PointF[] { u_diag_pt1, u_diag_pt2, r_vert_pt1, r_vert_pt2 },
        new PointF[] { r_vert_pt1, r_vert_pt2, l_horz_pt1, l_horz_pt2 },
        new PointF[] { l_horz_pt1, l_horz_pt2, l_diag_pt1, l_diag_pt2 },
        new PointF[] { l_diag_pt1, l_diag_pt2, l_vert_pt1, l_vert_pt2 },
    };
    return MakeIntersectionLed(segs);
}

This method calculates the locations of points that it can use to define the segments. It then creates an array of arrays named segs. Each entry in the segs array is an array containing four points that define two line segments. The point where the segments intersect gives one of the polygon’s vertices.

After it defines the segments, the MakeLed2 method passes the array into the following MakeIntersectionLed method.

public PointF[] MakeIntersectionLed(PointF[][] segs)
{
    List points = new List();

    foreach (PointF[] seg in segs)
    {
        PointF a1 = seg[0];
        PointF a2 = seg[1];
        PointF b1 = seg[2];
        PointF b2 = seg[3];
        bool lines_intersect, segs_intersect;
        PointF intersection, close_pt1, close_pt2;
        FindIntersection(a1, a2, b1, b2,
            out lines_intersect, out segs_intersect,
            out intersection, out close_pt1, out close_pt2);
        points.Add(intersection);
    }

    return points.ToArray();
}

After all that setup, this method is fairly simple. It loops through the segments in the segs array. Each entry in the array contains four points that define two line segments. The method simply calls the FindIntersection method to see where the two segments intersect and adds the resulting point to the points list. When it finishes processing all of the segment pairs, the method converts the list into an array and returns the result.

That’s the end of the code that creates polygons to draw the LEDs. The methods that I haven’t shown here are similar to the others. You can always download the example if you want to see all of their details.

DefineLetters

Recall that the LetterLeds dictionary maps letters as keys to arrays of Boolean values that indicate which LEDs should be drawn. The following code shows the DefineLetters method that fills the LetterLeds dictionary.

// Define the LED methods used to draw letters.
public void DefineLetters()
{
    if (LetterLeds != null) return;
    LetterLeds = new Dictionary();

    LetterLeds.Add((char)0, StringToBool("11111111111111"));

    LetterLeds.Add('0', StringToBool("11000100100011"));
    LetterLeds.Add('1', StringToBool("00001100000010"));
    LetterLeds.Add('2', StringToBool("10000111100001"));
    LetterLeds.Add('3', StringToBool("10000101000011"));
    LetterLeds.Add('4', StringToBool("01000111000010"));
    LetterLeds.Add('5', StringToBool("11000011000011"));
    LetterLeds.Add('6', StringToBool("11000011100011"));
    LetterLeds.Add('7', StringToBool("10000100000010"));
    LetterLeds.Add('8', StringToBool("11000111100011"));
    LetterLeds.Add('9', StringToBool("11000111000011"));
    LetterLeds.Add('A', StringToBool("11000111100010"));
    LetterLeds.Add('B', StringToBool("10010101001011"));
    LetterLeds.Add('C', StringToBool("11000000100001"));
    LetterLeds.Add('D', StringToBool("10010100001011"));
    LetterLeds.Add('E', StringToBool("11000010100001"));
    LetterLeds.Add('F', StringToBool("11000010100000"));
    LetterLeds.Add('G', StringToBool("11000001100011"));
    LetterLeds.Add('H', StringToBool("01000111100010"));
    LetterLeds.Add('I', StringToBool("10010000001001"));
    LetterLeds.Add('J', StringToBool("00000100100011"));
    LetterLeds.Add('K', StringToBool("01001010100100"));
    LetterLeds.Add('L', StringToBool("01000000100001"));
    LetterLeds.Add('M', StringToBool("01101100100010"));
    LetterLeds.Add('N', StringToBool("01100100100110"));
    LetterLeds.Add('O', StringToBool("11000100100011"));
    LetterLeds.Add('P', StringToBool("11000111100000"));
    LetterLeds.Add('Q', StringToBool("11000100100111"));
    LetterLeds.Add('R', StringToBool("11000111100100"));
    LetterLeds.Add('S', StringToBool("11000011000011"));
    LetterLeds.Add('T', StringToBool("10010000001000"));
    LetterLeds.Add('U', StringToBool("01000100100011"));
    LetterLeds.Add('V', StringToBool("01001000110000"));
    LetterLeds.Add('W', StringToBool("01000100110110"));
    LetterLeds.Add('X', StringToBool("00101000010100"));
    LetterLeds.Add('Y', StringToBool("00101000001000"));
    LetterLeds.Add('Z', StringToBool("10001000010001"));
    LetterLeds.Add(' ', StringToBool("00000000000000"));
    //                                -|\|/|--|/|\|-
}

This method makes a new LetterLeds dictionary and then adds items to that list.

Each item that it adds has the form of a letter and the result of a call to the StringToBool helper method described shortly. That method converts a string of zeros and ones into an array of corresponding Boolean values.

The DefineLetters method starts by creating a special entry for the letter (char)0. That entry has every LED turned on so it creates a result similar to several of the pictures shown earlier in this post. A bit later you’ll see how the program uses that entry when you try to draw a letter that is undefined.

After it defines “letter 0,” the code defines entries for the digits 0 through 9, the letters A through Z, and the space character. Feel free to add others if you like.

If you study the entries, you can verify that 0s and 1s identify each letter’s LEDs. For example, the Zs entries activate the top and bottom LEDs, plus the upper-right to lower-left diagonal LEDs.

The comment at the end of the method makes it a little easier to create new entries. The -|\|/|--|/|\|- characters show the direction in which the corresponding LEDs point.

The following code shows the StringToBool helper method.

// Convert a string of the form 10100110...
// into an array of bool.
private bool[] StringToBool(string values)
{
    bool[] result = new bool[values.Length];
    for (int i = 0; i < values.Length; i++)
        result[i] = (values[i] == '1');
    return result;
}

This method creates an array of bool that has the same length as its input string. It then loops through the string’s characters and sets the corresponding array entries to true if the character is 1. When it is finished, the method returns the array.

Drawing Letters

The class does a lot of work to set up the LetterLeds dictionary and the LedFuncs array. Using them is a lot easier.

The following MakeLetterPgons method returns list of polygons needed to draw a letter.

// Make the polygons that represent a letter.
public void MakeLetterPgons(char letter, PointF position,
    out List<PointF[]> used_pgons, out List<PointF[]> unused_pgons)
{
    used_pgons = new List<PointF[]>();
    unused_pgons = new List<PointF[]>();

    bool[] used;
    if (LetterLeds.ContainsKey(letter)) used = LetterLeds[letter];
    else used = LetterLeds[(char)0];

    for (int i = 0; i < used.Length; i++)
    {
        if (used[i])
            used_pgons.Add(LedFuncs[i](position));
        else
            unused_pgons.Add(LedFuncs[i](position));
    }
}

This method uses output parameters to return polygons for the LEDs that should and should not be used to draw the letter.

The method first creates the two lists. It then uses the LetterLeds dictionary’s ContainsKey method to see if the dictionary contains an entry for the letter. If the letter is in the dictionary, then the code fetches its Boolean values and saves them in the used array. If the letter is not in the dictionary, then the code uses the “letter 0” entry’s Boolean values so the letter will be drawn with all LEDs active as a sort of placeholder. (Alternatively you could use the space character to make a blank result.)

The code then loops through the Boolean values in the used array. If an entry is true, the program calls the corresponding LedFuncs method to generate the LED and adds it to the used_pgons list. If the used array entry is false, the program calls the LedFuncs method and saves the result in the unused_pgons list.

The following DrawLetter method uses the MakeLetterPgons method to draw a letter.

// Draw a letter.
public void DrawLetter(Graphics gr, Brush bg_brush,
    Brush used_brush, Pen used_pen,
    Brush unused_brush, Pen unused_pen,
    PointF position, char letter)
{
    // Clear the background.
    gr.FillRectangle(bg_brush,
        position.X, position.Y,
        CellWidth, CellHeight);

    // Draw the polygons.
    List<PointF[]> used_pgons, unused_pgons;
    MakeLetterPgons(letter, position, out used_pgons, out unused_pgons);
    foreach (PointF[] pgon in unused_pgons)
    {
        gr.FillPolygon(unused_brush, pgon);
        gr.DrawPolygon(unused_pen, pgon);
    }
    foreach (PointF[] pgon in used_pgons)
    {
        gr.FillPolygon(used_brush, pgon);
        gr.DrawPolygon(used_pen, pgon);
    }
}

This method fills the letter’s background area with the brush bg_brush. Normally you would probably want the whole drawing area to have this same background color. In that case, this step isn’t really necessary. In fact, if you have some sort of drawing already on the drawing area and you want to draw the letter on top, you might need to not do this. You can pass the brush SolidBrushes.Transparent in as the bg_brush parameter to leave the background unchanged.

Next the code calls the MakeLetterPgons method to get the letter’s polygons. It then loops through the polygons representing inactive LEDs and fills and then outlines them. As with the background, you can pass a transparent brush or pen for the unused_brush and unused_pen parameters if you don’t want to draw one of those items.

The method repeats those steps for the active LEDs and is done. Simple!

Drawing Text

The LedText class has one more method: DrawText. This method uses the following code to draw a string of letters.

// Draw a sequence of letters.
public void DrawText(Graphics gr, Brush bg_brush,
    Brush used_brush, Pen used_pen,
    Brush unused_brush, Pen unused_pen,
    PointF position, float h_spacing, string text)
{
    foreach (char ch in text)
    {
        DrawLetter(gr, bg_brush, used_brush, used_pen,
            unused_brush, unused_pen, position, ch);
        position.X += CellWidth * h_spacing;
    }
}

The code loops through the input string’s letters. The code calls DrawLetter to draw each letter and then adds h_spacing times the letters’ cell width to the X position to draw the next letter shifted to the right.

Normally h_spacing should be something like 1.2 to allow a little space between the letters. You can make that space wider, narrower, or even negative if you like.

The Main Program

The main program includes several test methods. The following method is initially not commented out so the program draws the digits 0 through 9 and the letters A through Z.

private void TestLetters(Graphics gr)
{
    gr.Clear(Color.Black);
    gr.SmoothingMode = SmoothingMode.AntiAlias;

    const float margin = 10;
    const float cell_width = 50;
    const float cell_height = 80;
    const float led_thickness = 7;
    const float gap = 1.5f;

    LedText letter = new LedText(
        cell_width, cell_height, led_thickness, gap);

    Brush bg_brush = Brushes.Black;
    Brush used_brush = Brushes.Lime;
    Pen used_pen = Pens.Transparent;
    Brush unused_brush = new SolidBrush(Color.FromArgb(0, 40, 0));
    Pen unused_pen = Pens.Transparent;

    PointF position = new PointF(margin, margin);
    letter.DrawText(gr, bg_brush, used_brush, used_pen,
            unused_brush, unused_pen, position,
            1.2f, "ABCDEFGHI");

    position.Y += letter.CellHeight * 1.2f;
    letter.DrawText(gr, bg_brush, used_brush, used_pen,
            unused_brush, unused_pen, position,
            1.2f, "JKLMNOPQR");

    position.Y += letter.CellHeight * 1.2f;
    letter.DrawText(gr, bg_brush, used_brush, used_pen,
            unused_brush, unused_pen, position,
            1.2f, "STUVWXYZ0");

    position.Y += letter.CellHeight * 1.2f;
    letter.DrawText(gr, bg_brush, used_brush, used_pen,
            unused_brush, unused_pen, position,
            1.2f, "123456789");
}

This method defines some geometry values and creates a LedText object. It then makes a PointF variable named position to keep track of where the text should be drawn.

The code then calls the DrawText method to draw the string “ABCDEFGHI.” Nxet it adds 1.2 times the letters’ cell height to the position variable’s Y coordinate to move down a line. It repeats those steps, drawing a string of letters and then moving down to a new line, until it has drawn the letters and digits.

Conclusion

This is not a font. You can go online and download LED fonts, but I wanted to build LED-style letters myself and I still don’t know how to create a font from scratch in C#. For now, this program is pretty fun.

Download the example to see all of the details and to experiment with it. Try changing the letter geometry, colors, and brushes. For example, the picture below draws “3” with yellow LEDs outlined in red and “5” with LEDs filled with a green and lime brick pattern.


[LED-style letters]


Download Example   Follow me on Twitter   RSS feed   Donate




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

Use Microsoft Print to PDF to create a PDF file in C#

[PDF]

This example shows one way that you can make a C# program create a PDF file. It’s super easy. (I haven’t posted any easy examples for a while, so this one makes a refreshing change.)

Selecting the Printer

In a Windows Forms program, you use a PrintDocument object to generate printouts. To print to a specific printer, simply set that object’s PrinterSettings.PrinterName property to the name of the printer that you want to use. To create a PDF file, set the printer name to “Microsoft Print to PDF.”

Note that you can do this from other programs, too. For example, Excel, Word, WordPad, and others can print to Microsoft Print to PDF. Some of those applications also have their own ways to create PDFs. For example, Excel’s Save As command can save to a PDF.

The only catch is that this printer must be installed and activated on your system. Try printing from WordPad and see if this printer is available. If it is not, see this Microsoft article.

How to Add or Reinstall the Microsoft PDF Printer

Printing

Microsoft Print to PDF is a native Windows 10 print driver. When the PrintDocument object sends its output to this driver, the driver prompts the user for the name of the PDF file that it should create and then places its output in the file in PDF format.

When you click this program’s Print to PDF button, the following code executes.

private int NextPage = 0;

private void btnPrintToPdf_Click(object sender, EventArgs e)
{
    NextPage = 0;
    pdocShapes.PrinterSettings.PrinterName = "Microsoft Print to PDF";
    pdocShapes.Print();
}

The button’s Click event handler first sets the NextPage variable to zero to make printing start with the first page. The program uses this variable to keep track of the number of the next page that it should print. You’ll see how that works shortly.

(Tip: If your program prints more than one page, then you’ll need some sort of next page variable like this one. You should reset it to zero whenever you start a new printout. If you don’t, then the second time you try to print, the variable will point after the last page so you won’t see anything.)

Next the code sets the PrinterName property for the PrintDocument object named pdocShapes. It finishes by calling the PrintDocument object’s Print method.

The following code shows the PrintDocument object’s PrintPage method, which actually generates the printed results.

private void pdocShapes_PrintPage(object sender,
    System.Drawing.Printing.PrintPageEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    switch (NextPage)
    {
        case 0:
            using (Pen pen = new Pen(Color.Red,10))
            {
                e.Graphics.DrawEllipse(pen, e.MarginBounds);
            }
            break;

        case 1:
            using (Pen pen = new Pen(Color.Green, 10))
            {
                e.Graphics.DrawRectangle(pen, e.MarginBounds);
            }
            break;

        case 2:
            using (Pen pen = new Pen(Color.Blue, 10))
            {
                Point[] points =
                {
                    new Point(
                        e.MarginBounds.Left,
                        e.MarginBounds.Top + e.MarginBounds.Height / 2),
                    new Point(
                        e.MarginBounds.Left + e.MarginBounds.Width / 2,
                        e.MarginBounds.Top),
                    new Point(
                        e.MarginBounds.Right,
                        e.MarginBounds.Top + e.MarginBounds.Height / 2),
                    new Point(
                        e.MarginBounds.Left + e.MarginBounds.Width / 2,
                        e.MarginBounds.Bottom),
                };
                e.Graphics.DrawPolygon(pen, points);
            }
            break;
    }
    NextPage++;
    e.HasMorePages = (NextPage <= 2);
}

This code sets the Graphics object’s SmoothingMode and then uses a switch statement to figure out which page it is printing. It then draws either an ellipse, rectangle, or diamond on the page. Notice how the code uses e.MarginBounds to make the shape fill the printed page’s area inside its margins.

After it has drawn the current page, the code increments the NextPage variable. If NextPage is now greater than 2, then there are not more pages to print, so the code sets e.HasMorePages to false to tell the PrintDocument object that is is done.

The PrintDocument object raises its PrintPage event until e.HasMorePages is false and then it stops.

That’s all there is to it. Download the example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Use menu commands with shortcuts to save and restore user-drawn polygons in C# and WPF

[polygons]

The example Let the user edit polygons in WPF and C# shows how to let the user draw and edit polygons in WPF. This example shows how you can let the user save and restore those polygons.

While I was building this program, I wanted to use a menu to provide New, Open, and Save As commands. To make using the menu items easier, I wanted to let the user fire those commands by pressing Ctrl+N, Ctrl+O, and Ctrl+S. In addition to showing how to save and restore the user’s polygons, this post explains how to provide those commands and provide shortcuts for them.

XAML Code

Probably the easiest way to arrange the controls in a WPF program that uses menus is to use a DockPanel instead of a Grid as the window’s top-level container. Then you can dock the menu to the top of the panel and dock other controls below that.

The following XAML code shows how this example defines its window.

<Window x:Class="howto_wpf_serialize_polygon.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="howto_wpf_serialize_polygon" Height="300" Width="300">
    <Window.CommandBindings>
        <CommandBinding Command="New"
            CanExecute="NewCommand_CanExecute"
            Executed="NewCommand_Executed" />
        <CommandBinding Command="Open"
            CanExecute="OpenCommand_CanExecute"
            Executed="OpenCommand_Executed" />
        <CommandBinding Command="SaveAs"
            CanExecute="SaveAsCommand_CanExecute"
            Executed="SaveAsCommand_Executed" />
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Gesture="Ctrl+S" Command="SaveAs" />
    </Window.InputBindings>
    <DockPanel>
        <Menu Height="22" Margin="2" DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <MenuItem Command="New" />
                <MenuItem Command="Open" />
                <MenuItem Command="SaveAs" InputGestureText="Ctrl+S" />
                <Separator />
                <MenuItem Name="mnuFileExit" Header="_Exit"
                    Click="mnuFileExit_Click"  />
            </MenuItem>
        </Menu>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
            <RadioButton Content="Draw" Margin="5" Name="radDraw"
                IsChecked="True" />
            <RadioButton Content="Edit" Margin="5" Name="radEdit"
                Click="radEdit_Click" />
        </StackPanel>
        <Border DockPanel.Dock="Bottom" Name="border1"
            Margin="10,0,10,10" BorderBrush="Gray" BorderThickness="1">
            <Canvas Name="canDraw" Background="Transparent"
                ClipToBounds="True"
                MouseDown="canDraw_MouseDown"
                MouseMove="canDraw_MouseMove"
                MouseUp="canDraw_MouseUp"/>
        </Border>
    </DockPanel>
</Window>

For the moment, ignore the Window.CommandBindings and Window.InputBindings sections. After those, the XAML code defines a DockPanel. The DockPanel contains a Menu that holds several MenuItem objects. Notice that the Menu is docked to the top of the DockPanel.

Below the menu, the DockPanel holds a StackPanel that contains the Draw and Edit radio buttons. The StackPanel is also docked to the top of the DockPanel, so it occupies the top of the panel’s area that is not used by the Menu.

The last control inside the DockPanel is the Canvas control on which the user draws. (See the earlier post for information about how the program uses this control.) Notice that the Canvas control is not docked. You can dock it if you like, but by default the DockPanel control’s final child fills any remaining space inside the panel.

Standard Commands

WPF uses a special commanding system to process user commands. The idea is that several different actions such as menus, shortcuts, and buttons might all trigger the same actions. The program can bind all of those controls to a single command object that then executes your code when it is invoked.

This follows the common WPF theme of “Twice as flexible and only five times as hard!” In this instance, however, this is only about three times as hard as it is in Windows Forms applications, at least for the standard commands.

You can make your own commands (back to “only fives times as hard”), but WPF provides a set of standard commands for common tasks such as Open, Copy, AlignCenter, and Zoom. Those commands are a bit easier to use.

Using the standard commands requires four pieces: creating command bindings, creating input bindings, creating controls that use the commands, and executing the commands.

Creating Command Bindings

Command bindings tell a control how to map commands to methods in the code behind. This example’s window uses the following command binding section.

<Window.CommandBindings>
    <CommandBinding Command="New"
        CanExecute="NewCommand_CanExecute"
        Executed="NewCommand_Executed" />
    <CommandBinding Command="Open"
        CanExecute="OpenCommand_CanExecute"
        Executed="OpenCommand_Executed" />
    <CommandBinding Command="SaveAs"
        CanExecute="SaveAsCommand_CanExecute"
        Executed="SaveAsCommand_Executed" />
</Window.CommandBindings>

Each command binding has a command such as New or Open. Each binding identifies two methods for its command, one method to execute to determine whether the command should be enabled and one to execute to perform the command. For example, the program calls the NewCommand_CanExecute method to determine whether the New command should be allowed. It calls the NewCommand_Executed method when the user executes the command.

If the NewCommand_CanExecute method returns false (through its parameters; you’ll see that shortly), then the program automatically disables the controls that are bound to the command. In this example, it would disable the New menu item and the Ctrl+N shortcut.

At this point, the program knows what methods to execute for the New, Open, and SaveAs commands. Next we need to assign controls to trigger those commands.

Creating Input Bindings

Input bindings let you define gestures to fire commands. This example uses the following code to make input bindings.

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+S" Command="SaveAs" />
</Window.InputBindings>

This code makes the gesture Ctrl+S fire the SaveAs command.

The program doesn’t need input bindings for the New and Open commands because they are automatically bound to the Ctrl+N and Ctrl+O shortcuts.

Creating Controls That Use the Commands

To make a control trigger a command, you set the control’s Command property. The following code shows how this example attaches commands to menu items.

<MenuItem Header="_File">
    <MenuItem Command="New" />
    <MenuItem Command="Open" />
    <MenuItem Command="SaveAs" InputGestureText="Ctrl+S" />
    <Separator />
    <MenuItem Name="mnuFileExit" Header="_Exit"
        Click="mnuFileExit_Click"  />
</MenuItem>

[polygons]

Notice that this XAML code does not explicitly assign text for the menu items. These standard commands come with standard text and shortcuts. For example, a menu item attached to the New command automatically displays the text New and its shortcut Ctrl+N as shown in the picture on the right.

You can override a menu’s text by setting its Header property. It’s somewhat strange that the default commands do not define accelerator keys to let you navigate menus by pressing the Alt key. You can add that capability by including the Header property and adding the accelerators as in Header="_New". (I didn’t do that for this example.)

The SaveAs command does not have a pre-defined shortcut, so the program’s XAML code for that menu item explicitly gives it the input gesture Ctrl+S. This makes the menu item display Ctrl+S as shown in the earlier picture.

The InputGestureText property makes the menu item display the shortcut text Ctrl+S, and the KeyBinding in the Windows.InputBindings section described earlier makes Ctrl+S fire the SaveAs command, but those are two completely separate things. If you omit the input gesture, then Ctrl+S will still fire the action, it just won’t appear in the menu item. Conversely if you omit the input gesture, then the menu item will display the shortcut Ctrl+S, but that key sequence won’t do anything. Both off those situations could be confusing to the user, so I suggest that you do both or neither.

Now the program knows what methods to execute when a command fires and it has menus and gestures to fire those commands. The final piece to the example’s basic commanding is the actual code that the commands execute.

Executing the Commands

The methods that the commands execute are relatively straightforward. The following code shows the methods for the New command.

private void NewCommand_CanExecute(object sender,
    CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

private void NewCommand_Executed(object sender,
    ExecutedRoutedEventArgs e)
{
    canDraw.Children.Clear();
}

The CanExecute method sets its e.CanExecute parameter to indicate whether the command should be allowed. The program calls this method whenever it might need to enable or disable the controls that are attached to the command. This example’s CanExecute methods simply return true so the commands are always allowed.

The Executed method does whatever is necessary for the command. The New command simply clears the canDraw Canvas control’s Children collection to remove any previously drawn polygons.

The following section describes the other commands’ Execute methods.

Saving and Loading Polygons

The main new feature of this example is the ability to save and restore drawn polygons. Normally we might like to serialize and deserialize the program’s Canvas control or perhaps its Children collection. Unfortunately the Canvas class is not marked as serializable. Fortunately WPF provides us a relatively simple alternative: the XamlWriter and XamlReader classes. The XamlWriter class can generate XAML code that creates a given WPF control. Conversely the XamlReader class can read XAML code and use it to create a control.

The following code executes when the SaveAs command fires.

SaveFileDialog sfdSerialization = new SaveFileDialog();
private void SaveAsCommand_Executed(object sender,
    ExecutedRoutedEventArgs e)
{
    sfdSerialization.Filter =
        "DRW Files (*.drw)|*.drw|All files (*.*)|*.*";
    sfdSerialization.DefaultExt = "drw";
    if (sfdSerialization.ShowDialog().Value)
    {
        string xaml = XamlWriter.Save(canDraw);
        File.WriteAllText(sfdSerialization.FileName, xaml);
    }
}

This code creates a SaveFileDialog at the form level. The SaveAsCommand_Executed method sets the dialog’s filter and default extension, and then displays the dialog.

If the user selects a file and clicks Save, the dialog returns true. It returns the value through a nullable boolean, so we must use its Value property to get the actual result.

If the dialog returns true, the program uses the XamlWriter class’s static Save method to get XAML code for the canDraw Canvas control. It then uses File.WriteAllText to save the XAML code into the file that the user selected.

The following text shows what this file might look like.

<Canvas Background="#00FFFFFF" Name="canDraw" Cursor="{x:Null}" Cli
pToBounds="True" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/p
resentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
;<Polygon Points="142,32.04 70,127.04 220,131.04 99,39.04 116,157.0
4 220,56.04 58,81.04 178,161.04" Stroke="#FF0000FF" StrokeThickness="2
" /><Polygon Points="77,21.04 18,118.04 92,181.04 241,172.04 255
,68.04 161,10.04" Stroke="#FF0000FF" StrokeThickness="2" /></Can
vas>

The code is written all on one line so it’s kind of hard to read, but if you look closely you can see the polygon’s points and stroke properties.

The following code executes when the Open command fires.

OpenFileDialog ofdSerialization = new OpenFileDialog();
private void OpenCommand_Executed(object sender,
    ExecutedRoutedEventArgs e)
{
    ofdSerialization.Filter =
        "DRW Files (*.drw)|*.drw|All files (*.*)|*.*";
    ofdSerialization.DefaultExt = "drw";
    if (ofdSerialization.ShowDialog().Value)
    {
        string xaml = File.ReadAllText(ofdSerialization.FileName);
        canDraw = (Canvas)XamlReader.Parse(xaml);
        border1.Child = canDraw;

        canDraw.MouseMove += canDraw_MouseMove;
        canDraw.MouseDown += canDraw_MouseDown;
        canDraw.MouseUp += canDraw_MouseUp;
    }
}

This code creates an OpenFileDialog at the form level. The OpenCommand_Executed method displays the dialog. If the user selects a file and clicks Open, the program uses File.ReadAllText to read the XAML code from the selected file. It then uses the XamlReader class’s static Parse method to convert the XAML code into a Canvas control and saves it in the variable canDraw.

Note that the canDraw variable is the one created by the Window Designer generated code. That’s just the name of the control originally defined by the window’s XAML code.

The program places the new control inside the window’s Border control border1.

The window’s original XAML code wired the canDraw control up to some event handlers, so the program must do the same for the newly created canDraw control or else the user will no longer be able to create and edit polygons.

Refining CanExecute

At this point, the program can handle the New, Open, and SaveAs commands, more or less. Unfortunately the new commands cause some unexpected results if you invoke them while you are in the middle of editing a polygon. For example, suppose you are creating a new polygon, you click three points, and then you press Ctrl+N. At that point program clears the canDraw control’s Children collection so the polygon that you are drawing is no longer visible, but the control still has a mouse capture and the program is still tracking mouse clicks to finish drawing the polygon. It seems as if nothing is happening until you press the right mouse button to finish creating the new polygon and that polygon suddenly appears.

Similarly if you press Ctrl+O while drawing a new polygon, you can load a file but you’re still drawing. Or is you press Ctrl+S while creating a new polygon, the saved file will contain the partially drawn polygon.

Yet another weird bug happens if you’re in the middle of editing a polygon when you invoke one of the commands. When you’re editing a polygon, the canDraw has captured mouse events. If you load a saved file while you are editing, the capture remains with the old version of the canDraw control. If you click on the new control, you can remove the capture, but it’s somewhat confusing.

To avoid all of these strange effects, you can modify the CanExecute methods. Instead of always returning true, you can make those methods call a new CommandsSafe method that can look more closely at what’s happening to decide if it’s safe to execute the commands. The following code shows the new version of the New command’s CanExecute method.

private void NewCommand_CanExecute(object sender,
    CanExecuteRoutedEventArgs e)
{
    e.CanExecute = CommandsSafe(true);
}

The following code shows the CommandsSafe method.

// Return true if the New, SaveAs, and Open
// commands should be allowed.
private bool CommandsSafe(bool require_polygons)
{
    if (NewPolyline != null) return false;
    if (EditPolygon != null) return false;
    if (require_polygons) return canDraw.Children.Count > 0;
    return true;
}

If NewPolygon is not null, then the user is creating a new polygon so the method returns false. Similarly if EditPolygon is not null, then the user is editing a polygon so the method also returns false.

If the require_polygons parameters is true, then the method returns true if the user has drawn at least one polygon and it returns false otherwise. The New and Save commands’ CenExecute methods pass the value true into the method, so the program does not enable those commands unless the user has created at least one polygon. (There’s no need to create a new drawing if the drawing is empty, and there’s no reason to save a blank drawing.)

Finally, if require_polygons is not true, the method returns true.

A Quick Software Engineering Note

You could use a different approach to resolve the issue of the user invoking New, Open, or SaveAs while drawing or editing a polygon. For example, you could make the commands’ Executed methods finalize the new polygon or stop editing before performing their main tasks. That would work, but I thought it would be more confusing.

In general programs that have complex states often have this kind pf problem. The program is in one state (such as creating a new polygon) and the user does something that should move it into a new state (such as pressing Ctrl+S). When that happens, the program must either finish the first state or refuse to move into the new state.

Another example that is common in data-intensive applications occurs when the user is editing text. For example, suppose the user is typing some text in some sort of data grid control and then presses Ctrl+S. Should the text that has been typed so far be committed to the database? Or should the edit be discarded?

To make handling this kind of transition easier, you should consider providing methods to commit or cancel changes. For example, the DataGrid and DataGridView controls both have an EndEdit method that finalizes the input and stops editing. In this posts, the example simply doesn’t allow the user to perform an action that causes this kind of transition.

Conclusion

This is a faily long post to described a relatively simple change to the previous example. The XamlReader and XamlWriter classes make converting controls to and from XAML code easy. The saving and restoring that code in text file is just as easy.

It’s the commanding code that takes some explaining. The following list shows the steps. There are several, but individually they’re not too hard.

  • Use command bindings to bind command objects to the methods that they should execute.
  • Use input bindings to bind input gestures such as Ctrl+S to commands.
  • Make menu items.
    • Use the Command property to indicate the command the item should invoke.
    • Use the InputGestureText property to make the menu item display an input gesture such as Ctrl+S.
    • Use the Header property to change the item’s caption if necessary, possibly to give it an accelerator.
  • Implement the commands’ CanExecute and Executed methods.

As an exercise, try adding New, Open, and Save As buttons to the program’s StackPanel. Bind them to the corresponding command objects and watch how the program enables and disables them as needed.

Download the example program to see additional details. For information about how the program lets the user draw and edit polygons, see the previous example.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Pingback: Code Project post “Clipping Plane in WPF 3D”

[WPF 3D]

The Code Project post Clipping Plane in WPF 3D by charles922 references a couple of my WPF 3D posts. His post explains several WPF 3D concepts including;

[WPF 3D]

To see Charles’ post, click here.

For LOTS more information on three-dimensional graphics in WPF, see my book WPF 3d,
Three-Dimensional Graphics with WPF and C#
.



Follow me on Twitter   RSS feed   Donate




Posted in 3D, 3D graphics, algorithms, books, drawing, graphics, mathematics, wpf, XAML | Tagged , , , , , , , , , , , , , , | Leave a comment

Sprialize an image in C#

[sprialize]

This example shows how to spiralize an image. By spiralize I mean shade pieces of the picture with different colors so the colored areas for a spiral as shown in the picture.

This could be a difficult problem. Fortunately I’ve already posted examples that show how to perform the more difficult pieces, so this program is mostly a matter of reusing those techniques in new ways.

Overview

At a high level, the basic approach to spiralize an image is relatively simple.

  1. Generate the points needed to define the sections of the spiral
  2. For each section of the spiral:
    1. Make a colorized version of the original image
    2. Use the colorized image to make a TextureBrush and use the brush to fill the spiral section

I covered the hardest parts of this process in earlier posts, but there is still a fair amount of work to do.

Code

To spiralize an image, the program uses following SpiralizeImage method.

// Spiralize the image.
private Bitmap SpiralizeImage(Bitmap original_bm, Color[] colors,
    float A, float color_scale, float opacity,
    bool outline_spirals, bool make_elliptical)
{
    int wid = original_bm.Width;
    int hgt = original_bm.Height;
    Bitmap bm = new Bitmap(wid, hgt);
    using (Graphics gr = Graphics.FromImage(bm))
    {
        gr.Clear(picSpiral.BackColor);
        gr.SmoothingMode = SmoothingMode.AntiAlias;

        int num_spirals = colors.Length;

        // Angular spacing between different spirals.
        float d_start = (float)(2 * Math.PI / num_spirals);

        // The angle where the next spiral starts.
        float start_angle = 0;

        // Center point.
        PointF center = new PointF(wid / 2, hgt / 2);

        // Draw on the entire image.
        Rectangle rect = new Rectangle(0, 0, wid, hgt);

        // Find the maximum distance to the rectangle's corners.
        float max_dist = Distance(center, rect);

        // Calculate the maximum theta value that we need to go to.
        float max_theta = max_dist / A + 2 * (float)Math.PI;

        // Get points defining the spirals.
        List<List<PointF>> spiral_points = new List<List<PointF>>();

        // Get the spirals' points.
        for (int i = 0; i <= num_spirals; i++)
        {
            spiral_points.Add(GetSpiralPoints(
                center, A, start_angle, max_theta));
            start_angle += d_start;
        }

        // Fill the areas between the spirals.
        for (int i = 0; i < num_spirals; i++)
        {
            // Make a list holding the next spiral's points.
            List<PointF> points = new List<PointF>(spiral_points[i]);

            // Add the following spiral's points reversed.
            List<PointF> points2 =
                new List<PointF>(spiral_points[i + 1]);
            points2.Reverse();
            points.AddRange(points2);

            // Make a colorized version of the image.
            using (Bitmap colorized_bm =
                ColorizeImage(OriginalBm, Colors[i], color_scale))
            {
                // Fill the spiral.
                using (TextureBrush brush =
                    new TextureBrush(colorized_bm))
                {
                    gr.FillPolygon(brush, points.ToArray());
                }
            }

            // Optional: Outline the spiral's polygon.
            if (outline_spirals)
                gr.DrawLines(Pens.Black, points.ToArray());
        }
    }

    // Combine the new image with the original one.
    Bitmap new_bm = (Bitmap)original_bm.Clone();
    using (Graphics gr = Graphics.FromImage(new_bm))
    {
        // Make a ColorMatrix with the opacity.
        ColorMatrix color_matrix = new ColorMatrix();
        color_matrix.Matrix33 = opacity;

        // Make the ImageAttributes object.
        ImageAttributes attributes = new ImageAttributes();
        attributes.SetColorMatrix(color_matrix,
            ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

        // Draw the input bitmap onto the Graphics object.
        Rectangle rect = new Rectangle(0, 0,
            new_bm.Width, new_bm.Height);

        gr.DrawImage(bm, rect,
            0, 0, new_bm.Width, new_bm.Height,
            GraphicsUnit.Pixel, attributes);
    }

    // Make it elliptical if desired.
    if (make_elliptical) new_bm = MakeElliptical(new_bm);

    return new_bm;
}

This method creates a new bitmap with the same size as the original and makes an associated Graphics object.

Next the code uses the number of colors that we should use to calculate the angular spacing between the different spiral sections. It calculates some drawing parameters such as the rectangle that contains the image and the center of the image. It then finds the distance from the center of the rectangle to its corners so we know how far to draw the spirals to spiralize the entire image. It also calculates the largest angle that we need to use when building the spirals to cover the image. Those calculations are described in the post Draw a filled spiral in C#.

After all of this setup, the program creates a list of lists of points to hold the points that define each of the spiral sections. It loops through the spirals and calls the GetSpiralPoints method to find each spiral’s points. See the post Draw an Archimedes spiral in C# for information on how that method works.

After it has found each spiral’s points, the code loops through the spirals.

At this point, each of the spirals includes points moving from the center outward. To fill the strip between the spiral arms, the program uses a technique described in the post Draw a filled spiral in C#. It first copies the current spiral’s points into a list named points. Then it gets the following spiral’s points, reverses them, and adds them to the points list.

The result is a list of points that goes out from the center along one spiral arm and then returns on the next spiral arm. This defines a polygon that we can fill to fill the spiral section. That’s the technique used by the post Draw a filled spiral in C#.

Next the program calls the ColorizeImage method (described later) to make a colorized version of the original image. It then makes a TextureBrush from the colorized image. If you fill a shape with this brush, the shape is filled with the corresponding parts of the colorized image. This example uses the brush to fill the current spiral section.

If the program’s Outline Spirals checkbox is checked, the code also draws the spiral section’s lines to outline that section.

After it has finished filling all of the spiral sections, I found that the result sometimes overwhelmed the original picture so it was hard to see. You could adjust the section drawing so the program drew each section with reduced opacity, but I took a simpler approach. After it creates the colorized image, the program copies the whole thing onto the original image with a reduce opacity. In the picture at the top of this post, for example, the program has copied the colorized image at 50% opacity onto the original image.

To copy the colorized image onto the original, the program makes a new bitmap to hold the result. It then creates a ColorMatrix and sets its opacity entry to the desired opacity. It makes an ImageAttributes object to use the ColorMatrix and then draws the colorized image on top of the copied original.

Finally, if the Elliptical checkbox is checked, the program calls the MakeElliptical method described later to make the result elliptical. The method finishes by returning the result image.

ColorizeImage

The ColorizeImage method uses techniques described in the post Rainbowize an image in C#. I simply moved the key code into a new method to make it easier to reuse.

The following code shows the ColorizeImage method.

// Return a colorized image.
// See http://csharphelper.com/blog/2014/10/rainbowize-an-image-in-c/
private Bitmap ColorizeImage(Bitmap original_bm,
    Color color, float color_scale)
{
    // Create a ColorMatrix for this color.
    ColorMatrix cm = new ColorMatrix(new float[][]
    {
        new float[] {color.R / 255f * color_scale, 0, 0, 0, 0},
        new float[] {0, color.G / 255f * color_scale, 0, 0, 0},
        new float[] {0, 0, color.B / 255f * color_scale, 0, 0},
        new float[] {0, 0, 0, 1, 0},
        new float[] {0, 0, 0, 0, 1},
    });

    // Make the result bitmap.
    int wid = original_bm.Width;
    int hgt = original_bm.Height;
    Bitmap result_bm = new Bitmap(wid, hgt);

    // Color the image.
    using (ImageAttributes attr = new ImageAttributes())
    {
        attr.SetColorMatrix(cm);

        // Copy the original image onto the result bitmap.
        using (Graphics gr = Graphics.FromImage(result_bm))
        {
            Point[] dest_points =
        {
            new Point(0, 0),
            new Point(wid, 0),
            new Point(0, hgt),
        };
            Rectangle source_rect = new Rectangle(0, 0, wid, hgt);
            gr.DrawImage(original_bm, dest_points,
                source_rect, GraphicsUnit.Pixel, attr);
        }
    }

    return result_bm;
}

This method takes as a parameter a color that should be the basis for the colorization. The ColorMatrix multiplies the red, green, and blue components of each pixel by a scale factor that depends on the color and the color_scale parameter.

For example, if the color is red and color_scale is 2, then the first entry in the ColorMatrix is 255 / 255 * 2 = 2. That means the matrix will double the amount of red in each pixel. Because the color red has green and blue components equal to zero, the other scaling factors in the matrix will be 0 / 255 * 2 = 0 and the matrix will remove the pixels’ green and blue components.

After it has created the ColorMatrix, the method creates result bitmap and an ImageAttributes object to apply the colors. It then copies the original image onto the result bitmap using the ImageAttributes object and returns the result.

See the post Rainbowize an image in C# to see additional details.

MakeElliptical

The MakeElliptical method shown in the following code makes an image elliptical.

// Make the image elliptical.
private Bitmap MakeElliptical(Bitmap bm)
{
    Bitmap new_bm = (Bitmap)bm.Clone();
    using (Graphics gr = Graphics.FromImage(new_bm))
    {
        gr.Clear(Color.Transparent);
        using (TextureBrush brush = new TextureBrush(bm))
        {
            Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
            gr.FillEllipse(brush, rect);
        }
    }
    return new_bm;
}

This method is relatively simple. It creates a new bitmap and clears it with the color Transparent. It then uses the original image to make a TextureBrush and uses it to fill an ellipse that covers the result bitmap. The result is a bitmap with transparent corners and that is filled with an ellipse containing whatever was in the corresponding parts of the original image as shown in the picture at the top of the post.

Conclusion

The basic idea behind this example is to fill polygons with colorized versions of the original image. This example filled spiral sections to create a spiralized image, but you could use other polygons. For example, you could cover the image with rectangles, ellipses, or puzzle piece shapes.

Download the example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




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