BLOG.CSHARPHELPER.COM

Compare floating-point values safely in C#

The computer stores values in binary using 0s and 1s. That means it cannot store all possible decimal values exactly. Sometimes when you multiply or divide two floating-point values, the computer may be unable to store the result exactly.

Usually this is no big deal, but you should be aware that this can happen. In particular, when you compare two values to see if they are equal, you cannot simply use == to compare them. Instead, subtract them and see if the result is close to 0.

For example, consider the following calculation.

float A = 5.7f;
float B = 12f;
float A_times_B = A * B;
txtAtimesB.Text = A_times_B.ToString();

The value A_times_B should be 68.4 but, due to the way the computer stores floating-point values, the result is not exact. If a program uses == to compare the result to 68.4, the result is false.

Instead you should subtract the result from the value 68.4 and see if the difference is (in absolute value) close to 0.

This example uses the following code to demonstrate this technique.

float A = 5.7f;
float B = 12f;
float A_times_B = A * B;
txtAtimesB.Text = A_times_B.ToString();

bool equals1 = (A_times_B == 68.4f);
txtEquals1.Text = equals1.ToString();

bool equals2 = Math.Abs(A_times_B - 68.4f) < 0.00001;
txtEquals2.Text = equals2.ToString();

If you look at the picture at the beginning of the post, you'll see that the computer displays the product as 68.39999 instead of 68.4. (Even this may be slightly different from the way the computer stores the value internally. This is just the decimal representation of the binary value stored by the computer.)

The picture shows that the == test decided that the result was not equal to 68.4. The test Math.Abs(A_times_B - 68.4f) < 0.00001 correctly determined that the product was close to 68.4.

How close the difference must be to 0 depends on the calculation. In this example, 0.00001 works. If you perform a long series of calculations, rounding errors may accumulate and the result may be farther from what you expect it to be so you may need to use another value such as 0.001.

   

Select and print multiple files in C#

This example lets you select multiple files for printing. You can use it, for example, to print all of the .cs files in a project's directory.

Select a printer from the ComboBox at the top. Enter or select a directory and enter a file matching pattern. Then click List Files to see a list of files matching the pattern. Finally check the files you want to print and click Print.

This example uses techniques described in several previous posts.

The following code is the heart of the printing procedure.

// Print the selected files.
private void btnPrint_Click(object sender, EventArgs e)
{
    // Select the desired printer.
    pdocFile.PrinterSettings.PrinterName = cboPrinter.Text;

    // Print the checked files.
    foreach (string filename in clbFiles.CheckedItems)
    {
        Console.WriteLine("Printing: " + filename);

        // Get the file's name without the path.
        FileInfo file_into = new FileInfo(filename);
        string short_name = file_into.Name;

        // Set the PrintDocument's name for use by the printer queue.
        pdocFile.DocumentName = short_name;

        // Read the file's contents.
        try
        {
            FileContents = File.ReadAllText(filename).Trim();
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error reading file " + filename +
                ".\n" + ex.Message);
            return;
        }

        // Print.
        pdocFile.Print();
    }

    MessageBox.Show("Spooled " + clbFiles.CheckedItems.Count +
        " files for printing.");
}

The code sets the PrintDocument's PrinterSettings.PrinterName property so the PrintDocument sends output to the selected printer. It sets the document's DocumentName property to the file's name without the path so you can see the file's name in the printer's spool.

Next the code loops over the files that are checked in the CheckedListBox. For each checked file, the code stores the file's text in the variable FileContents. It then calls the PrintDocument's Print method to print the file. See the example Print a text file in C# for information on how the program prints the file.

After it has finished, the program displays a message telling you how many files it scheduled for printing.

   

Set a print job's document name in C#

By default, when you send a PrintDocument to a printer, the printer spool shows the job's name as "document." You can change that name by setting the PrintDocument's DocumentName property.
This example uses the following code to print a small sample document.

// Print the document.
private void btnPrint_Click(object sender, EventArgs e)
{
    // Select the printer.
    pdocFile.PrinterSettings.PrinterName = cboPrinter.Text;

    // Set the print document name.
    pdocFile.DocumentName = txtDocumentName.Text;

    // Print.
    pdocFile.Print();
}

