Copy ListView data into an array in C#

[ListView]

This example uses the following ListView extension method to copy ListView data into a two-dimensional array of strings.

// Return the ListBox's contents in a string[,].
public static string[,] GetListViewData(this ListView lvw)
{
    // Get the number of rows and columns.
    int num_rows = lvw.Items.Count;
    int num_cols = 0;
    for (int i = 0; i < num_rows; i++)
    {
        if (num_cols < lvw.Items[i].SubItems.Count)
            num_cols = lvw.Items[i].SubItems.Count;
    }

    // Make the array.
    string[,] results = new string[num_rows, num_cols];

    // Populate the array.
    // Note that SubItems includes the items, too.
    for (int r = 0; r < num_rows; r++)
    {
        for (int c = 0; c < num_cols; c++)
            results[r, c] = lvw.Items[r].SubItems[c].Text;
    }

    // Return the result.
    return results;
}

The method sets num_rows equal to the number of items in the ListView control’s Items collection. It then loops through that collection to see which item has the most sub-items.

Note that an item’s SubItems collection includes the item, too. For example, suppose the ListView control named lvw has a first item that includes the values Apple, Banana, and Cherry. Then lvw.Items[0].Text is Apple and lvw.Items[0].SubItems contains all three values Apple, Banana, and Cherry.

When it loops through the SubItems collection, the program keeps track of the SubItems collection that has the most items. that gives the number of columns we need in the array.

Having found the number of rows and columns it needs, the program allocates the results array. It then loops through the items again, this time copying the ListView data into the array. When it is done, the method returns the result.

Using the extension method is easy. The following code shows how the main program uses the method when you click the Get Data button.

// Get the ListView's contents.
private void btnGetData_Click(object sender, EventArgs e)
{
    // Get the contents.
    string[,] listview_data = lvwBooks.GetListViewData();

    // Display the contents.
    StringBuilder sb = new StringBuilder();
    int num_rows = listview_data.GetUpperBound(0) + 1;
    int num_cols = listview_data.GetUpperBound(1) + 1;
    for (int r = 0; r < num_rows; r++)
    {
        for (int c = 0; c < num_cols; c++)
        {
            sb.Append(listview_data[r, c] + "|");
        }
        sb.AppendLine();
    }
    txtResult.Text = sb.ToString();
}

This event handler uses the extension method to get the ListView control’s values. It then loops through them adding the values to a StringBuilder. When it is finished, the program displays the resulting string in a text box.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in arrays, controls, strings | Tagged , , , , , , , , , , | Leave a comment

Draw the spiral of Theodorus in C#

[spiral of Theodorus]

The spiral of Theodorus (which is also called the square root spiral, Einstein spiral, and Pythagorean spiral) was first devised by the Greek mathematician Theodorus of Cyrene during the 5th century BC. The spiral consists of a sequence of right triangles where the ith triangle has side lengths 1, √i, and √(i+1). Each triangle’s edge of length √(i+1) is shared with the next triangle as shown in the following picture from Wikipedia.


[spiral of Theodorus]

When you fill in the number of triangles that you want to draw and click Draw, the following code executes.

private void btnDraw_Click(object sender, EventArgs e)
{
    // Get the spiral of Theodorus's points.
    int num_triangles = int.Parse(txtNumTriangles.Text);
    List<PointF> edge_points = FindTheodorusPoints(num_triangles);

    // Draw the spiral of Theodorus.
    picSpiral.Image = DrawTheodorusSpiral(
        edge_points, picSpiral.ClientSize,
        chkOutline.Checked, chkFill.Checked);
}

This code gets the number of triangles that you entered. It then calls the FindTheodorusPoints method described shortly to get the points on the outside of the spiral of Theodorus. It finishes by calling the DrawSpiral method (also described shortly) to draw the spiral.

The following sections describe the FindTheodorusPoints and DrawSpiral methods and their helper routines.

FindTheodorusPoints

The following code shows the FindTheodorusPoints method.

// Find points on the spiral of Theodorus.
private List<PointF> FindTheodorusPoints(int num_triangles)
{
    // Find the edge points.
    List<PointF> edge_points = new List<PointF>();

    // Add the first point.
    float theta = 0;
    float radius = 1;
    for (int i = 1; i <= num_triangles; i++)
    {
        radius = (float)Math.Sqrt(i);
        edge_points.Add(new PointF(
            radius * (float)Math.Cos(theta),
            radius * (float)Math.Sin(theta)));
        theta -= (float)Math.Atan2(1, radius);
    }

    return edge_points;
}

This method first creates an edge_points list to hold the triangles’ outer vertices.

The variable theta keeps track of the angle that the points make with respect to the spiral’s center. Variable radius keeps track of the length of the triangles’ side lengths.

The code sets theta = 0 and radius = 1, and then enters a loop to find the leading edge point for each triangle. Inside the loop, the code uses theta and radius to find the triangle’s point and adds it to the list.

Each triangle’s inner angle (the one by the spiral’s center) is the arc tangent of the opposite side (which always has length 1) and the adjacent side. The adjacent side for the ith triangle has length radius = √i, so the program subtracts Atan2(1, radius) from theta to prepare for the next point on the spiral. (The code subtracts this value instead of adding it so the angles increase counterclockwise for the triangles.)

After it finishes finding the edge points, the method returns them.

DrawTheodorusSpiral

The following code shows the DrawTheodorusSpiral method, which draws the spiral.

