Click here to Skip to main content
15,883,883 members
Articles / Desktop Programming / WPF
Article

Intellisense-like Method Selection Pop-up Window

Rate me:
Please Sign up or sign in to vote.
3.84/5 (16 votes)
9 Jan 2008CPOL6 min read 78.2K   70   15
Intellisense-like method selection pop-up window

Introduction

Today, intellisense becomes a must in a whole range of different applications. Applications become smarter due to this enormous unharvested CPU power that is idling on your desktop and the applications find a way to use it.

Using the Code

The technique that I'm going to explain here is a user-friendly way to hint or help the user with her or his next actions while she or he works on the primary task. For example:

  1. The user types a text, a spell checker (which is at your fingertips in WPF) finds the misspelled words and the application can suggest a choice to the user.
  2. The user types a script and the application lets the user choose a next command from a list of possibilities.
  3. Etc.

We can go on with the list, but, I hope, you got the idea. In my case, I'll show you how to help the user construct a LINQ query.

clip_image001

To keep the explanation short and to the point, let's scope the problem: the application will show a popup only when the user types a predefined keyword and presses a "." (dot). The popup will contain a list of possible methods that can be applied to the object.

Let's create a class that will provide a list of the item's methods. Let's say that the item is of type SyndicationItem. So we'll use reflection methods (if you don't know what reflection is, you should look into it, it's very useful in a whole range of applications) to find all the methods of the SyndicationItem type. You can use a .GetType() of an object or you can use a simple typeof(...) construct. The following code snippet does just this:

C#
 1: public class ItemMethods
 2: {
 3:     public IEnumerable<string> Methods
 4:     {
 5:         get
 6:         {
 7:             Type type = typeof(SyndicationItem);
 8:
 9:             var methods = (from method in
10:                           type.GetMethods(System.Reflection.BindingFlags.Public |
11:                           System.Reflection.BindingFlags.Instance)
12:                           select method.Name).ToList<string>();
13:
14:             return methods;
15:         }
16:     }
17: }

Now let's go to Blend and create our popup. Select a Popup control from the controls list and drop it anywhere on the form (better to put it on the object that it'll be binded to or, at least, on its parent layout element). You can put any single control on the popup, but, if you'd like to extend your functionality later, put some layout element. In my example, you can see that I've put a Grid element. The ListBox that will be populated with our methods is positioned on the Grid. You have to set the following properties on the Popup:

  1. Placement="Bottom" - This tells the system to position the Popup at the bottom of the element it will be referring to. The same way as Visual Studio shows methods' intellisense below and to the right of the "." that you press.
  2. StaysOpen ="False" - This tells the WPF to close the Popup if it loses focus.
  3. IsOpen="False" - This hides the Popup on form initialization.

To make the ListBox display method names correctly, you have to bind it to the ItemMethods class that we've defined previously. Don't forget to compile your code - this will allow the Expression Blend to find it and help you with binding.

  1. Go to the Advanced Properties of the ItemsSource property (small rectangle to the right of the property value box) and click on Data Binding.
  2. Add the ItemMethods object from the list that will be presented to you after you press "+ CLR Object" button. This will add your new Data Source.
  3. On the Fields tree (on the right side), select the Methods collection.

If you did everything right - you'll see a list of SyndicationItem methods in the Popup. Note that Expression Blend has added a new data source for you...

C#
1: <ObjectDataProvider x:Key="ItemMethodsDS"
2:     d:IsDataSource="True"
3:     ObjectType="{x:Type Castile:ItemMethods}"/>

... and added a bind source to your ListBox: ItemsSource="{Binding Path=Methods, Mode=OneWay, Source={StaticResource ItemMethodsDS}}".

I'm preparing a big article about data binding (it's a huge, powerful and extremely important part of WPF that you should now), but in a nutshell, this specific string tells WPF to take the items from ItemMethodsDS data source from a property Methods.