The code sets the PrintDocument's PrinterSettings.PrinterName property so output goes to the printer you selected from the ComboBox at the top of the form.

It then sets the DocumentName property to the value you entered in the TextBox.

Finally the code calls the PrintDocument object's Print method to start printing.

If you click the Print button and then check the selected printer's spool (before the document has finished printing), you should see the document name you entered.

   

Display the number of items checked in a CheckedListBox in C#

The CheckedListBox control has a CheckedItems collection that holds references to the control's currently checked items. You can use the collection's Count property to determine the number of items checked.

When the user checks or unchecks an item, the control raises its ItemCheck event to let you know. Unfortunately the control's CheckedItems collection has not yet been updated so the collection's Count does not give you the value it will have after the item is checked or unchecked.

To display an accurate count, you need to look at the event handler's e.CurrentValue and e.NewValue parameters to determine whether the item should be included in the count.

When you check and uncheck items in this example, the following ItemCheck event handler displays the total number if items in the CheckedListBox and the number of items that are selected.

// Update the display of files checked.
private void clbFiles_ItemCheck(object sender, ItemCheckEventArgs e)
{
    // Get the current number checked.
    int num_checked = clbFiles.CheckedItems.Count;

    // See if the item is being checked or unchecked.
    if ((e.CurrentValue != CheckState.Checked) &&
        (e.NewValue == CheckState.Checked))
            num_checked++;
    if ((e.CurrentValue == CheckState.Checked) &&
        (e.NewValue != CheckState.Checked))
            num_checked--;

    // Display the count.
    lblCount.Text = clbFiles.Items.Count + " items, " +
        num_checked + " selected";
}

The code gets the current number of items checked. Then depending on the e.CurrentValue and e.NewValue parameters, it updates that count. The event handler finishes by displaying the total number of items and the number of checked items.

This example demonstrates one other useful technique for using CheckedListBoxes. Normally you must select an item in the list to give it focus and then click its box to check it. That means you need to click the item twice to check or uncheck it.

You can make this easier and less annoying if you set the control's CheckOnClick propertry to true. Then when the user clicks an item in the list, it is checked or unchecked.

This example's Form_Load event handler uses the following statement to set this property (or you can do it at design time).

clbFiles.CheckOnClick = true;

   

Print a text file in C#