// Draw the spiral of Theodorus.
private Bitmap DrawTheodorusSpiral(List<PointF> edge_points,
    Size size, bool outline_triangles, bool fill_triangles)
{
    // Make the bitmap and associated Graphics object.
    int wid = size.Width;
    int hgt = size.Height;
    Bitmap bm = new Bitmap(wid, hgt);
    using (Graphics gr = Graphics.FromImage(bm))
    {
        gr.SmoothingMode = SmoothingMode.AntiAlias;
        gr.Clear(Color.White);

        // Make brushes.
        Color[] colors = RainbowColors(255);
        Brush[] brushes = ColorsToBrushes(colors);

        // Scale and center.
        float xmin, xmax, ymin, ymax;
        GetBounds(edge_points, out xmin, out xmax,
            out ymin, out ymax);
        RectangleF drawing_rect = new RectangleF(
            xmin, ymin, xmax - xmin, ymax - ymin);
        RectangleF target_rect = new RectangleF(
            5, 5, wid - 10, hgt - 10);
        MapDrawing(gr, drawing_rect, target_rect, false);

        // Draw.
        using (Pen pen = new Pen(Color.Black, 0))
        {
            int num_brushes = brushes.Length;
            for (int i = edge_points.Count - 1; i > 0; i--)
            {
                PointF[] points =
                {
                    new PointF(0, 0),
                    new PointF(
                        edge_points[i].X,
                        edge_points[i].Y),
                    new PointF(
                        edge_points[i - 1].X,
                        edge_points[i - 1].Y),
                };
                if (fill_triangles)
                    gr.FillPolygon(brushes[i % num_brushes],
                        points);
                if (outline_triangles)
                    gr.DrawPolygon(pen, points);
            }
        }
    }

    return bm;
}

This method draws the spiral of Theodorus on a bitmap of a specified size and returns the bitmap.

It starts by creating a bitmap of the correct size, making an associated Graphics object, and clearing it.

The method then calls the RainbowColors method to make some colors. It then uses the ColorsToBrushes method to convert those colors into an array of brushes. Both of those methods are described shortly.

The code then calls the GetBounds method (also described shortly) to get bounds for the spiral’s edge points. It uses the bounds to make a rectangle representing the drawing area. It then passes the drawing rectangle and a rectangle that represents the bitmap’s surface (minus a margin) to the MapDrawing method.

The MapDrawing method applies translation and scaling transformations to the Graphics object to make drawing commands it inside the target area. For more information on this method, see the post Scale a drawing to fit a target area in C#.

Now the method draws the spiral of Theodorus. It creates a black pen with thickness 0. (Pens with thickness 0 are not scaled even if the Graphics object includes a scaling transformation.)

[spiral of Theodorus]

The method then enters a loop that runs from the last triangle point to the first. The loops runs last-to-first instead of first-to-last because later triangles may overlap earlier ones. The picture on the right shows the spiral of Theodorus with 30 triangles. You can see that the outer ones overlap the inner ones. Drawing the triangles last-to-first lets you see pieces of all of the triangles.

After it fills a triangle, the method outlines it.

As it draws each triangle, the code checks the outline_triangles and fill_triangles values to see whether it should outline or fill the triangles. (Draw the spiral without filling it if you want to see the overlapping outlines of the triangles.)

RainbowColors

The following code shows the RainbowColors method.

// Return an array of rainbow colors.
private Color[] RainbowColors(byte alpha)
{
    return new Color[]
    {
        Color.FromArgb(alpha, 255, 0, 0),
        Color.FromArgb(alpha, 255, 255, 0),
        Color.FromArgb(alpha, 255, 128, 0),
        Color.FromArgb(alpha, 0, 255, 0),
        Color.FromArgb(alpha, 0, 255, 255),
        Color.FromArgb(alpha, 0, 0, 255),
        Color.FromArgb(alpha, 255, 0, 255),
    };
}

This method simply builds an array holding a predefined set of colors. The method takes an alpha parameter in case you want to make the colors semi-transparent. (I originally thought that would be useful in this example, but it just made the result more cluttered.)

ColorsToBrushes

The following code shows the ColorsToBrushes helper method.

// Convert colors to brushes.
private Brush[] ColorsToBrushes(Color[] colors)
{
    int num_colors = colors.Length;
    Brush[] brushes = new Brush[num_colors];
    for (int i = 0; i < num_colors; i++)
        brushes[i] = new SolidBrush(colors[i]);
    return brushes;
}

This method simply loops through an array of colors and makes a SolidBrush for each.

GetBounds

The following code shows the GetBounds method.

// Get the points' bounds.
private void GetBounds(List<PointF> points,
    out float xmin, out float xmax,
    out float ymin, out float ymax)
{
    // Find the bounds.
    xmin = points[0].X;
    xmax = xmin;
    ymin = points[0].Y;
    ymax = ymin;
    foreach (PointF point in points)
    {
        if (xmin > point.X) xmin = point.X;
        if (xmax < point.X) xmax = point.X;
        if (ymin > point.Y) ymin = point.Y;
        if (ymax < point.Y) ymax = point.Y;
    }
}

This method simply loops through an array of PointF and finds their minimum and maximum X and Y values. (You could use LINQ to do something similar, but it wouldn’t be any easier to read and it would be slightly slower.)

Summary

Download the example to see additional details and to experiment with the program. For example, you might try using semi-transparent colors, with or without the triangle outlines. If you fill around 10,000 or more triangles without outlining them, you can also see an interesting wave effect flowing through the spiral’s rings.

For more information on the spiral of Theodorus, see the Wikipedia article Spiral of Theodorus.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Add LINQ to autocomplete in C#

[autocomplete]

This example adds LINQ to the example Improve autocomplete suggestion in C#. It adds LINQ in two places: when it loads the list of words and when the program searches for the best matches that begin with a particular letter.

The following code shows how the older version of the program loads its word list.

// Non-LINQ method.
foreach (string line in lines)
{
    if (line.Length > 1)
    {
        // Get the first letter.
        int index = (int)line.ToUpper()[0] - (int)'A';
        Words[index].Add(line);
    }
}

This code loops through the words in the lines array. For each word it gets the word’s first letter and uses it to decide which list in the Words array of lists should hold that word.

The following code shows the LINQ version.

// LINQ method.
var word_groups =
    from string line in lines
    group line by line.ToUpper()[0] into g
    select new { Letter = g.Key, Words = g };
foreach (var g in word_groups)
{
    int index = (int)g.Letter - (int)'A';
    Words[index] = g.Words.ToList();
}

