Title: Label images at the bottom in C#
The second step in building a Hiragana flash card program is to label images so you can understand what they mean. My last post in the series, Split images into halves in C#, showed how to split an image into two pieces and remove whitespace from around the pieces. This example places a label at the bottom of the image.
Using the Program
To use the program, enter the From Directory where the unlabeled images are and click List to make the program list the image files on the left.
Enter a destination directory where the labeled images should be placed in the To Directory text box.
When you click an image file's name in the list, the program displays the image at the bottom.
It's actually not hard to label images. The program is complicated quite a bit because it allows several (admittedly not completely necessary) options. To change the label's font, click the ellipsis to the right of the alphabet sample text to display the following font selection dialog.
If you select a font and click OK, the program updates its sample font. Notice that the font selection dialog includes a color dropdown that lets you pick from a palette of standard colors. If you select one of the colors, the program uses it for its sample text.
If you click the BG Color sample, the program displays a color selection dialog where you can pick the label area's background color.
Enter the desired message height in the Msg Hgt text box. This tells the program how tall to make the area where the label is drawn. You may need to adjust this height depending on the size of the font that you selected with the font selection dialog.
Use the Alignment radio buttons to determine how the label is aligned. You can align it in the label area's top left, top middle, bottom right, etc.
Normally you will want to set the label area height, font, font color, background color, and alignment once and then process several pictures.
Click on the file list to load an image, enter its caption in the Message text box, and click Draw to display the result. If you like it, click Save to save the result. The program gives the saved file the same name as the original file but in the To Directory.(It also beeps so you know that it did something.)
The following sections describe the program's most interesting pieces of code.
The following ListFiles method builds the list of image files in the program's list box.
private void ListFiles()
DirectoryInfo dir_info = new DirectoryInfo(txtFromDir.Text);
foreach (FileInfo file_info in dir_info.GetFiles())
string extension = file_info.Extension.ToLower();
if ((extension == ".png") ||
(extension == ".jpg") ||
(extension == ".gif") ||
(extension == ".tiff") ||
(extension == ".jpeg"))
The method first clears the lstFiles ListBox control. It then creates a DirectoryInfo object for the directory named in the txtFromDir TextBox.
The code then calls the DirectoryInfo object's GetFiles method to list the files in the directory and loops through those files.
If a file's extension is png, jpg, gif, tiff, or jpeg, the program adds its FileInfo object to the list box. The FileInfo class's ToString method returns the file's name without its directory path. The ListBox control uses the ToString methods of the objects that it contains to decide what to display, so the ListBox displays the files' names without the directory path.
Clicking on a File
When you click on an entry in the file list, the following code executes.
private void lstFiles_SelectedIndexChanged(object sender, EventArgs e)
FileInfo file_info = lstFiles.SelectedItem as FileInfo;
picImage.Image = LoadBitmapUnlocked(file_info.FullName);
The ListBox control stores its items as the generic object type. The event handler converts the selected object into a FileInfo object. It uses that object's FullName property to get the file's name including path, and calls the LoadBitmapUnlocked method to read the file. It displays the result in the picImage picture box.
For information on the LoadBitmapUnlocked method, see the post Load images without locking their files in C#.
Drawing the Label
When you click the Draw button, the program uses the following code to create the labeled image.
private void btnDraw_Click(object sender, EventArgs e)
// Add the label to the image.
private void ApplyLabel()
FileInfo file_info = lstFiles.SelectedItem as FileInfo;
if (file_info == null) return;
using (Bitmap bm = LoadBitmapUnlocked(file_info.FullName))
picImage.Image = LabelBitmap(bm);
The Draw button's Click event handler simply calls the ApplyLabel method.
The ApplyLabel method converts the list box's selected item into a FileInfo object and returns if that object does not exist.
If the FileInfo object exists, the program loads its file into a bitmap. Notice that the code uses that object in a using block so it is automatically disposed when the block ends.
Inside the block, the code calls the LabelBitmap method described next to draw the label onto a new bitmap. It is important that the method returns a new bitmap because the using block must not dispose of a bitmap while the program still needs to use it. The program displays the labeled bitmap in the picImage PictureBox.
The following LabelBitmap method adds a label to a bitmap and returns the result in a new bitmap.
// Add the label to the image.
private Bitmap LabelBitmap(Bitmap bm)
int label_height = 0;
label_height = int.Parse(txtMargin.Text);
catch (Exception ex)
Bitmap new_bm = new Bitmap(bm.Width, bm.Height + label_height);
Rectangle rect = new Rectangle(
0, bm.Height, bm.Width, label_height);
using (StringFormat sf = GetStringFormat())
using (Graphics gr = Graphics.FromImage(new_bm))
gr.DrawImage(bm, 0, 0);
using (SolidBrush brush = new SolidBrush(lblSample.BackColor))
brush.Color = lblSample.ForeColor;
brush, rect, sf);
The method first parses the label area's desired height. It then makes a new bitmap with extra space added on the bottom for the label.
Next the code creates a rectangle representing the label area.
The program then uses the GetStringFormat method to get a StringFormat object that correctly aligns the message text. I'll describe that method shortly. The program also makes a Graphics object associated with the new bitmap.
At this point, the code is finally ready to start drawing. It first draws the original image at the origin of the new bitmap. It then uses the background color that you selected to create a brush and fills the label area with that color.
Next the method changes the brush's color to the selected label color. It then draws the message text into the label area's rectangle using the StringFormat for alignment.
Note that the DrawString method will wrap the text across multiple lines in the label area if it won't fit on one line.
The following code shows the GetStringFormat method.
private StringFormat GetStringFormat()
StringFormat sf = new StringFormat();
if (radUL.Checked || radUM.Checked || radUR.Checked)
sf.LineAlignment = StringAlignment.Near;
else if (radML.Checked || radMM.Checked || radMR.Checked)
sf.LineAlignment = StringAlignment.Center;
else if (radLL.Checked || radLM.Checked || radLR.Checked)
sf.LineAlignment = StringAlignment.Far;
if (radUL.Checked || radML.Checked || radLL.Checked)
sf.Alignment = StringAlignment.Near;
else if (radUM.Checked || radMM.Checked || radLM.Checked)
sf.Alignment = StringAlignment.Center;
else if (radUR.Checked || radMR.Checked || radLR.Checked)
sf.Alignment = StringAlignment.Far;
This method creates a new StringFormat object. That object has two key alignment properties. The Alignment property determines how text is aligned horizontally (left, center, or right). The LineAlignment property determines how the text is aligned vertically (top, middle, or bottom).
The only weird thing about those properties is that they take values Near, Center, or Far to indicated top/left, center/middle, right/bottom.
This method simply examines the program's radio buttons to see which is checked, sets the properties appropriately, and returns the StringFormat object.
That concludes the most interesting pieces of code. There are still a few details such as how the program displays the font and color selection dialogs, and how it saves the resulting images. Download the example to see those details and to experiment with the program.
There are many ways that you could enhance this program. For example, you could position the label area in other parts of the image such as 8 pixels from the upper right corner. You could also allow more complicated label backgrounds such as a semi-transparent background placed on top of the image or a gradient background. Handling all of the possibilities would require you to basically write a full drawing program. If you want to try some specific modifications, however, most shouldn't be too hard.
The previous post explained how to split Hiragana images into left and right pieces. This post explains how to label the right image. My next post will describe the final Hiragana flash card program that uses those images.
Download the example to experiment with it and to see additional details.