At this point, you're free to customize your ListBox. For example, I've decided to remove the scrollbars (HorizontalScrollBarVisibility="Hidden" and VerticalScrollBarVisibility="Hidden") and make the ListBox searchable (when you start typing the letters, it'll jump to the item that starts with these letters).

C#
 1: <Popup x:Name="popupLinqMethods" Height="Auto" Width="150"
 2:        StaysOpen="False" Placement="Bottom" IsOpen="false"
 3:        d:LayoutOverrides="Width, Margin"
 4:        HorizontalAlignment="Left">
 5:     <Grid Width="Auto" Height="Auto">
 6:         <ListBox x:Name="lstMethodsSelection"
 7:              ScrollViewer.HorizontalScrollBarVisibility="Hidden"
 8:              ScrollViewer.VerticalScrollBarVisibility="Hidden"
 9:              KeyDown="OnMethodsSelectionKeyDown"
10:              SelectedIndex="0"
11:              IsTextSearchEnabled="True"
12:              ItemsSource="{Binding Path=Methods, Mode=OneWay,
                     Source={StaticResource ItemMethodsDS}}"
13:              ItemTemplate="{DynamicResource ListSyndicationObjectMethodsTemplate}"
14:          />
15:     </Grid>
16: </Popup>

To make the Popup disappear when you press Enter (after selecting a desirable item) or when you press Escape, we need to let the ListBox handle these events. Hence you need an event handler (note the KeyDown="OnMethodsSelectionKeyDown" property in the XAML above). To do this, you have to put an event handler name in the KeyDown event on the event's list of the ListBox. After you'll press Enter, you'll be taken back to Visual Studio to edit your event handler. Here is how it can look like:

C#
 1: private void OnMethodsSelectionKeyDown
          (object sender, System.Windows.Input.KeyEventArgs e)
 2: {
 3:     switch (e.Key)
 4:     {
 5:         case System.Windows.Input.Key.Enter:
 6:             // Hide the Popup
 7:             popupLinqMethods.IsOpen = false;
 8:
 9:             ListBox lb = sender as ListBox;
10:             if (lb == null)
11:                 return;
12:
13:             // Get the selected item value
14:             string methodName = lb.SelectedItem.ToString();
15:
16:             // Save the Caret position
17:             int i = txtFilterText.CaretIndex;
18:
19:             // Add text to the text
20:             txtFilterText.Text = txtFilterText.Text.Insert(i, methodName);
21:
22:             // Move the caret to the end of the added text
23:             txtFilterText.CaretIndex = i + methodName.Length;
24:
25:             // Move focus back to the text box.
                // This will auto-hide the PopUp due to StaysOpen="false"
26:             txtFilterText.Focus();
27:             break;
28:
29:         case System.Windows.Input.Key.Escape:
30:             // Hide the Popup
31:             popupLinqMethods.IsOpen = false;
32:             break;
33:     }
34: }

All what we did so far was the control of the behavior and the content of the Popup, but we did nothing to trigger its appearance. Here your imagination is your limit. To make this sample work, I've decided to show the Popup when the user will type the word item. in the script editor. As soon as he'll press "." - the Popup will appear, allowing him to insert the selected method back to the script. The following KeyUp text box event handler's code allows me to do just that. Note that the Key.OemPeriod value is used to identify the pressed "." (dot). It wasn't that obvious to me. Note, as well, the hardcoded item. hot-word. This is done to simplify the explanation. In your code, it should be modified to reflect your needs.