This code defines a query that selects the words in the lines array and groups them by their first letters. For each group, the program uses the group’s first letter to calculate the index where the group’s words should be stored in the Words list and then stores the group there converting it into a List.

LINQ is often not as fast as other methods. Its strength lies in the fact that it’s easy to write and often produces code that is easier to read than code that you write to perform similar actions. In this case, however, I don’t think the LINQ is easier to write or understand. The LINQ group command has a confusing syntax and the result is rather confusing. For this simple example that selects all of the items, LINQ doesn’t add much and actually makes the code longer, so I wouldn’t use it here.

The other use of LINQ in this example, however, is more helpful. The following code shows how the previous version of the program finds the best matches for a word.

// Find the best matches for the typed word.
private void FindBestMatches(string word, int num_matches,
    out string[] words, out int[] values)
{
    // Get the word list for the word's first letter.
    int index = (int)word.ToUpper()[0] - (int)'A';

    // Find words that start with the same letter.
    List<string> match_words = new List<string>();
    List<int> match_values = new List<int>();
    foreach (string test_word in Words[index])
    {
        // Consider the next word up to
        // the length of the typed word.
        int max_length = Math.Min(test_word.Length, word.Length);
        string short_word = test_word.Substring(0, max_length);

        // Build the edit graph.
        Node[,] nodes = MakeEditGraph(word, short_word);

        // List the distance.
        int x = nodes.GetUpperBound(0);
        int y = nodes.GetUpperBound(1);
        match_words.Add(test_word);
        match_values.Add(nodes[x, y].distance);
    }

    // Sort the matches by distance, smallest distance first.
    string[] match_words_array = match_words.ToArray();
    int[] match_values_array = match_values.ToArray();
    Array.Sort(match_values_array, match_words_array);

    // Return the desired number of matches.
    int max = Math.Min(num_matches, match_values_array.Length);
    words = new string[max];
    Array.Copy(match_words_array, words, max);
    values = new int[max];
    Array.Copy(match_values_array, values, max);
}

The code loops through the list of words that have the same initial letter as the target word. For each test word in the list, the code creates an edit graph representing the transformation from the word to the test word and saves the word and its edit distance. After the loop finishes, the program sorts the words and their edit distances and returns at most the desired number of matches.

A few hints that LINQ might be useful here include the fact that the code searches over lists (words with matching first letters), sorts some arrays (the words and their edit distances), and returns at most a certain number of items (the words and their edit distances again).

This code performs a somewhat involved calculation to find the edit distance for a word. While you may be able to work that calculation into LINQ code, LINQ statements are a lot easier to understand if you extract that calculation into a separate method. The new example does that with the following GetEditDistance method.

// Return the edit distance from target_word to test_word.
private int GetEditDistance(string target_word, string test_word)
{
    // Make test_word the same length as target_word.
    int max_length = Math.Min(test_word.Length, target_word.Length);
    test_word = test_word.Substring(0, max_length);

    // Build the edit graph.
    Node[,] nodes = MakeEditGraph(target_word, test_word);

    // Return the distance.
    int x = nodes.GetUpperBound(0);
    int y = nodes.GetUpperBound(1);
    return nodes[x, y].distance;
}

This method performs the same steps used by the previous examples to calculate a test word’s edit distance. It then returns that distance.

The following code shows how the new example uses this GetEditDistance method and LINQ to find the best matches for a word.

// Find the best matches for the typed word.
private string[] FindBestMatches(string target_word,
    int num_matches)
{
    // Get the word list for the word's first letter.
    int index = (int)target_word.ToUpper()[0] - (int)'A';

    // Use LINQ to select the test words' edit distance.
    var word_distance_query =
        from string test_word in Words[index]
        orderby GetEditDistance(target_word, test_word)
        select GetEditDistance(target_word, test_word) +
            " " + test_word;

    // Return up to the desired number of matches.
    return word_distance_query.Take(num_matches).ToArray();
}

This code builds a LINQ query that searches the Words list that contains the words beginning with the target word’s first letter. It orders the results by the edit distances between those words and the target word. Finally, it selects the edit distances and the corresponding words.

Having built the query, the code invokes it’s Take method to take at most the desired number of matches. It calls the result’s ToArray method to convert the result into an array and returns it.

This example has one other minor change. The previous version kept the test words and their edit distances in two separate arrays so it could sort the arrays by edit distance. It then needed to combine the values to create strings showing the words and their edit distances. The TextChanged event handler that calls FindBestMatches must then assemble the edit distances and words as shown in the following code.

// Find good guesses for this word.
private void txtWord_TextChanged(object sender, EventArgs e)
{
    lstGuesses.Items.Clear();
    string word = txtWord.Text;
    if (word.Length == 0) return;

    // Find the best matches.
    string[] words;
    int[] values;
    FindBestMatches(word, 10, out words, out values);

    // Display the best matches.
    for (int i = 0; i < words.Length; i++)
    {
        lstGuesses.Items.Add(values[i].ToString() +
            '\t' + words[i]);
    }
}

The new example’s LINQ code sorts the results, builds the result strings, and returns at most a desired number of items all in one step. This not only simplifies the FindBestMatches method, but it also simplifies the text box’s TextChanged event handler that calls it as shown in the following code.

// Find good guesses for this word.
private void txtWord_TextChanged(object sender, EventArgs e)
{
    lstGuesses.DataSource = null;
    string word = txtWord.Text;
    if (word.Length == 0) return;

    // Find the best matches.
    lstGuesses.DataSource = FindBestMatches(word, 100);
}

This use of LINQ is worth the effort. It makes the FindBestMatches method easier to understand and makes the TextChanged event handler that calls it simpler, too. It probably reduces performance, but this application is fast enough that the user won’t notice.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, LINQ, strings | Tagged , , , , , , , , , , , , , , | Leave a comment

Improve autocomplete suggestion in C#

[autocomplete]