This example lets you select a printer and file, and then print the file on the selected printer. (Actually this version just displays a print preview. To send the file directly to the selected printer, see the example Send a printout directly to a specific printer in C#.)
When the program starts, it executes the following initialization code.

// Populate the list of printers.
private void Form1_Load(object sender, EventArgs e)
{
    // Find all of the installed printers.
    foreach (string printer in PrinterSettings.InstalledPrinters)
    {
        cboPrinter.Items.Add(printer);
    }

    // Find and select the default printer.
    try
    {
        PrinterSettings settings = new PrinterSettings();
        cboPrinter.Text = settings.PrinterName;
    }
    catch
    {
    }

    // Initially select the source code file.
    string file_path = Application.StartupPath;
    if (file_path.EndsWith(@"\bin\Debug"))
        file_path = file_path.Substring(0, file_path.Length - 10);
    if (file_path.EndsWith(@"\bin\Release"))
        file_path = file_path.Substring(0, file_path.Length - 12);
    if (file_path.EndsWith(@"\"))
        file_path = file_path.Substring(0, file_path.Length - 1);
    file_path += @"\Form1.cs";
    txtFile.Text = file_path;
}

The code first initializes a ComboBox with the names of the available printers. It sets the ComboBox's initial selection to the default printer. (See Determine the default printer in C# for details.) It then copies name of the example's Form1.cs file into the File TextBox.

If you enter or select a file name and then click Preview, the following code displays a print preview for the file.

// The text contained in the file.
private string FileContents;

// Preview the selected file.
private void btnPreview_Click(object sender, EventArgs e)
{
    // Read the file's contents.
    try
    {
        FileContents = File.ReadAllText(txtFile.Text).Trim();
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error reading file " + txtFile.Text +
            ".\n" + ex.Message);
        return;
    }

    // Display the print preview dialog.
    ppdTextFile.ShowDialog();
}

This code uses the System.IO.File class's ReadAllText method to save the file's contents into the string FileContents. It then displays the PrintPreviewDialog named ppdTextFile.

At design time, I added the PrintPreviewDialog to the form. I also added a PrintDocument named pdocTextFile to the form and set the PrintPreviewDialog's Document property equal to pdocTextFile.

When the code calls the dialog's ShowDialog method, it asks pdocTextFile to generate the printout that it should display. The pdocTextFile component raises its PrintPage event to let the program create the printout. The following code shows the PrintPage event handler.

// Print a page of the text file.
private void pdocTextFile_PrintPage(object sender, PrintPageEventArgs e)
{
    // Make a font for printing.
    using (Font font = new Font("Courier New", 10))
    {
        // Make a StringFormat to align text normally.
        using (StringFormat string_format = new StringFormat())
        {
            // See how much of the remaining text will fit.
            SizeF layout_area = new SizeF(
                e.MarginBounds.Width, e.MarginBounds.Height);
            int chars_fitted, lines_filled;
            e.Graphics.MeasureString(FileContents, font,
                layout_area, string_format,
                out chars_fitted, out lines_filled);

            // Print as much as will fit.
            e.Graphics.DrawString(
                FileContents.Substring(0, chars_fitted),
                font, Brushes.Black, e.MarginBounds,
                string_format);

            // Remove the printed text from the string.
            FileContents = FileContents.Substring(chars_fitted).Trim();
        }
    }

    // See if we are done.
    e.HasMorePages = FileContents.Length > 0;
}

The event handler creates Font and StringFormat objects to use when printing. It then uses the techniques described in See how much text can fit in a rectangle in C# to see how much of the file's contents will fit within the margins of the printed page. The event handler draws that much text and removes the printed text from the FileContents variable.

If FileContents contains more text, the event handler sets e.HasMorePages to true to indicate that the PrintDocument should generate more pages of printout.

   

See how much text can fit in a rectangle in C#

This program draws as much of its text as possible the left PictureBox and draws the rest of the text in the right PictureBox. If you change the form's height, the PictureBoxes' heights change so the amount of text that fits in the left PictureBox changes.

The question is, "How does the program figure out how much text fits in the left PictureBox?" The answer is to use the Graphics object's MeasureString method.

The following code shows how the program draws the text in the two PictureBoxes. It looks complicated because there are a lot of details involving creating Bitmaps, making associated Graphics objects, creating StringFormat objects, and so forth. However, all of that is relatively straightforward. The most interesting part, which determines how much text will fit in the left PictureBox, is shown in red.

// Draw as much text as possible in picText1.
// Draw the remainder in picText2.
private void DrawText()
{
    string text = "    This is a fairly long piece of text ...";

    // Make a font for printing.
    using (Font font = new Font("Times New Roman", 12))
    {
        // Make a StringFormat to align text normally.
        using (StringFormat string_format = new StringFormat())
        {
            // Stop drawing at a word boundary.
            string_format.Trimming = StringTrimming.Word;

            // Make a Bitmap for picText1.
            Bitmap bm1 = new Bitmap(
                picText1.ClientSize.Width,
                picText1.ClientSize.Height);
            using (Graphics gr = Graphics.FromImage(bm1))
            {
                gr.Clear(picText1.BackColor);

                // Make a Rectangle representing where
                // the text should be drawn.
                const int margin = 5;
                Rectangle rect = new Rectangle(margin, margin,
                    picText1.ClientSize.Width - 2 * margin,
                    picText1.ClientSize.Height - 2 * margin);
                gr.DrawRectangle(Pens.LightGreen, rect);

                // See how much of the text will fit in picText1.
                int chars_fitted, lines_filled;
                SizeF avail_size = new SizeF(rect.Width, rect.Height);
                gr.MeasureString(text, font,
                    avail_size, string_format,
                    out chars_fitted, out lines_filled);

                // Draw the text that will fit.
                string text_that_fits = text.Substring(0, chars_fitted);
                gr.DrawString(text_that_fits, font,
                    Brushes.Black, rect, string_format);

                // Display the result.
                picText1.Image = bm1;

                // Remove the printed text from the total text.
                text = text.Substring(chars_fitted);
            } // End drawing on picText1

            // Draw the remaining text on picText2.
            // Make a Bitmap for picText2.
            Bitmap bm2 = new Bitmap(
                picText2.ClientSize.Width,
                picText2.ClientSize.Height);
            using (Graphics gr = Graphics.FromImage(bm2))
            {
                gr.Clear(picText2.BackColor);

                // Make a Rectangle representing where
                // the text should be drawn.
                const int margin = 5;
                Rectangle rect = new Rectangle(margin, margin,
                    picText2.ClientSize.Width - 2 * margin,
                    picText2.ClientSize.Height - 2 * margin);
                gr.DrawRectangle(Pens.LightGreen, rect);

                // Draw the text.
                gr.DrawString(text, font,
                    Brushes.Black, rect, string_format);
            } // End drawing on picText2

            // Display the result.
            picText2.Image = bm2;
        } // End using string_format
    } // End using font
} // End DrawText

The code starts by defining the text it will display. It then creates Font and StringFormat objects to use when drawing text.

The code sets the StringForm's object's Trimming property to Word so the text breaks at a word boundary if it doesn't fit in the left PictureBox. See the post Use the StringFormat class's line trimming values in C# for more information on string trimming.

Next the code makes a Bitmap and associated Graphics object to draw the left PictureBox's text. It makes a Rectangle representing the location where the text should be drawn and outlines it in light green so you can see it.

The code then uses the Graphics object's MeasureString method to determine how much of the text will fit in the allowed size with the given font. The code then copies that much of the text into the variable text_that_fits and draws the text.

Next the program removes the text that it has already drawn from the string text. It then draws the remainder of the text in the right PictureBox.

   

Determine the default printer in C#

When this example starts, the following event handler fills the cboPrinter ComboBox with the names of the available printers and then selects the default printer.

private void Form1_Load(object sender, EventArgs e)
{
    // Find all of the installed printers.
    foreach (string printer in PrinterSettings.InstalledPrinters)
    {
        cboPrinter.Items.Add(printer);
    }

    // Find and select the default printer.
    try
    {
        PrinterSettings settings = new PrinterSettings();
        cboPrinter.Text = settings.PrinterName;
    }
    catch
    {
    }
}

The code first loops through the PrinterSettings.InstalledPrinters string collection and adds the printers to the cboPrinter ComboBox. (PrinterSettings is defined in the System.Drawing.Printing namespace.)

Next the code gets an object representing the PrinterSettings. That object is initialized with default printing values. In particular, its PrinterName property gives the name of the default printer. The code sets the ComboBox's Text property to that printer's name.

There are two weird things here. First, the PrinterSettings class's InstalledPrinters property is a static (shared) property so you need to use the class's name to use it. You cannot creates the settings object and then use its InstalledPrinters property.

Second, properties such as the PrinterName property are instance values so you need to create an instance of the PrinterSettings class to use them.

(It would have been more consistent if all of the values were either static or instance values. I suspect Microsoft did it this way because a program can set the instance values to change the defaults but it cannot set values in the InstalledPrinters collection to change the available printers. I guess that makes sense. Static values are fixed and instance values are changeable.)

   

Resize a RichTextBox to fit its contents in C#

The example Resize a TextBox to fit its text in C# shows how to make a TextBox fit its text. That example uses a TextRenderer to determine how big the control's text will be when it is drawn on the screen and then resizes the control accordingly.

That approach won't work with a RichTextBox because, unlike a TextBox, a RichTextBox can contain text in different fonts, text that is aligned in various ways, and even pictures.

When this example starts, the following code executes to prepare the RichTextBox.

// Don't let the RichTextBox wrap long lines.
private void Form1_Load(object sender, EventArgs e)
{
    rchContents.WordWrap = false;
    rchContents.ScrollBars = RichTextBoxScrollBars.None;
}

This code sets the control's WordWrap property to false so the control doesn't try to wrap long lines of text to fit its current size. If you allow the control to wrap text, you get strange results where the control wraps text and then tries to make itself narrower. If you add a bunch of text, the control eventually makes itself extremely tall and thin. There may be a way to make this work but it doesn't seem useful.

Next this code sets the control's ScrollBars property to None. If you don't do this, the control may display scroll bars when its size is too close to the size required by the contents. The scroll bars take up a lot of room (relatively speaking) so you can't see all of the contents. You can add some extra space to make sure the text fits (in fact, the code that follows does this to make things look nicer), but the scroll bars mess things up if they appear so you may as well turn them off.

When the contents of the control change and the new contents resize a different amount of space than the old contents, the RichTextBox raises its ContentsResized event and the following event handler executes.

// Make the RichTextBox fit its contents.
private void rchContents_ContentsResized(object sender, ContentsResizedEventArgs e)
{
    const int margin = 5;
    RichTextBox rch = sender as RichTextBox;
    rch.ClientSize = new Size(
        e.NewRectangle.Width + margin,
        e.NewRectangle.Height + margin);
}

This code uses the e.NewRectangle parameter to see how much space the new contents need. It adds a margin so things don't look too crowded and sets the control's ClientSize.

Run the program and type some text into the RichTextBox. You can also copy images or formatted text (such as the colored text displayed in Visual Studio's code editor) and paste them into the RichTextBox to see how it handles them.

   

Blank the WebBrowser control in C#

If a program displays a web page in a WebBrowser control and then that page become irrelevant to the program, you may want to blank the control. Doing that is as easy as making the control navigate to the special URL "about:blank." When you click this example's Blank button, the following code blanks the WebBrowser control.

// Blank the WebBrowser.
private void btnBlank_Click(object sender, EventArgs e)
{
    wbrCSharpHelper.Navigate("about:blank");
}

That's easy enough but there is a catch. The WebBrowser control takes some time to get ready to work and to load a web page. If you try to make it do something before it is ready, it sometimes fails. It seems to do okay with simple navigation, but actions such as trying to set the control's Document contents may cause trouble.

To avoid those kinds of errors, you should wait until the control's has finished loading whatever document it is loading before you do anything else. This probably isn't necessary in this example but the program does it for form's sake.

At design time, I disabled the Blank button. I also set the WebBrowser control's URL property to www.csharphelper.com/howto_index.html so it loads the C# Helper index page when it starts. When that page is finished loading, the control's DocumentCompleted event fires and the following event handler executes.

// Enable the Blank button.
private void wbrCSharpHelper_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    btnBlank.Enabled = true;
}

This code simply enables the Blank button so you can blank the control.

   

Make a scrolled window in C#

Over the years, I've posted literally thousands of tips, tricks, and examples on my VB Helper web site. I've posted many examples over such a long time that I don't remember what I've also posted to this site.

This is one of the examples that's so obvious that I didn't realize I hadn't posted it. It's obvious, at least, if you've seen it before. If you haven't seen it, you can spend a lot of time building your own scrolled window out of PictureBoxes, VerticalScrollBars, and HorizontalScrollBars.

Suppose you have something such as a large picture that doesn't fit nicely on a form. You might like to display it in a scrolled window so the user can scroll around to see different parts of the picture. You can accomplish that by using a nested pair of PictureBoxes and some scroll bars, but there's a much easier way.

Put the picture in a PictureBox. Then put the PictureBox inside a Panel control and set its AutoScroll property to true. Now the Panel control will automatically display scroll bars and arrange its contents when the user scrolls. You don't need to write any code and only need to set a single property: AutoScroll.

If the Panel control is anchored so it resizes when the form resizes, the control will even display and hide either or both of its scroll bars as needed. If the user makes the form big enough to display everything, the scroll bars disappear. If the user make the form small, the scroll bars appear and function appropriately.

The Panel control doesn't need to hold a single control as it does in this example. You can place any number of controls inside it and it will still let the user scroll to see them all. (If you have a lot of controls, it's sometimes easier work with them in the form designer if you place them in a single container such as a PictureBox and then place the PictureBox inside the Panel control, but that's not a requirement.)

   

Calendar

July 2013
SuMoTuWeThFrSa
123456
78910111213
14151617181920
21222324252627
28293031

Subscribe


Blog Software
Blog Software