C#
 1: private void OnFilterTextKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
 2: {
 3:     TextBox txtBox = sender as TextBox;
 4:     if ((txtBox == null) || (txtBox.CaretIndex == 0))
 5:         return;
 6:
 7:     // Check for a predefined hot-key
 8:     if (e.Key != System.Windows.Input.Key.OemPeriod)
 9:         return;
10:
11:     // Get the last word in the text (preceding the ".")
12:     string txt = txtBox.Text;
13:     int wordStart = txt.LastIndexOf(' ', txtBox.CaretIndex - 1);
14:     if (wordStart == -1)
15:         wordStart = 0;
16:
17:     string lastWord = txt.Substring(wordStart, txtBox.CaretIndex - wordStart);
18:
19:     // Check if the last word equal to the one we're waiting
20:     if (lastWord.Trim().ToLower() != "item.")
21:         return;
22:
23:     ShowMethodsPopup(txtBox.GetRectFromCharacterIndex(txtBox.CaretIndex, true));
24: }
25:
26: private void ShowMethodsPopup(Rect placementRect)
27: {
28:     popupLinqMethods.PlacementTarget = txtFilterText;
29:     popupLinqMethods.PlacementRectangle = placementRect;
30:     popupLinqMethods.IsOpen = true;
31:     lstMethodsSelection.SelectedIndex = 0;
32:     lstMethodsSelection.Focus();
33: }

It's a little tricky to place the Popup exactly at the desired place - it has almost unlimited selection of different placement combinations (see MSDN help for the Placement property. In my case I needed, as I've mentioned above, to open it to the lower right from the pressed ".". So my Popup has the Placement="Bottom" - this will make it appear under the ".".

How to find the "." location - you'll ask? That's a great question!

It's not that easy in Windows Forms, but was super easy in WPF. The character location in a TextBox can be found by calling the GetrectFromCharacterIndex method. But this will give you the coordinates of the character inside the TextBox and the Popup will open in the incorrect place, because it'll calculate its location relative to its parent Layout element. This is not what we need. To compensate the calculation, we need to point the Popup PlacementTarget to our TextBox (see code above: PlacementTarget = txtFilterText).

Now we're done! Start your script editor and start typing. You should see something very similar to the picture I've posted at the beginning of this article.

Points of Interest

  • Reflection
  • WPF

History

  • 9th January, 2008: Initial post

This is a single article from a series. Stay tuned.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Symantec
United States United States
http://www.linkedin.com/in/igormoochnick

Comments and Discussions

 
QuestionKeyboard focus Pin
tomabaza20-Apr-12 10:19
tomabaza20-Apr-12 10:19 
QuestionMy vote of 5* Pin
Nasenbaaer9-Nov-11 4:55
Nasenbaaer9-Nov-11 4:55 
Just by reading the article, I found all wanted solutions!
GeneralMy vote of 5 Pin
Nasenbaaer9-Nov-11 4:55
Nasenbaaer9-Nov-11 4:55 
GeneralMy vote of 1 Pin
webmaster44211-Jul-11 8:54
webmaster44211-Jul-11 8:54 
GeneralMy vote of 1 Pin
Helmy.m27-Jul-10 0:05
Helmy.m27-Jul-10 0:05 
GeneralMy vote of 1 Pin
Member 143915718-Jun-10 3:13
Member 143915718-Jun-10 3:13 
GeneralTks Pin
xpack8724-Jan-10 20:54
xpack8724-Jan-10 20:54 
GeneralYou Didn't Post All The Code Pin
#realJSOP21-Aug-09 2:05
mve#realJSOP21-Aug-09 2:05 
GeneralMy vote of 2 Pin
#realJSOP21-Aug-09 2:03
mve#realJSOP21-Aug-09 2:03 
QuestionCode Please? Pin
sam.hill9-Jan-08 10:52
sam.hill9-Jan-08 10:52 
GeneralRe: Code Please? Pin
Rajib Ahmed9-Jan-08 14:22
Rajib Ahmed9-Jan-08 14:22 
GeneralUsing the code Pin
Dewey9-Jan-08 10:46
Dewey9-Jan-08 10:46 
GeneralNice article... Pin
Rajib Ahmed9-Jan-08 9:40
Rajib Ahmed9-Jan-08 9:40 
GeneralRe: Nice article... Pin
igormoochnick26-Jan-08 18:06
igormoochnick26-Jan-08 18:06 
GeneralRe: Nice article... Pin
Rajib Ahmed30-Jan-08 17:09
Rajib Ahmed30-Jan-08 17:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.