This example improves on the example Suggest autocomplete words in C#. The previous example loads all of its words into a big array. Then, to find the words that start with a particular letter, the program uses a binary search to find the letter in the word list. The program then loops through the words in the list until it finds a word that doesn’t begin with the same letter.

When this new example starts, it loads the words that start with different letters into different lists. Then when it needs to process the words that start with a given letter, it simply loops through the appropriate list.

The following code shows how the program loads its lists of words.

// The dictionary.
private List<string>[] Words;

// Load the dictionary.
private void Form1_Load(object sender, EventArgs e)
{
    // Get the words from the dictionary file.
    string[] lines = File.ReadAllLines("Words.txt");

    // Group the words by first letter.
    Words = new List<string>[26];
    for (int i = 0; i < 26; i++)
        Words[i] = new List<string>();
    foreach (string line in lines)
    {
        if (line.Length > 1)
        {
            // Get the first letter.
            int index = (int)line.ToUpper()[0] - (int)'A';
            Words[index].Add(line);
        }
    }

    txtWord.Text = "absi";
}

The program uses the File class’s ReadAllLines method to read the words into an array. It then loops through the array.

For each word in the array (not counting single letters such as “A” and “R”), the code uses the word’s first letter to determine which list should hold the word and then adds the word to that list.

The other change to the program occurs in the FindBestMatches method. In the previous version of that method uses binary search to find the first word in the list starting with a given letter and then uses a somewhat confusing loop to examine the words that follow until it finds a word that starts with a new letter or it runs out of words.

The following code shows the new version of the loop.

// Get the word list for the word's first letter.
int index = (int)word.ToUpper()[0] - (int)'A';

// Find words that start with the same letter.
List<string> match_words = new List<string>();
List<int> match_values = new List<int>();
foreach (string test_word in Words[index])
{
    ...
}

This code subtracts the ASCII value of the letter A from the word’s first letter to get the index of the list that holds words starting with the same letter. It creates output lists for words that match and their edit distance values much as before. It then uses a simple foreach loop to examine the words that have the same first letter as the target word. The omitted code indicated by the ellipsis is the same as in the previous version.

This version is slightly faster (you can’t really tell the difference when you’re running it) but the loop in the FindBestMatches method is easier to understand.

In my next post, I’ll convert some of the searches performed by the program into LINQ. If you have time, you might want to give them a try yourself before you read that post.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, strings | Tagged , , , , , , , , , , , , , , | 1 Comment

Suggest autocomplete words in C#

[autocomplete]

This example shows one way that a program can suggest words for the user.

When I type something on my phone, it displays a list of possible words for autocomplete below the editing area. For example, if I type “gping” it suggests “going” as a possible word that I might be trying to type.

Edit Distance

I don’t know what algorithm the phone uses to suggest words, but this example shows one possible approach. This program uses an “edit distance” algorithm to decide how similar the word you have typed so far is from those in a dictionary. It then suggests the words that are most similar to what you have typed.

To completely understand the program, you need to understand the edit distance algorithm. I wrote an article about this algorithm and you can find it in the DevX article Turn a Parsnip into a Turnip with Edit Distance Algorithms.

You can read the article on DevX so I won’t repeat it here, but the general idea is to build an edit graph that shows the best way to convert one word into another word by inserting and removing letters. The last node in the graph gives the “distance” from the start word to the end word.

When you type into this example’s upper text box, the following event handler executes to display the best matches.

// Find good guesses for this word.
private void txtWord_TextChanged(object sender, EventArgs e)
{
    lstGuesses.Items.Clear();
    string word = txtWord.Text;
    if (word.Length == 0) return;

    // Find the best matches.
    string[] words;
    int[] values;
    FindBestMatches(word, 10, out words, out values);

    // Display the best matches.
    for (int i = 0; i < words.Length; i++)
    {
        lstGuesses.Items.Add(values[i].ToString() +
            '\t' + words[i]);
    }
}

This code clears the program’s ListBox of matches. It then gets the text that you typed and, if that text is blank, returns.

If the text is not blank, the code calls the FindBestMatches method to get arrays containing the best matches and their edit distance values. The results are sorted in increasing order of edit distance value.

The event handler finishes by displaying the edit distance values and the words in the program’s ListBox so you can see them.

All of the hard work is done in the following FindBestMatches method.

// Find the best matches for the typed word.
private void FindBestMatches(string word, int num_matches,
    out string[] words, out int[] values)
{
    // Find words that start with the same letter.
    string start_char = word.Substring(0, 1).ToUpper();
    int start_index = Array.BinarySearch(Words, start_char);
    List<string> match_words = new List<string>();
    List<int> match_values = new List<int>();
    for (int i = start_index + 1; i < Words.Length; i++)
    {
        // Get the next word and make sure it starts
        // with the same letter.
        string test_word = Words[i];
        if (test_word.Substring(0, 1).ToUpper() != start_char)
            break;

        // Consider the next word up to the length
        // of the typed word.
        int max_length = Math.Min(test_word.Length, word.Length);
        string short_word = test_word.Substring(0, max_length);

        // Build the edit graph.
        Node[,] nodes = MakeEditGraph(word, short_word);

        // List the distance.
        int x = nodes.GetUpperBound(0);
        int y = nodes.GetUpperBound(1);
        match_words.Add(test_word);
        match_values.Add(nodes[x, y].distance);
    }

    // Sort the matches by distance, smallest distance first.
    string[] match_words_array = match_words.ToArray();
    int[] match_values_array = match_values.ToArray();
    Array.Sort(match_values_array, match_words_array);

    // Return the desired number of matches.
    int max = Math.Min(num_matches, match_values_array.Length);
    words = new string[max];
    Array.Copy(match_words_array, words, max);
    values = new int[max];
    Array.Copy(match_values_array, values, max);
}

The Words array contains a list of 14,596 words in sorted order. The list includes each letter of the alphabet. For example, the section of words starting with the letter “a” begins with the value “A” to make finding those words easier.

