The WinRT runtime provides spell check and auto complete functions in controls like the TextBox and the RichTextBox out of the box. To do so, it hooks into a Windows 8 default system Dictionary that’s installed based on the language you choose for your Windows 8 setup. This provides red squiggles under incorrectly spelt words and provides a right click context menu with suggestions for fixing the error. This also provides an ‘Add to Dictionary’ functionality that adds new words to the Dictionary. However, there is no way to hook a custom dictionary into this setup. To enable this, all you have to do is set two properties on the XAML Control
Recently I received a feature request for my Twitter client. The requested behavior was while typing a Tweet, when user types in @, a list of Friends should come up allowing user to select from the DropDown list. This was an interesting challenge and I decided to convert my attempt into this post.
The problem at hand
I broke down the problem as follows:
1. Identify screen location of Cursor in a textbox
2. Show Popup menu control at the above location
3. Populate popup menu with relevant menu items
4. Update text when menu item is selected.
Hacking up a Solution
First thing I did was attacked the problem in-place. I hacked the following code together by adding a code behind event to a text box directly.
1. if (updateText.SelectionStart > 0)
2. {
3. if (updateText.Text.Substring(updateText.SelectionStart-1, 1).EndsWith("@"))
4. {
5. Rect position = updateText.GetRectFromCharacterIndex(updateText.SelectionStart-1, true);
6. var transform = updateText.TransformToVisual(this);
7. var point = transform.TransformPoint(new Point(position.Left, position.Bottom));
8. PopupMenu popupMenu = new PopupMenu();
9. popupMenu.Commands.Add(new UICommand("@Owned", (IUICommand command) =>
10. {
11. var oldSelectionStart = updateText.SelectionStart;
12. var newSelectionStart = oldSelectionStart + command.Label.TrimStart('@').Length;
13. updateText.Text = updateText.Text.Insert(updateText.SelectionStart, command.Label.TrimStart('@'));
14. updateText.SelectionStart = newSelectionStart;
15. }));
16. await popupMenu.ShowForSelectionAsync(new Rect(point, point), Placement.Below);
17. }
18. }
Here ‘updateText’ is the Name of a TextBox control in XAML. In the above code, line 5 gets position of the cursor with respect to the top left corner of the TextBox. As you can see, the text box’s SelectionStart property always return the current cursor position. We pick the last typed character by moving back one character.
Next in line 6 we get the Transformation matrix for the text box control with respect to the form (this).
In line 7, we get the point location below the cursor with respect to the entire screen.
Line 8 through 15 we are hardcoding one Menu Item in a popup menu.
Line 10 through 14 is an anonymous function that we assign as the handler for the Menu Item selection command. The event handler captures the current cursor position, calculates the final cursor position and inserts the text from the selected command’s label.
Finally in Line 16, we show the popup menu at the point and set the placement position as Below, this shows the Popup Menu below the text (default position for popup menu is above the point specified).
This is what it looks like, it pops up as soon as I press @.

With the hacked up Proof-of-concept done, let us now build a reusable component that can be just dropped in and configured.
Building a Reusable TextBox Control
Step 1: We start off with new Application using a Standard Windows 8 Blank Application template

