
Introduction
When using an application that presents a lot of text in controls, users will expect to be able to search for text phrases by pressing Ctrl-F. Unfortunately for the application developer, while adding the control is just a matter of dragging it from the toolbox, adding the search functionality is a bit more work.
To solve this, I developed a lightweight class library, SearchableControls, that contains ready-to-use SeachableTextBox, SearchableRichTextBox, SearchableListView, and SearchableTreeView controls. These are straight extensions of the Framework classes, and so they behave identically, other than the user's ability to search them with Ctrl-F or the context menu, and some other basic enhancements. In the case of the text box controls, users can also search and replace text using the standard Ctrl-H shortcut. These options are also available from the context menu.
Find features
The search dialog is designed to behave like standard Windows applications such as Notepad. Features include;
- After the first result is returned, the user can continue to search ("search again") from the search dialog.
- The text entry box is a combo box featuring a history of previous searches.
- Either a case-sensitive or case-insensitive search can be selected.
- The user can select to use wildcards (* or ?) or full regular expressions in the search.
- Searches will continue from the top of the document until the original search position is reached.
- If the user moves or resizes the search dialog control, its position will be restored if the search dialog is displayed again on that particular searchable control.
- For text boxes, the word under the caret when the user selects search is the default search term.
Additional features

The main feature of these controls is the search function. However, I have also added some functionality conspicuously absent from the basic controls.
For SearchableRichTextBox, this includes a full context (right click) menu (including Copy, Paste, etc.). The basic RichTextBox does not supply a context menu. The same is supplied for the SearchableTextBox. (This is useful because you cannot supplement or override TextBox's context menu - only replace it completely.)
Also available is a FindDialog toolbox control which allows developers to add search functionality to their own controls. Other than a drag and drop, it is only necessary to supply a SearchRequested event handler and the shortcut code.

SearchableRichTextBox also includes the ability to format text from the context menu or keyboard (Ctrl-B toggles bold, Ctrl-I toggles italics, and Ctrl-U toggles underlining). A dialog to change the font of the selected text is also on the context menu.
I have also added a shortcut key for Ctrl-A to select all the text in the controls (other than the extension of TreeView, which famously does not support multiple selection). This is another feature surprisingly absent from the Framework implementations.
Using the code
Getting the controls into the toolbox
From the DLL:
The controls can be used directly from the binary library (SearchableControls.dll). In Visual Studio 2005, right click on the form designer toolbox, and select Choose Items. Then click the Browse button, and navigate to and open the .dll. Click OK, and the four controls will now be added somewhere to the toolbar.
From the source:
Download the source, and add the SearchableControls.vcproj to your solution. Build the solution, and the controls should appear in your toolbox automatically. If this doesn't happen, closing and re-opening Visual Studio can encourage it.

Getting the controls into your form
This is a matter of dragging them to your form from the designer toolbox in the usual way. Build and run your application, click on the new control, press Ctrl-F, or right click->Find, and you will see the Find dialog. Typing the text and pressing Return or clicking the Search button will search the contents of the control. Pressing F3 in the Find dialog or in the control will search for the next occurrence of the string. Once the remainder of the document has been searched, searches will take place from the start of the document.

Adding the Find entries to the parent form menu
Users will expect to see Find under the form's Edit menu. In the designer, add the Edit->Find menu item to your form and give it a Click event. Add a call to SearchableControls.OpenFindDialog(Controls); as below...
private void findToolStripMenuItem_Click(object sender, EventArgs e)
{
SearchableControls.Utility.OpenFindDialog(Controls);
}
Now, add an Edit->Find Again menu item, and give it a Click event. Add a call to SearchableControls.Utility.FindNext(Controls); as below ..
private void findAgainToolStripMenuItem_Click(object sender, EventArgs e)
{
SearchableControls.Utility.FindNext(Controls);
}
These functions allow multiple searchable controls on a form as they will select the most appropriate based on focus. However, all controls apply the interface ISearchable. This exposes a single function FindDialog() which returns the FindDialog object associated with that control. Calling Show() on this object will present the Find dialog to the user. Other functionality is available, see the source code or metadata for more details. This allows an individual control's Find functionality to be controlled externally by menus, toolboxes, or whatever.
How it works
Each control supplies KeyDown events and context menu items to control its own FindDialog object. This object raises events such as SearchRequested and ReplaceRequested to control the actual search behaviour on the parent form. The parent control searches its text with a supplied regular expression, and uses its normal selection method to highlight any found text.
A note about HideSelection
Most Framework controls have a HideSelection property which controls if selections are visible when the control is not focused. As selection is used to indicate search results which should be visible when the Find dialog is focused, it is necessary to turn off HideSelection temporarily, or search results will be invisible.
Unfortunately, a slight flicker is visible when this property is altered. One way to stop that is to set HideSelection to false permanently on individual controls, using the designer. This is usually acceptable if there is only one major control on the form.
Customizing the library
Customizing the Find dialog
It is a simple matter for developers to customize the appearance of the Find dialog using Visual Studio's designer. Unwanted controls can simply be hidden from the view.
Extending other controls to be searchable
Drag a FindDialog object from the toolbox to your chosen control. Supply a SearchRequested event handler.
This event handler has a minimum function it is required to perform. It is expected to search its content from after the current selection until the end, and then from the top of the content until the current selection. It is expected to match text against the regular expression supplied in e.SearchRegularExpression, e.g., e.SearchRegularExpression.IsMatch(myText), select any found content, and return 'true'. If no matches are found, it is expected to return 'false'.
Using regular expressions frees the controls from having to process case sensitivity and other exotic search options such as wildcards.
As mentioned above, if you are deriving from a Framework control, and using selection to indicate search results, you will probably need to turn off HideSelection. This can either be done permanently, or when selections are actually made, as with the SearchableControl controls.
Further development
The idea of supplying the expected basic functionality to controls could be explored further.
History
- 08 July 2006 :- Initial release.
- 08 Aug 2006 :- Version 1.2 release, including replace functionality, search history, and
FindDialog as a toolbox control.
| You must Sign In to use this message board. |
|
|
 |
|
 |
thanks for development, and please add following line in findForm_Closing function
ParentControl.FindForm().Activate(); ParentControl.Focus();
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Very nice control by the way, I was wondering if there was any particular reason why this was not implemented as "Extender Provider" that could have been made to work on any of the controls like TextBox, TreeView etc. Thanks
I do not fear computers. I fear the lack of them.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello!
First of all thank You for this great control.
I have only one problem: After termination of the find dialog, the textbox returns to the cusrsor position from before starting the search. Is it possible to manipulate the cursor psoition in a way that it is set to the last item found in the find dialog?
Tanks You in advance Georg
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
This is a great contribution to CodeProject! Well done, and a 5 from me!
How many bytes of text have I typed in my lifetime??? Man, I wish I kept track...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Jim, Not able to download this Source code zip file. Could you please help me in downloading it.
Thanks, Naveen.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
I have searchable RichTextBox and my ISearchable DataGridView inside .NET 2 SplitContainer.
Utility.FindSearchable(controls)
does not look inside container controls. It is not possible to create Find MenuStrip handler.
How to create Find command click handler in menu if form contains container controls ?
Andrus
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It would probably be pretty easy to adapt that function to look inside child controls.
If you can't work out how to do it I might be able to find time over the next few weeks to make that change.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I added lines
Control control = iterControl; while (control is ContainerControl) control = ((ContainerControl)control).ActiveControl;
but it still does not find. No idea why.
public static ISearchable FindSearchable(Control.ControlCollection controlCollection) { ISearchable firstSearchable = null; foreach (Control iterControl in controlCollection) { Control control = iterControl; while (control is ContainerControl) control = ((ContainerControl)control).ActiveControl; ISearchable searchable = control as ISearchable; if (searchable != null) { if (control.Focused) { return searchable; } else if (firstSearchable == null || control.TabIndex < ((Control)firstSearchable).TabIndex) { firstSearchable = searchable; } } }
return firstSearchable; }
Andrus
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This is a very handy addition to my library and it holds a lot of promise 
If you select to search by regex or wild card you will get a regex search for wild card and a wild card search for regex. This is a simple code fix.
In the "FindForm.cs" file at line 297 is the responsible switch statement and should be modified to read:
switch ((SearchType)searchTypeComboBox.SelectedIndex) { case SearchType.RegularExpression: text = searchHistoryComboBox.Text; break;
case SearchType.Wildcards: text = Regex.Escape( searchHistoryComboBox.Text ).Replace( @"\*", ".*" ).Replace( @"\?", "." ); break;
case SearchType.PlainTextSearch: default: text = Regex.Escape(searchHistoryComboBox.Text); break; }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Cheers Ross, that fix and some more can be found in the very latest version on http://jimblackler.net/SearchableControls.zip
If someone from CodeProject wants to update this article (I can't see how to automatically) please do.
JIm
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
..you're great. I wonder if I can add an item to your menu directly in the application where your control is added, without modifying the source code of the dll...Is there a way?
Andrea
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
If by menu you mean the right click context menu, this ought to work in C#:
Add this function in a somewhere in your code (a good place would be the form hosting the control you want to customize).
public static void MergeContextMenus(ContextMenuStrip target, ContextMenuStrip source) { List list = new List();
foreach (ToolStripItem item in source.Items) { list.Add(item); }
foreach (ToolStripItem item in list) { target.Items.Add(item); } }
(For List<> don't forget the "using System.Collections.Generic;" at the top of wherever you add it.)
In the form with the control on it, add a ContextMenu in the designer containing the items you want to add.
In the form's constructor, after InitializeComponent();, add this line (I assume the menu you added is called contextMenuStrip1. I assume your control is called searchableListView1) ...
MergeContextMenus(searchableListView1.ContextMenuStrip, contextMenuStrip1);
Hope that works.
Jim
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Yeah! It works even if the searchableListView's contextmenustrip propriety is set to none! How this works? And...at this address: http://msdn2.microsoft.com/en-us/library/aa970779.aspx it is written: "By default, both TextBox and RichTextBox have a context menu that appears when a user right-clicks inside the control." Why can't we get it?
Andrea
-- modified at 15:19 Friday 24th August, 2007
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I am new to C# programming and this was just what I was looking for. I was having a real hard time finding something that would explain this as well as your code did.
Many thanks!
Brian
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Jim, would it be possible for you to post an updated source with the changes noted in the various postings?
I am also looking for suggestions on how to open a file, apply formatting and color to specific positions in the text before it opens into a RichTextBox.
Thanks,
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi, thanks for this control, It's fantastic.
Using it I get a problem with Cancel button. I replace a text in a richtextbox, and i try to cancel the operation. Pressing Cancel button the richtextbox is cleared in its entirely.
Can you help me pls?
thanks Walter
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Jims,
I am working on the project of my company. Your searchable control idea is suitable for my searching algorithm in data grid view. Can I allow to use some of your code or idea in my project ?
Wilim
-- modified at 1:05 Monday 22nd January, 2007
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Yes you are welcome, it is free even for commercial use. If you have any queries or comments, do let me know here or on jimblackler@gmail.com.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hey Jim,
I've been thinking about an idea that may help to speed up search and replace a bit. Change out the line by line scrolling for something that would have the window scroll only when needed, like when the selection gets near the bottom of the searchableRichTextBox, or something like that. That also might help alleviate any flicker issues while replacing entries.
I'm just wondering how one would implement that idea, would you try to detect when the selected text is at the lowest line or what? Also, when the window scrolls to keep the selected area from leaving the window, when it's at the bottom line or whatever, it should scroll the text so that that line is now at the top, otherwise it'd just end up scrolling line by line at the bottom of the window, instead of elsewhere, and that's just as bad as the normal line by line scroll.
I'm going to try turning off the line by line scroll, and see what happens. If it flips out on me, I'll turn it back on.
Anyway, just my thoughts, happy coding  Brian
PS, no more bugs found so far, good job
Some say that ignorance is bliss... Blissful, aren't they?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|