The FindBestMatches method gets the first character in the target word and converts it to upper case. It then uses the Array.BinarySearch method to locate that letter in the word list. (Note that BinarySearch only works if the array is sorted, which it is.) For example, if you have entered “bea,” then the method searches for the word “B.”

Next, the code loops through the array examining words that start with this letter. If you typed “bea,” then it loops over words that start with “b.”

For each word that starts with the given letter, the program truncates the word to the length that you have typed so far. If you typed “bea,” and the program is considering the word “barter,” it truncates that test word to “bar.”

Next, the program builds an edit graph that transforms what you typed into the test word (for example, “bea” into “bar”). It adds the entire test word (barter) and its edit distance to the match_words and match_values lists.

After it has processed all of the words that start with the given letter, the method must return those that have the smallest edit distance values. To do that, it converts the match_words and match_values lists into arrays and uses Array.Sort to sort them. It uses match_words as the array to sort and uses match_values as an array of keys to use while sorting. The Array.Sort method sorts match_values in increasing order and arranges match_words so its values correspond to the numbers in match_values.

Finally, the FindBestMatches method uses Array.Copy to copy the desired number of matches and their edit distances into the words and values output arrays for return to the calling code.

TextBox AutoComplete

The TextBox control also provides some autocomplete features, which the example demonstrates in its bottom text box. When the program starts, the following Form_Load event handler loads the Words array and prepares the bottom text box for autocomplete.

// The dictionary.
private string[] Words;

// Load the dictionary.
private void Form1_Load(object sender, EventArgs e)
{
    Words = File.ReadAllLines("Words.txt");
    txtWord.Text = "absi";

    // Make an autocomplete TextBox.
    AutoCompleteStringCollection word_source =
        new AutoCompleteStringCollection();
    word_source.AddRange(Words);
    txtAuto.AutoCompleteSource = AutoCompleteSource.CustomSource;
    txtAuto.AutoCompleteCustomSource = word_source;
    txtAuto.AutoCompleteMode = AutoCompleteMode.Suggest;
}

The event handler uses the File class’s ReadAllLines method to load the file Words.txt into the Words array. It then enters the test word fragment “absi” in the form’s upper text box.

Next, the code creates an AutoCompleteStringCollection object to hold the text box’s auto completion words. This object behaves mostly like a string collection. (My guess is that internally it uses a trie, also known as a radix tree or prefix tree, to make searching for words easier. For more information on tries, see the Wikipedia article trie.)

The code uses the collection’s AddRange method to add the words in the Words array to it. It then sets the text box’s AutoCompletionSource property to CustomSource, its AutoCompletionCustomSource to the string collection, and its AutoCompleteMode property to Suggest.

After that, the text box works automatically. When you type the beginning of a word, the control displays dropdown list of words that begin with what you have typed. You can use then click on a word or the up and down arrows and press Enter to select a word to place in the text box.

Notes

As an exercise, you might try using LINQ to implement at least part of the FindBestMatches method. It might not work well for selecting words that start with the correct letter (it would loop through every entry in the word list), but it might make pulling out the best matches easier.

This method is not what my phone does but it does produce some reasonable suggestions. There are several ways that it might be improved. For example, this method considers all character exchanges equally likely. If you type “whst” the code considers the possibilities “whit” and “what” equally likely. If you look on the phone’s keypad, however, you’ll see that the s and a keys are right next to each other, so I probably tried to type “what” and just hit the wrong key. You could modify the edit distance algorithm to give preference to swapping letters that were close to each other on the keypad. (I may do that at some point.)

You could also probably improve the algorithm by considering the grammar and meaning of the sentence being typed. For example, if you type “Are you going ton,” then the final word is more likely to be “tonight” than “tonnage.” This is a much bigger topic, so I won’t pursue it.

This example also doesn’t include contractions in its word list. If you type “whats,” then you probably mean “what’s” and adding that to the word list (with some special rules for adding apostrophes) would give you a better result.

Finally, this method assumes that you typed the first letter of the word correctly but that may not always give the best solution. If you type “gello,” then “hello” is probably a better match than “gallop.” (I may work on this issue a bit later, too.)

As you can see, there are several ways you can improve this program, but it does give reasonable suggestions in many cases.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, strings | Tagged , , , , , , , , , , , , , , | 5 Comments

Build an MRU list that uses project settings in C#

[MRU list]

The post Build an MRU list in C# explains how to build an MRU list (most recently used list). That example stores recently used files in the system Registry. This example does the same thing but it stores file information in the program’s settings. It stores the file paths as a single semi-color delimited string in the project settings.


To create the setting, open the Project menu and select Settings from the bottom. On the Settings tab, create a new setting named FilePaths as shown in the following picture.


[MRU list]

Now the program can use that setting. Unfortunately it’s the form’s designer-generated code that defines the setting. The previous example uses the MruList class to manage its MRU list, so the MruList object that manages the list cannot easily use the form’s settings.

To make using the setting easy, I added the following methods to the program’s Form1 class.

// Methods to get and save the MRU list file paths.
public string GetMruFilePaths()
{
    return Properties.Settings.Default.FilePaths;
}

public void SaveMruFilePaths(string paths)
{
    Properties.Settings.Default.FilePaths = paths;
    Properties.Settings.Default.Save();
}

The GetMruFilePaths method simply returns the FilePaths setting. The SaveMruFilePaths method sets the FilePaths value and then saves it.

The new version of the MruList class now needs an instance of the Form1 class so it can use these two new methods. The following code shows how the new MruList class begins.

public class MruList
{
    // The application's form.
    private Form1 TheForm = null;
    ...
    // Constructor.
    public MruList(Form1 the_form, ToolStripMenuItem menu,
        int num_files)
    {
        TheForm = the_form;
        ...
    }
    ...
}

The class now defines a new field TheForm to hold the instance of the form that is using the MruList object. The class’s constructor saves the form instance in that field.

The rest of the MruList class works much as it did before except it uses the new setting instead of the Registry. The following code shows the new version of the LoadFiles method.

