Combine images in rows and columns in C#

[Combine images in rows and columns in C#]

I just finished my latest book WPF 3d, Three-Dimensional Graphics with WPF and C# and I wanted to combine images showing samples from the book into a single image. You can use a program such as MS Paint to do this manually, but it’s a lot of work. I wrote this example to make it easier to combine images in this way.

Enter the following values.

  • The source directory that contains the images that you want to combine
  • The output file name
  • The number of columns you want to use
  • A margin (pixels) to add space between the images
  • The background color (between the images)

You can click on the background color box to select a color.

After you enter the values, click Go to combine the images. The program gives each image the same amount of room. Each image’s position is as wide as the widest image and as tall as the tallest image.

The following code shows how the program makes the combined image.

private void btnGo_Click(object sender, EventArgs e)
{
    Cursor = Cursors.WaitCursor;

    // Get the picture files in the source directory.
    List<string> files = new List<string>();
    foreach (string filename in
        Directory.GetFiles(txtSourceDir.Text))
    {
        int pos = filename.LastIndexOf('.');
        string extension = filename.Substring(pos).ToLower();
        if ((extension == ".bmp") ||
            (extension == ".jpg") ||
            (extension == ".jpeg") ||
            (extension == ".png") ||
            (extension == ".tif") ||
            (extension == ".tiff") ||
            (extension == ".gif"))
                files.Add(filename);
    }

    int num_images = files.Count;
    if (num_images == 0)
    {
        Cursor = Cursors.Default;
        MessageBox.Show("Selected 0 files");
        return;
    }

    // Load the images.
    Bitmap[] images = new Bitmap[files.Count];
    for (int i = 0; i < num_images; i++)
        images[i] = new Bitmap(files[i]);

    // Find the largest width and height.
    int max_wid = 0;
    int max_hgt = 0;
    for (int i = 0; i < num_images; i++)
    {
        if (max_wid < images[i].Width) max_wid = images[i].Width;
        if (max_hgt < images[i].Height) max_hgt = images[i].Height;
    }

    // Make the result bitmap.
    int margin = int.Parse(txtMargin.Text);
    int num_cols = int.Parse(txtNumCols.Text);
    int num_rows = (int)Math.Ceiling(num_images / (float)num_cols);
    int wid = max_wid * num_cols + margin * (num_cols - 1);
    int hgt = max_hgt * num_rows + margin * (num_rows - 1);
    Bitmap bm = new Bitmap(wid, hgt);

    // Place the images on it.
    using (Graphics gr = Graphics.FromImage(bm))
    {
        gr.Clear(picBackground.BackColor);

        int x = 0;
        int y = 0;
        for (int i = 0; i < num_images; i++)
        {
            gr.DrawImage(images[i], x, y);
            x += max_wid + margin;
            if (x >= wid)
            {
                y += max_hgt + margin;
                x = 0;
            }
        }
    }

    // Save the result.
    SaveImage(bm, txtOutputFile.Text);

    Cursor = Cursors.Default;
    MessageBox.Show("Done");
}

[Combine images in rows and columns in C#]

The program first uses Directory.GetFiles to list the files in the source directory. It checks each file’s extension and saves the names of the image files.

Next, the program loops through the image file names and loads each file into a bitmap. It then loops through the bitmaps to find the largest width and height.

The program then calculates the number of rows and columns it needs to display the images and creates a new bitmap large enough to hold all of the images. It creates a Graphics object associated with the bitmap, and then loops through the images again. This time it copies each image into the next row/column position in the new bitmap.

After it finishes copying the images the program saves the result by calling the SaveImage method described in the post Save images with an appropriate format depending on the file name’s extension in C#.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Book Sample Pictures: WPF 3d

[WPF 3d]

This page shows thumbnail images for my book WPF 3D, Three-Dimensional Graphics with WPF and C#. Click on a picture to see a full-sized version.


[ OverviewTable of ContentsQuestions and DiscussionErrataSample PicturesSource Code (17.2 MB) ]

[ Buy at: Amazon ]


Follow me on Twitter   RSS feed   Donate




Posted in 3D, 3D graphics, algorithms, books, drawing, graphics, mathematics, wpf, XAML | Tagged , , , , , , , , , , , , , , | 3 Comments

Book Errata Page: WPF 3d

[WPF 3d]

This is the errata page for my book WPF 3D, Three-Dimensional Graphics with WPF and C#. If you find mistakes, please post them here in the “Leave a Reply” box at the bottom of the page. I will moderate them and fix them in new copies of the book.


[ OverviewTable of ContentsQuestions and DiscussionErrataSample PicturesSource Code (17.2 MB) ]

[ Buy at: Amazon ]


Follow me on Twitter   RSS feed   Donate




Posted in 3D, 3D graphics, algorithms, books, drawing, graphics, mathematics, wpf, XAML | Tagged , , , , , , , , , , , , , , | 3 Comments

Book Discussion Page: WPF 3d

[WPF 3d]

This is a discussion page for my book WPF 3D, Three-Dimensional Graphics with WPF and C#. Please post questions, thoughts, and suggestions here in the “Leave a Reply” box at the bottom of the page. I will moderate posts and reply as appropriate.


[ OverviewTable of ContentsQuestions and DiscussionErrataSample PicturesSource Code (17.2 MB) ]

[ Buy at: Amazon ]


Follow me on Twitter   RSS feed   Donate




Posted in 3D, 3D graphics, algorithms, books, drawing, graphics, mathematics, Uncategorized, wpf, XAML | Tagged , , , , , , , , , , , , , , | 3 Comments

WPF 3d: Table of Contents

[WPF 3d]

This is a brief summary of the table of contents for my book WPF 3D, Three-Dimensional Graphics with WPF and C#.

Contents
Introduction
Part I. Getting Started
Chapter 1. XAML Example
Chapter 2. C# Example
Chapter 3. Coordinates
Chapter 4. Projections
Chapter 5. Camera Control
Chapter 6. Lights
Chapter 7. Materials

Part II. Building Shapes
Chapter 8. Vector Arithmetic
Chapter 9. Transformations
Chapter 10. Polygons
Chapter 11. Parallelograms
Chapter 12. Boxes
Chapter 13. Pyramids
Chapter 14. Cones
Chapter 15. Cylinders
Chapter 16. Spheres
Chapter 17. Tori
Chapter 18. Tetrahedrons
Chapter 19. Cubes
Chapter 20. Octahedrons
Chapter 21. Dodecahedrons
Chapter 22. Icosahedrons
Chapter 23. Wireframes

Part III. Investigating Advanced Topics
Chapter 24. Geodesic Spheres
Chapter 25. Stellate Solids
Chapter 26. Surfaces
Chapter 27. Normals
Chapter 28. Transformation Surfaces
Chapter 29. Fractal Surfaces
Chapter 30. . Three-Dimensional Text
Chapter 31. Two-Dimensional Text
Chapter 32. Moving Objects
Chapter 33. Models
Chapter 34. Charts and Graphs
Chapter 35. Skeletons
Afterword
Index


[ OverviewTable of ContentsQuestions and DiscussionErrataSample PicturesSource Code (17.2 MB) ]

[ Buy at: Amazon ]


Follow me on Twitter   RSS feed   Donate




Posted in 3D, 3D graphics, algorithms, books, drawing, graphics, mathematics, wpf, XAML | Tagged , , , , , , , , , , , , , , | 3 Comments

Graph a square curve in C#

[square curve]

This example shows how to graph a curve that draws a square. The basic idea isn’t as bizarre as you might think. You probably know that x2 + y2 = 1 is the equation for a circle. So what is the equation for a square?

If you use the similar equation xN + yN = 1 where N is an increasingly large even integer, the equation approximates a square. For example, the picture at the top of this post shows the graph of the squarish curve x10 + y10 = 1.

By making N big enough, you can make the curve approximate a square to within any given tolerance. If you set N = 100, you can’t really see a difference between the curve and a square.

You can’t really plot the equation xN + yN = 1 directly, so the program solves it for x and actually graphs the following equation.


[square curve]

If the power N is an even integer, then this equation has two real solutions: a positive root and a negative root.

When you enter a positive, even, integer power in the text box and click Graph, the program makes a Bitmap and draws axes on it. That part is relatively straightforward so it isn’t shown here. Download the example to see how it works.

The following code shows how the program draws the curve.

// Draw the curve.
private void DrawGraph(Graphics gr, Pen pen, float wxmin)
{
    // Get the power.
    int power = int.Parse(txtPower.Text);
    float root = 1f / power;

    // Even integer power. -1 <= x, y <= 1
    List points1 = new List();
    List points2 = new List();
    for (float x = -1f; x <= 1f; x += 0.001f)
    {
        float y = -(float)Math.Pow(1 - Math.Pow(x, power), root);
        points1.Add(new PointF(x, y));
        points2.Add(new PointF(x, -y));
    }

    // Combine the lists.
    points2.Reverse();
    points1.AddRange(points2);

    // Draw the curve.
    gr.DrawPolygon(pen, points1.ToArray());
}

This code draws the graph of the equation shown above. It loops through values for x between -1 and 1 and uses the previous equation to find the corresponding y values.

The points that use positive y values move across the top of the square curve. The code saves them in the points1 array.

Similarly, the points that use negative y values move across the bottom of the square curve. The code saves them in the points2 array.

To easily draw the final curve, the program reverses the order of the points in the points2 array and adds those points to the end of the points1 array. The combined points now walk clockwise around the square curve.

The method finishes by drawing the points as a polygon.

Download the example to see additional details and to experiment with the program.

Note that the code only works with even, positive, integer powers.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, drawing, mathematics | Tagged , , , , , , , , , , , | 4 Comments

Graph a curve in polar coordinates in C#

[polar coordinates]
[polar coordinates]

In Cartesian coordinates, a point is specified by X and Y values. In polar coordinates, a point is specified by r and θ where:

  • r is the point’s radial distance from the origin
  • θ is the angle that gives the direction between the positive X axis and the point

You can use the following equations to convert from polar coordinates to Cartesian coordinates.

  • X = r × Cos(θ)
  • Y = r × Sin(θ)

This program graphs the equation r = 2 * Sin(5 θ).

The following Form_Load event handler does everything except actually draw the curve.

// Draw the graph.
private void Form1_Load(object sender, EventArgs e)
{
    // Make the Bitmap and associated Graphics object.
    Bitmap bm = new Bitmap(300, 300);
    using (Graphics gr = Graphics.FromImage(bm))
    {
        gr.Clear(Color.White);

        // Set up a transformation to map the region
        // -2.1 <= X <= 2.1, -2.1 <= Y <= 2.1 onto the  Bitmap.
        RectangleF rect = new RectangleF(-2.1f, -2.1f, 4.2f, 4.2f);
        PointF[] pts =
            {
                new PointF(0, bm.Height),
                new PointF(bm.Width, bm.Height),
                new PointF(0, 0),
            };
        gr.Transform = new Matrix(rect, pts);
        gr.SmoothingMode = SmoothingMode.AntiAlias;

        using (Pen thin_pen = new Pen(Color.Blue, 0))
        {
            // Draw the X and Y axes.
            thin_pen.Color = Color.Blue;
            gr.DrawLine(thin_pen, -2.1f, 0, 2.1f, 0);
            const float big_tick = 0.1f;
            const float small_tick = 0.05f;
            for (float x = (int)-2.1f; x <= 2.1f; x += 1)
                gr.DrawLine(thin_pen, x, -small_tick, x, small_tick);
            for (float x = (int)-2.1f + 0.5f; x <= 2.1f; x += 1)
                gr.DrawLine(thin_pen, x, -big_tick, x, big_tick);

            gr.DrawLine(thin_pen, 0, -2.1f, 0, 2.1f);
            for (float y = (int)-2.1f; y <= 2.1f; y += 1)
                gr.DrawLine(thin_pen, -small_tick, y, small_tick, y);
            for (float y = (int)-2.1f + 0.5f; y <= 2.1f; y += 1)
                gr.DrawLine(thin_pen, -big_tick, y, big_tick, y);

            // Draw the graph.
            DrawGraph(gr);
        }
    }

    // Display the result and size the form to fit.
    picGraph.Image = bm;
    this.ClientSize = new Size(
        picGraph.Width + 2 * picGraph.Left,
        picGraph.Height + 2 * picGraph.Top);
}

This code makes a Bitmap and an associated Graphics object. It clears the Bitmap and creates a transformation to center the region -2.1 ≤ {x, y} ≤ 2.1 on the Bitmap. It then creates a thin pen (otherwise the transformation makes the pen extremely thick) and draws the X and Y axes.

The code then calls the DrawGraph method described shortly to draw the graph on the Bitmap.

Next, the event handler sets the picGraph PictureBox control’s Image property to the Bitmap. That control’s SizeMode property is set to AutoSize so it resizes itself to fit the image. The event handler finishes by resizing the form so its client area is big enough to show the PictureBox.

The following code shows the DrawGraph method that draws the graph.

// Draw the graph on a Bitmap.
private void DrawGraph(Graphics gr)
{
    // Generate the points.
    double t = 0;
    const double dt = Math.PI / 100.0;
    const double two_pi = Math.PI;
    List<PointF> points = new List<PointF>();
    while (t <= two_pi)
    {
        double r = 2 * Math.Sin(5 * t);
        float x = (float)(r * Math.Cos(t));
        float y = (float)(r * Math.Sin(t));
        points.Add(new PointF(x, y));
        t += dt;
    }

    // Draw the curve.
    using (Pen thin_pen = new Pen(Color.Red, 0))
    {
        gr.DrawPolygon(thin_pen, points.ToArray());
    }
}

This code makes the variable t range from 0 to 2π. For each value of t, the method calculates r = 2 * Sin(5 t). It then converts the polar coordinates into Cartesian coordinates and adds the point to the points list.

After it has generated all of the points, the method draws them.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, graphics, mathematics | Tagged , , , , , , , , , , , , | 1 Comment

Create a PowerPoint presentation from a Word document in C#

This example shows how you can create a PowerPoint presentation containing slides that correspond to selected styles in a Microsoft Word document.


[Word to PowerPoint]

How to Use It

Enter or select a Word document and a destination PowerPoint presentation. Enter the styles in the Word document that contain the text you want to use for the PowerPoint slides. Fill in a “Title Style” and a “Heading Style” and click Go.

The program makes a PowerPoint presentation. It use the first piece of text in the Word document’s “Title Style” style for the PowerPoint presentation’s title slide. It uses text in the “Heading Style” to make other slides. It finishes by creating an Agenda slide that contains links to the “Heading Style” slides.

[Word to PowerPoint]

For example, the project includes the Word document Test.docx shown on the right. The first line is in the Heading 1 style. The Section lines are in the Heading 2 style. When you run the program with Title Style = Heading 1 and Heading Style = Heading 2, you get the result shown at the top of this post. The presentation includes a title slide labeled “Title.” Next comes the agenda slide that provides links to the other slides, which are labeled “Section 1,” “Section 2,” and “Section 3.”

If there are other elements in the Word document that you want to include in the presentation, you could modify the program to include them. For example, you could add a second level of heading. After using the program to build the basic presentation, you can fill in the details such as adding text, pictures, and other slides.

How it Works

So how does the program work?

First, I used Project > Add Reference command to open the Add Reference dialog. On the .NET tab, I added references to the following:

  • Microsoft.Office.Interop.PowerPoint
  • Microsoft.Office.Interop.Word
  • Office

To make using the libraries easier, the program includes the following using directives.

using Word = Microsoft.Office.Interop.Word;
using Ppt = Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.Core;

When you click the Go button, the following code executes to start the conversion process.

// Create the PowerPoint presentation.
private void btnGo_Click(object sender, EventArgs e)
{
    try
    {
        Cursor = Cursors.WaitCursor;

        // Get the information we need from the Word file.
        string title;
        List<string> headings;
        GetWordData(txtWordFile.Text, txtTitleStyle.Text,
            txtHeadingStyle.Text,
            out title, out headings);

        if (title == "")
        {
            MessageBox.Show("Could not find title style '" +
                txtTitleStyle.Text + "'");
            return;
        }
        if (headings.Count == 0)
        {
            MessageBox.Show("Could not find heading style '" +
                txtHeadingStyle.Text + "'");
            return;
        }

        // Create the PowerPoint presentation.
        CreatePpt(txtPowerPointFile.Text, title, headings);

        MessageBox.Show("Done");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        Cursor = Cursors.Default;
    }
}

This code mostly calls the GetWordData and CreatePpt methods.

The GetWordData method (described shortly) opens the Word file and finds the appropriate text. It saves the first piece of text with the “Title Style” in its title parameter. It saves all of the text with the “Heading Style” in the headings array.

The CreatePpt method (also described shortly) creates the PowerPoint presentation.

GetWordData

The following code shows the GetWordData method. The code actually isn’t too complicated but error handling makes it a bit longish.

// Get the necessary information from the Word document.
private void GetWordData(string file_name,
    string title_style, string heading_style,
    out string title, out List<string> headings)
{
    // Load the Word document.
    // Get the Word application object.
    Word._Application word_app = new Word.ApplicationClass();

    object save_changes = false;
    object missing = System.Reflection.Missing.Value;

    try
    {
        // Make Word visible (optional).
        word_app.Visible = false;

        // Open the file.
        object filename = file_name;
        object confirm_conversions = false;
        object read_only = true;
        object add_to_recent_files = false;
        object format = 0;

        Word._Document word_doc =
            word_app.Documents.Open(ref filename,
                ref confirm_conversions, ref read_only,
                ref add_to_recent_files, ref missing, ref missing,
                ref missing, ref missing, ref missing, ref format,
                ref missing, ref missing, ref missing, ref missing,
                ref missing, ref missing);

        try
        {
            // Get the title.
            object style_name_obj = txtTitleStyle.Text;
            WordFindWordWithStyle(word_doc, word_app,
                style_name_obj, out title);
            Console.WriteLine("Title: " + title);

            // Get the headings.
            style_name_obj = txtHeadingStyle.Text;
            headings = new List<string>();

            // Repeat until we find a match before the previous one.
            int last_pos = -1;
            for (; ; )
            {
                string heading;
                int pos = WordFindWordWithStyle(word_doc, word_app,
                    style_name_obj, out heading);
                if ((pos == 0) || (pos < last_pos)) break;

                last_pos = pos;
                headings.Add(heading);
                Console.WriteLine("Section: " + pos + ": " +
                    heading);
            }
        }
        catch
        {
            // Rethrow the exception.
            throw;
        }
        finally
        {
            // Close the document without prompting.
            word_doc.Close(ref save_changes, ref missing,
                ref missing);
        }
    }
    catch
    {
        // Rethrow the exception.
        throw;
    }
    finally
    {
        word_app.Quit(ref save_changes, ref missing, ref missing);
    }
}

The GetWordData method creates a Word application object to act as a Word automation server and uses it to open the Word document. The method then calls WordFindWordWithStyle (described next) to try to find a piece of text with the “Title Style.”

The code then enters a loop where it uses WordFindWordWithStyle to try to find text with the “Heading Style.” The WordFindWordWithStyle method returns the index of the next occurrence of the text it finds. If it returns 0, it did not find the text so the program breaks out of the loop.

If WordFindWordWithStyle returns an index less than the previous one, that means it has wrapped around to the beginning of the file so it has already returned all of the occurrences of the “Heading Style.” In that case, the code also breaks out of the loop.

The GetWordData method then returns, having stored the text it found in its title and headings parameters. The rest of the method’s code is error handling.

Notice how the method uses finally blocks to close the Word document if it is open and to close the Word application server if it is open. If you don’t do this, you may get odd system effects such as a zombie Word server running in the background and taking up resources but not doing anything useful.

WordFindWordWithStyle

The following code shows the WordFindWordWithStyle method.

// Find an occurrence of a style in a Word document.
// If the style isn't found, set text = "" and return 0.
private int WordFindWordWithStyle(Word._Document word_doc,
    Word._Application word_app, object style_name, out string text)
{
    word_app.Selection.Find.ClearFormatting();
    object style;
    try
    {
        style = word_doc.Styles.get_Item(ref style_name);
    }
    catch
    {
        text = "";
        return 0;
    }
    word_app.Selection.Find.set_Style(ref style);

    object blank_text = "";
    object false_obj = false;
    object true_obj = true;
    object wrap_obj = Word.WdFindWrap.wdFindContinue;
    object missing = System.Reflection.Missing.Value;
    word_app.Selection.Find.Execute(ref blank_text,
        ref false_obj, ref false_obj, ref false_obj,
        ref false_obj, ref false_obj, ref true_obj,
        ref wrap_obj, ref true_obj, ref missing,
        ref missing, ref false_obj, ref false_obj,
        ref false_obj, ref false_obj);

    text = word_app.Selection.Text.Trim();
    return word_app.Selection.Start;
}

This method clears any formatting in the Word Selection object so it can begin a fresh search. It then uses the Word document object’s Styles collection to get the object representing the desired style. If there is an error, that means the desired style doesn’t exist in the Word document. In that case, the method returns 0 to indicate it did not find any text.

If the method did find the desired style, it sets the Selection.Find object’s style to the style of the object that it found. The code creates some variables to represent search parameters and then calls the Selection.Find object’s Execute method to find the next occurrence of the desired style.

The most interesting parameter at this point is Word.WdFindWrap.wdFindContinue. This value makes Selection.Find move from one match to the next, wrapping around to the beginning of the file when it reaches the end. Other choices for this parameter are wdFindAsk (but you don’t want to ask the user whether to continue) and wdFindStop (which only finds one occurrence and doesn’t move to the next).

After it has found some text, the method trims it and saves it in the text output parameter. Finally the method returns the index of the text that it found.

CreatePpt

The example’s last interesting piece of code is the CreatePpt method shown in the following code. Like the GetWordData method, it is complicated slightly by error handling code.

// Create the PowerPoint presentation.
private void CreatePpt(string file_name,
    string title, List<string> headings)
{
    // Get the PowerPoint application object.
    Ppt._Application ppt_app = new Ppt.ApplicationClass();

    try
    {
        // Create the presentation.
        Ppt.Presentation ppt_pres =
            ppt_app.Presentations.Add(MsoTriState.msoFalse);
        try
        {
            // Create the title slide.
            Ppt.Slide title_slide =
                ppt_pres.Slides.Add(1,
                    PpSlideLayout.ppLayoutTitleOnly);
            title_slide.Shapes[1].TextFrame.TextRange.Text = title;

            // Keep track of the other slides so
            // we can make an agenda.
            string agenda = "";

            // Create other slides.
            int slide_num = 1;
            foreach (string heading in headings)
            {
                slide_num++;
                Ppt.Slide heading_slide = ppt_pres.Slides.Add(
                    slide_num, PpSlideLayout.ppLayoutText);
                heading_slide.Shapes[1].TextFrame.TextRange.Text =
                    heading;

                agenda += '\n' + heading;
            }

            // Create the agenda.
            if (agenda.Length > 0)
            {
                // Remove the initial \n.
                agenda = agenda.Substring(1);

                // Create the agenda slide.
                Ppt.Slide agenda_slide = ppt_pres.Slides.Add(
                    2, PpSlideLayout.ppLayoutText);
                agenda_slide.Shapes[1].TextFrame.TextRange.Text =
                    "Agenda";
                agenda_slide.Shapes[2].TextFrame.TextRange.Text =
                    agenda;

                // Make each line in the agenda text be a hyperlink
                // to the corresponding section page.
                for (int i = 1; i < slide_num; i++)
                {
                    // Page number (the current slide number) is
                    // the section number plus 2 (for the title
                    // slide and the agenda).
                    int page_number = i + 2;

                    // Set the SubAddress.
                    // (Address gives the file or URL. We don't
                    // use Address so the destination is within
                    // this document. SubAddress gives the
                    // location within the file.
                    // In this case, that's the slide number.)
                    agenda_slide.Shapes[2].TextFrame.TextRange.
                        Lines(i, i).ActionSettings[
                        PpMouseActivation.ppMouseClick].
                        Hyperlink.SubAddress =
                            page_number.ToString();
                }
            }

            // Save the presentation.
            ppt_pres.SaveAs(file_name,
                PpSaveAsFileType.ppSaveAsPresentation,
                MsoTriState.msoFalse);
        }
        catch
        {
            // Rethrow the exception.
            throw;
        }
        finally
        {
            // Close.
            ppt_pres.Close();
        }
    }
    catch
    {
        // Rethrow the exception.
        throw;
    }
    finally
    {
        ppt_app.Quit();
    }
}

This method starts by creating a PowerPoint application server object. It then uses the Presentations.Add method to create a new presentation. The parameter to that method indicates whether the new presentation should be displayed in a visible window.

Next, the method calls the presentation object’s Slides.Add method to create the title slide. As its name implies, the ppLayoutTitleOnly type of slide contains only a title text area. The code sets the text for the first TextFrame object on the slide to the title text taken from the Word file.

The method then loops through the heading values the program got from the Word file. For each value, the code creates a new slide. This slide has type ppLayoutText, which gives it a title text box and a larger detail text area beneath. The method sets the slide’s first shape’s text (in the title text area) to the heading taken from the Word file.

The code also adds the new slide’s heading to the growing string agenda. After the loop finishes, the string agenda contains a list of the slides’ titles.

After the method has built all of the detail slides, it inserts an agenda slide after the main title slide. It sets the slide’s title to “Agenda” and sets the text beneath to the agenda string.

Next, the code loops from 1 to the number of detail slides that it created earlier. For each of those slides, the method gets the corresponding line in the agenda slide’s detail text and gets that line’s ActionSettings value. It sets the ppMouseClick action setting’s Hyperlink.SubAddress property to the corresponding page number. The result is that the line in the agenda slide is now a hyperlink to the corresponding page number within this document. (If you wanted a hyperlink to a location in another document, you could set the Hyperlink object’s Address property.)

The CreatePpt method finishes by saving the new PowerPoint presentation. If the file already exists, the method overwrites it without warning.

The rest of the method uses finally blocks to close the presentation and PowerPoint server.

I used this program to make presentations to go with my book:


Essential Algorithms: A Practical Approach to Computer Algorithms


ISBN: 978-1-118-61210-1
Paperback
624 pages
August 2013

[Essential Algorithms]

The presentations are intended for instructors. After using the program to build the basic presentation, I filled in the details by adding text, pictures, other slides, and some notes.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in interoperability, Office, Word | Tagged , , , , , , , , , , , , , , , | Leave a comment

Compare floating-point values safely in C#

[floating-point]

The computer stores values, including floating-point 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 is 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 because rounding errors may make them different when they should be the same. Instead, subtract the two values 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 slightly different. 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.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in calculations, mathematics | Tagged , , , , , , , , , , , , , | 3 Comments

Resize a RichTextBox to fit its contents in C#

[Resize a RichTextBox]

This example shows how you can resize a RichTextBox control so it fits the text and images that it contains.

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 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 require 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 control’s new contents needs. 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 it into the RichTextBox to see how it handles them.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, user interface | Tagged , , , , , , , , , , , | 5 Comments