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.
// 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.
// 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.
// 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;
// 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.
// 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 DrawTextThe 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.
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.)
// 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. 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.