// Load saved items from the project settings.
private void LoadFiles()
{
    // Get the file paths from the project settings.
    string all_paths = TheForm.GetMruFilePaths();
    string[] separators = { ";" };
    foreach (string path in all_paths.Split(
        separators, StringSplitOptions.RemoveEmptyEntries))
            FileInfos.Add(new FileInfo(path));
}

This version uses the form’s GetMruFilePaths to get the file path string. It splits the semi-colon delimited file paths, uses them to create FileInfo objects, and adds the objects to the FileInfos list.

The following code shows the new version of the SaveFiles method.

// Save the current items in the settings.
private void SaveFiles()
{
    // Save the current entries.
    string all_paths = "";
    foreach (FileInfo file_info in FileInfos)
        all_paths += file_info.FullName + ";";
    TheForm.SaveMruFilePaths(all_paths);
}

This method loops through the FileInfos entries and adds their full names (including paths) to a semi-colon delimited string. After building the string, the method calls the form’s SaveMruFilePaths method to update the setting.

Download the example and see the previous example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in files, programs, registry | Tagged , , , , , , , , , , , , , | Leave a comment

Build an MRU list in C#

[MRU list]

An MRU list (most recently used file list) displays in the File menu the files that a program has used most recently. If the user selects a file from the menu, the program reopens that file.

This example builds an MruList class that a program can use to make providing an MRU list easier. This example is fairly involved but many of its pieces are interesting so I’m going to describe them all, divided into the following sections.


Initialization

The following code shows variables that the MruList class uses to maintain its list of file names.

// The application's name.
private string ApplicationName;

// A list of the files.
private int NumFiles;
private List<FileInfo> FileInfos;

// The File menu.
private ToolStripMenuItem MyMenu;

// The menu items we use to display files.
private ToolStripSeparator Separator;
private ToolStripMenuItem[] MenuItems;

// Raised when the user selects a file from the MRU list.
public delegate void FileSelectedEventHandler(FileInfo file_info);
public event FileSelectedEventHandler FileSelected;

The ApplicationName variable holds the application’s name. This is used to store file information in the system Registry.

The NumFiles variable holds the number of files that the list can hold, not the number of files that it is actually holding at the time. Most programs set this value to 4 so the MRU list can hold at most 4 files.

The FileInfos list contains FileInfo objects describing the files currently in the list.

I use FileInfo objects because they easily store a file’s full path and short file name. You could use some other type of object instead if you wanted the MRU list to represent something other than files. For example, if you want to display a list of recently used engineering diagrams, you might want this list to hold objects that store the diagrams’ titles, file locations, and other data.

The MyMenu variable holds a reference to the program’s menu that should display the list. Normally that’s the program’s File menu. The Separator and MenuItems variables hold references to the menu items created by the MruList.

The FileSelectedEventHandler delegate defines the type of the FileSelected event that the MruList raises when the user selects a file from the list.

The following code shows the class’s constructor, which initializes the list.

// Constructor.
public MruList(ToolStripMenuItem menu, int num_files)
{
    ApplicationName = application_name;
    MyMenu = menu;
    NumFiles = num_files;
    FileInfos = new List<FileInfo>();

    // Make a separator.
    Separator = new ToolStripSeparator();
    Separator.Visible = false;
    MyMenu.DropDownItems.Add(Separator);

    // Make the menu items we may later need.
    MenuItems = new ToolStripMenuItem[NumFiles + 1];
    for (int i = 0; i < NumFiles; i++)
    {
        MenuItems[i] = new ToolStripMenuItem();
        MenuItems[i].Visible = false;
        MyMenu.DropDownItems.Add(MenuItems[i]);
    }

    // Reload items from the registry.
    LoadFiles();

    // Display the items.
    ShowFiles();
}

The constructor saves the application’s name, the menu that should hold the MRU list items, and the number of items that the list can hold. It then creates a new FileInfos list.

Next, the constructor creates the separator and menu items that it might need to display later. It finishes by calling LoadFiles to load saved files from the Registry and ShowFiles to display the correct menu items.

The following code shows the LoadFiles method.

// Load saved items from the Registry.
private void LoadFiles()
{
    // Reload items from the registry.
    for (int i = 0; i < NumFiles; i++)
    {
        string file_name = (string)RegistryTools.GetSetting(
            ApplicationName, "FilePath" + i.ToString(), "");
        if (file_name != "")
        {
            FileInfos.Add(new FileInfo(file_name));
        }
    }           
}

The LoadFiles method uses the RegistryTools class’s GetSetting method to load file names stored in the Registry. The following code shows the GetSetting method.

// Get a value.
public static object GetSetting(string app_name, string name,
    object default_value)
{
    RegistryKey reg_key =
        Registry.CurrentUser.OpenSubKey("Software", true);
    RegistryKey sub_key = reg_key.CreateSubKey(app_name);
    return sub_key.GetValue(name, default_value);
}

The GetSetting method opens the current user’s Software hive, gets a subkey named after the application, and then gets the desired setting, which in this case is a file path.

After the constructor has loaded saved file information from the Registry, it calls the following ShowFiles method to prepare the menu items for use.

// Display the files in the menu items.
private void ShowFiles()
{
    Separator.Visible = (FileInfos.Count > 0);
    for (int i = 0; i < FileInfos.Count; i++)
    {
        MenuItems[i].Text = string.Format("&{0} {1}",
            i + 1, FileInfos[i].Name);
        MenuItems[i].Visible = true;
        MenuItems[i].Tag = FileInfos[i];
        MenuItems[i].Click -= File_Click;
        MenuItems[i].Click += File_Click;
    }
    for (int i = FileInfos.Count; i < NumFiles; i++)
    {
        MenuItems[i].Visible = false;
        MenuItems[i].Click -= File_Click;
    }

    // Update the Registry.
    SaveFiles();
}

The ShowFiles method starts by displaying the separator if there are any files in the list. It then loops through the FileInfo objects in the FileInfos list. For each object, it sets the corresponding menu item’s Text, Visible, and Tag properties. It also attaches the File_Click event handler to the menu item’s Click event. (It first removes any previously installed event handler so the event handler isn’t installed twice.)