This is our Sample Application that hosts our Custom Control.
Step 2: We add a Windows 8 Runtime Component project called DynamicAutoCompleteTextBox so that our XAML control is usable in VB.NET and other .NET languages.
Step 3: Setting up the AutoCompleteTextBox control - Add a new cs file called AutoCompleteTextBox.cs. The code is split into three parts
The Declarations
Our control inherits the behavior of the TextBox control completely and it also implements the INotifyPropertyChange interface.
We have two Properties
- MenuTrigger : Holds the character that triggers the popup menu
- Dictionary: The IEnumerable that holds list of all possible Auto Complete options. In my Twitter client case, it would hold list of all the followers
Other Fields: Other fields, _isTriggered, _triggerDistance, _triggerLocation are used to calculate and track the current position of the cursor as well as the last invoked MenuTrigger.
The Constructor, initializes the collections and sets up the TextChanged event handler. There is no OnTextChanged method that can be overridden for the TextChanged event hence we use the handler. Alternately we can override the OnKeyUp method for finer grained control.
The EventHandler
1. void AutoCompleteTextBox_TextChanged(object sender, TextChangedEventArgs e)
2. {
3. CheckIfTriggered();
4. Rect position = this.GetRectFromCharacterIndex(this.SelectionStart - 1, true);
5. if (_isTriggered)
6. {
7. popupMenu.Commands.Clear();
8. string searchStr = this.Text.Substring(_triggerLocation, _triggerDistance)
9. .TrimStart(MenuTrigger.ToCharArray()[0]);
10. IEnumerable<string> items = _dictionary.Where(f => f.StartsWith(searchStr)).Take<string>(6);
11. foreach (var item in items)
12. {
13. popupMenu.Commands.Add(new UICommand(item, (IUICommand command) =>
14. {
15. var oldSelectionStart = this.SelectionStart;
16. string remainder = command.Label.Substring(searchStr.Length);
17. var newSelectionStart = oldSelectionStart + remainder.Length;
18. this.Text = this.Text.Insert(this.SelectionStart, remainder);
19. this.SelectionStart = newSelectionStart;
20. _triggerLocation = 0;
21. _triggerDistance = 1;
22. }));
23. }
24. var transform = this.TransformToVisual((UIElement)this.Parent);
25. var point = transform.TransformPoint(new Point(position.Left, position.Bottom));
26. try
27. {
28. popupMenu.ShowForSelectionAsync(new Rect(point, point), Placement.Below).AsTask();
29. }
30. catch (InvalidOperationException ex)
31. {
32. }
33. }
34. }
In the above code, we first check if the PopupMenu has already been triggered, for example user typed @ and then went ahead and typed @Su. The Popup Menu should shorten the list appropriately. Based on how much the user has typed after the Trigger Character, we extract the searchStr and filter our data source accordingly at line 10.
Based on the results returned, we take the top 6 and convert them into popup MenuItem commands.
The Trigger Logic
Final piece of code is the Trigger Logic in the method CheckIfTriggered()
1. if (this.SelectionStart > 0)
2. {
3. if (!_isTriggered)
4. {
5. _isTriggered = this.Text.Substring(this.SelectionStart -
_triggerDistance, 1).EndsWith(MenuTrigger.ToString());
6. if (_isTriggered)
7. {
8. _triggerLocation = this.SelectionStart - _triggerDistance;
9. }
10. }
11. else
12. {
13. _isTriggered = !this.Text.Substring(this.SelectionStart - 1, 1).Equals(" ");
14. if (_isTriggered)
15. {
16. _triggerDistance = this.SelectionStart - _triggerLocation;
17. }
18. else
19. {
20. _triggerLocation = 0;
21. _triggerDistance = 1;
22. }
23. }
24. }
It starts off by checking if atleast one character has been typed (or if the caret is at the beginning of the textbox). If the popup has not yet been triggered, it checks if the last character was a trigger character. If so, the _triggerLocation value is set to the current position.
If the popup has already been triggered, it checks if the current character is a space, that cancels the trigger. If the current character is not space, it calculates the number of characters typed since the trigger character and saves it in _triggerDistance.
That pretty much covers the ‘logic’, now let’s see how we can use the Control.
Using the Custom Control
In the SampleApp created earlier, add reference to the DynamicAutoCompleteTextBox project.
In the MainPage.xaml file add reference to the DynamicAutoCompleteTextBox’s namespace. Next we have a very simple XAML Grid layout. It has only two items, the Header Text and a DynamicAutoCompleteTextBox
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="140"></RowDefinition>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="2*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Left"
Style="{StaticResource PageHeaderTextStyle}"
Margin="120,54,0,0"
TextWrapping="Wrap"
Text="Dynamic AutoComplete TextBox Sample"
VerticalAlignment="Top"
Height="68" Width="1003"/>
<dtb:AutoCompleteTextBox x:Name="DynamicAutoCompleteTextControl"
Grid.Row="1"
Margin="120,20,120,20"
Text=""
MenuTrigger="@">
</dtb:AutoCompleteTextBox>
</Grid>
As you can see, we have setup the MenuTrigger to be ‘@’ in the XAML itself. In the code behind, all we have to do is assign the DataSource for the picker. We do this in the OnNavigatedTo event as follows
protected override void OnNavigatedTo(NavigationEventArgs e)
{
DynamicAutoCompleteTextControl.Dictionary =
DynamicAutoCompleteTextBox.AutoCompleteTextBox.GetSortedDictionaryFromString(
@"Suprotim,Minal,Sumit,Pravin,Mahesh");
}
We use the helper method to get a SortedList from the comma separated string input that we provide.
We are all set. Now let’s run the application. As soon as we press @, the complete list comes up.
When we continue typing, the list gets reduced
And when we select a Name and hit enter, the word is completed
Gotchas to be aware of
- If you look closely, we are loading only the top 6 strings that match our results. You’ll be wondering why only 6. Well like it or not, 6 is the hard set limit for number of Popup Menu Items you can have. If you try to add more, you’ll get a runtime exception.
- An empty try… catch block suppressing InvalidOperationException. Well this one is kind of goofy, the ShowForSelectAsync method is (as the name implies) Async. And if you try to hit the MenuTrigger character (e.g. @) too many times quickly, you have overlapping Async calls and WinRT throws an InvalidOperationException. The exception handler is a temp workaround.
Conclusion
In this sample, we created a Custom WinRT Component that adds an AutoComplete like functionality to a TextBox. Overriding functionality of framework controls often reduce confidence in the customization. However, as you can see there is no code that interferes with the working of a normal TextBox so our ‘wrapper’ is a pretty lightweight one.
The code for this is up on GitHub at https://github.com/dotnetcurry/winrt-autocomplete-textbox-component. Feel free to improve upon it and share feedback if you feel like it. Pull requests are welcome.
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Sumit is a .NET consultant and has been working on Microsoft Technologies since his college days. He edits, he codes and he manages content when at work. C# is his first love, but he is often seen flirting with Java and Objective C. You can follow him on twitter at @
sumitkm or email him at sumitkm [at] gmail