The code then hides any menu items that are not needed.

The following code shows the SaveFiles method that saves the current file list in the Registry.

// Save the current items in the Registry.
private void SaveFiles()
{
    // Delete the saved entries.
    for (int i = 0; i < NumFiles; i++)
    {
        RegistryTools.DeleteSetting(ApplicationName,
            "FilePath" + i.ToString());
    }

    // Save the current entries.
    int index = 0;
    foreach (FileInfo file_info in FileInfos)
    {
        RegistryTools.SaveSetting(ApplicationName,
            "FilePath" + index.ToString(), file_info.FullName);
        index++;
    }
}

The SaveFiles method deletes any existing Registry entries and then saves the current files’ full paths.


Adding and Removing Files

When the main program opens a file or saves a newly created file, it should call the MruLists object’s AddFile method shown in the following code.

// Add a file to the list, rearranging if necessary.
public void AddFile(string file_name)
{
    // Remove the file from the list.
    RemoveFileInfo(file_name);

    // Add the file to the beginning of the list.
    FileInfos.Insert(0, new FileInfo(file_name));

    // If we have too many items, remove the last one.
    if (FileInfos.Count > NumFiles) FileInfos.RemoveAt(NumFiles);

    // Display the files.
    ShowFiles();

    // Update the Registry.
    SaveFiles();
}

This method calls the RemoveFileInfo method to remove the file from the FileInfos list and then inserts the new file at the beginning of the list. This prevents the list from containing duplicates. Next, if the list contains too many files, the code removes the last item. The method finishes by calling ShowFiles to update the menu items and SaveFiles to save the current list in the Registry.

The following code shows the RemoveFileInfo method.

// Remove a file's info from the list.
private void RemoveFileInfo(string file_name)
{
    // Remove occurrences of the file's information from the list.
    for (int i = FileInfos.Count - 1; i >= 0; i--)
    {
        if (FileInfos[i].FullName == file_name)
            FileInfos.RemoveAt(i);
    }
}

This method loops through the file entries from back to front. If it finds an entry with the target file name, it removes that entry. (The code searches the list from back to front so removing an item doesn’t renumber the remaining items.) Note that the code cannot simply use the list’s Remove method because that method only looks for matching FileInfo objects not objects that are different but that represent the same file.

The RemoveFile method shown in the following code removes a file from the MRU list. The main program should call this method whenever it needs to remove a file from the list. For example, if the program tries to open a file and fails, most programs remove that file from the MRU list. (Strangely, some programs such as Visual Studio prompt the user to ask if it should remove the non-existent project from the MRU list. I guess they’re assuming that the project may magically reappear later.)

// Remove a file from the list, rearranging if necessary.
public void RemoveFile(string file_name)
{
    // Remove the file from the list.
    RemoveFileInfo(file_name);

    // Display the files.
    ShowFiles();

    // Update the Registry.
    SaveFiles();
}

This method calls RemoveFileInfo to remove the file from the list. It then calls ShowFiles to update the menu items and SaveFiles to update the Registry.


Handling Events

When the user selects a file from the MRU list, the following File_Click event handler executes.

// The user selected a file from the menu.
private void File_Click(object sender, EventArgs e)
{
    // Don't bother if no one wants to catch the event.
    if (FileSelected != null)
    {
        // Get the corresponding FileInfo object.
        ToolStripMenuItem menu_item = sender as ToolStripMenuItem;
        FileInfo file_info = menu_item.Tag as FileInfo;

        // Raise the event.
        FileSelected(file_info.FullName);
    }
}

This method simply raises the FileSelected event, passing it the FileInfo object representing the selected file. The program should then reload that file or do whatever is appropriate for the application.


Using MruList

The example program uses the following code to define and initialize its MruList variable.

// The MruList.
MruList MyMruList;

// Make the MruList.
private void Form1_Load(object sender, EventArgs e)
{
    MyMruList = new MruList("howto_mru_list", mnuFile, 4);
    MyMruList.FileSelected += MyMruList_FileSelected;
}

The program defines the variable MyMruList at the form level so all of its methods can use it. The form’s Load event handler initializes the MruList and registers the MyMruList_FileSelected event handler to handle the list’s FileSelected event.

The following code shows how the program tries to open a file.

// Open a file and add it to the MRU list.
private void OpenFile(string file_name)
{
    try
    {
        // Load the file.
        rchFile.Clear();
        if (file_name.ToLower().EndsWith(".rtf"))
        {
            rchFile.LoadFile(file_name);
        }
        else
        {
            rchFile.Text = File.ReadAllText(file_name);
        }

        // Add the file to the MRU list.
        MyMruList.AddFile(file_name);
    }
    catch (Exception ex)
    {
        // Remove the file from the MRU list.
        MyMruList.RemoveFile(file_name);

        // Tell the user what happened.
        MessageBox.Show(ex.Message);
    }
}

If the file’s name ends with .rtf, the program uses its RichTextBox control’s LoadFile method to load the file as an RTF file. If the name doesn’t end in .rtf, the program loads the file as text. If it successfully loads the file, the program calls the MruList object’s AddFile method to add the file to the MRU list.

If the program fails to load the file, it calls the MruList object’s RemoveFile method to remove the file from the MRU list if it is present. It then tells the user that there was a problem.

If the user selects a file from the MRU list, the MruList object raises its FileSelected event and the following event handler executes.

// Open a file selected from the MRU list.
private void MyMruList_FileSelected(string file_name)
{
    OpenFile(file_name);
}

This code simply calls the previous OpenFile method to open the selected file.

This program doesn’t have Save or Save As commands, but if it did they would call the MruList object’s AddFile method whenever the program saved a new or renamed file.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in files, programs, registry | Tagged , , , , , , , , , , , , , | 3 Comments

Useful tools and links

[tools]

This page lists some useful tools and links that I visit regularly. If you want to recommend others, please post them in the comments.


Tools

@icon sushi

Image to icon converter that supports the mask editor and the creation of Windows XP 32bit/Windows Vista format icons.

Inkscape

An Open Source vector graphics editor, with capabilities similar to Illustrator, CorelDraw, or Xara X, using the W3C standard Scalable Vector Graphics (SVG) file format.

FileZilla Client

A free FTP tool that lets you easily upload and download files.

SimpleOCR

A simple, free OCR (optical character recognition) program that can extract text from scanned documents or images.

Programming Sites

PInvoke.net

A Wiki that provides declarations for API functions, user-defined types used by API functions, and other information related to calling Win32 and other unmanaged APIs from managed code (written in languages such as C# or VB.NET).

C# Helper

My C# site containing tips, tricks, and examples in C#.

VB Helper

My Visual Basic site containing thousands of tips, tricks, and examples in Visual Basic “Classic” and Visual Basic .NET.

Wolfram Mathworld

This site bills itself as “The Web’s Most Extensive Mathematics Resource” and it’s probably right! A great resource for all things mathematical.

Forums

Follow me on Twitter   RSS feed   Donate




Posted in miscellany, tools | Tagged , , , , , , , , | Leave a comment

Get information about Windows shortcuts in C#

[shortcuts]

This post explains how to get information about Windows shortcuts. The post has two parts. The first explains how to get the information. The second explains how to create shortcuts.

Getting Shortcut Information

This example uses the following GetShortcutInfo method to get information about shortcuts.

// Get information about this link.
// Return an error message if there's a problem.
private string GetShortcutInfo(string full_name,
    out string name, out string path, out string descr,
    out string working_dir, out string args)
{
    name = "";
    path = "";
    descr = "";
    working_dir = "";
    args = "";
    try
    {
        // Make a Shell object.
        Shell32.Shell shell = new Shell32.Shell();

        // Get the shortcut's folder and name.
        string shortcut_path =
            full_name.Substring(0, full_name.LastIndexOf("\\"));
        string shortcut_name =
            full_name.Substring(full_name.LastIndexOf("\\") + 1);
        if (!shortcut_name.EndsWith(".lnk"))
            shortcut_name += ".lnk";

        // Get the shortcut's folder.
        Shell32.Folder shortcut_folder =
            shell.NameSpace(shortcut_path);

        // Get the shortcut's file.
        Shell32.FolderItem folder_item =
            shortcut_folder.Items().Item(shortcut_name);

        if (folder_item == null)
            return "Cannot find shortcut file '" + full_name + "'";                
        if (!folder_item.IsLink)
            return "File '" + full_name + "' isn't a shortcut.";

        // Display the shortcut's information.
        Shell32.ShellLinkObject lnk =
            (Shell32.ShellLinkObject)folder_item.GetLink;
        name = folder_item.Name;
        descr = lnk.Description;
        path = lnk.Path;
        working_dir = lnk.WorkingDirectory;
        args = lnk.Arguments;
        return "";
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
}

The program first creates a Shell32.Shell object to work with the Windows shell. It uses that object’s NameSpace method to get a Folder object representing the folder that contains the shortcut of interest. It then uses that object’s Items collection to get the shortcut’s name and a ShellLinkObject representing the shortcut. From that object, the method finally gets the rest of the information it needs.

If you look closely at the picture, you’ll see that in that example the shortcut’s …

  • … name was “Signature”
  • … description was “Open the file sig.txt”
  • … path was the path to the WordPad executable
  • … working directory was my desktop
  • … arguments was “sig.txt”

So when I double-click this shortcut, it starts WordPad and opens the file sig.txt on my desktop.

Making Shortcuts

I tested this in Windows 10. The details may differ slightly in other versions of Windows.

To create a shortcut, right-click on the desktop or in a folder, select New > Shortcut as shown below.


[shortcuts]

This makes Windows display the following dialog.


[shortcuts]

Enter the path to the program that you want to execute followed by any parameters that you want to pass into that program. In this example, the program is wordpad.exe and the parameter is the name of the file (sig.txt) that WordPad should edit.

Note that paths and parameters that include spaces should be surrounded by quotes as shown in the picture.

After you fill in the program and its parameters, click Next to display the following dialog.


[shortcuts]

Enter the name that you want to give the shortcut here and click Finish to create the shortcut.

To fine-tune the shortcut, right-click on it and select Properties to display the following dialog.


[shortcuts]

Here you can enter the start directory (in the Start In field) and the description (in the Comment field).


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in system | Tagged , , , , , , , , , | 3 Comments

List the shortcuts in the computer’s network neighborhood in C#

[network neighborhood]

This example uses the Windows Script Host to list shortcuts in the network neighborhood. Before the program can use the Windows Script Host, you must add a reference to the COM object “Windows Script Host Object Model.” To make using that library easier, the program includes the following two using directives.

using System.IO;
using IWshRuntimeLibrary;

The following code shows how the program builds its list of shortcuts.

private void Form1_Load(object sender, EventArgs e)
{
    // Make a Windows Script Host Shell object.
    IWshShell_Class wsh_shell = new IWshShell_Class();

    // Find the Nethood folder.
    IWshCollection special_folders = wsh_shell.SpecialFolders;
    object path_name = "Nethood";
    string nethood_path =
        special_folders.Item(ref path_name).ToString();
    DirectoryInfo di = new DirectoryInfo(nethood_path);

    // Enumerate Nethood's subdirectories.
    foreach (DirectoryInfo subdir in di.GetDirectories())
    {
        lstLinks.Items.Add(subdir.Name);
    }
}

The code creates an IWshShell_Class object and gets the Nethood entry from its SpecialFolders collection. It converts that value into a string to get the path to the network neighborhood folder. It then makes a DirectoryInfo object representing that folder.

Finally, the code loops through the folder’s subdirectories, listing them in the lstLinks ListBox.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in miscellany, network | Tagged , , , , , , , , , , , | 2 Comments