Click here to Skip to main content
15,867,594 members
Articles / Desktop Programming / WPF

Building a Search Text Box Control with WPF

Rate me:
Please Sign up or sign in to vote.
4.90/5 (47 votes)
3 Sep 2010CPOL3 min read 231.5K   12.9K   96   36
Another convenient control for your WPF applications.

Introduction

I've worked a lot with database applications. Sometimes, providing the capability to search through stored items is a critical requirement. Items may have many fields and, of course, they may be located in different tables. These raise the question of how to design an elegantly effective user interface supporting the capability. I tried some user interface prototypes, but none of them made me feel satisfied. A good solution came up when I used Windows Explorer.

clip_image002

Figure 1: The search text box on the top-right corner of Windows Explorer

The combination of the search button, the text label, and the text box is quite amazing. A lot of space is saved, and the functional design is good. I wanted to use such controls in my applications, but .NET Framework has not provided any standard control like this one. In this article, I'm demonstrating a solution based on WPF. Many thanks fly to David Owens for his wonderful article at http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/. Technically, his document showed me a practical way to realize my own Search Text Box control which has more functionalities. Now, let's get started!

Designing the control

Image 2

Figure 2: The control's layout

We're going to create a custom control exposing the following properties:

  • LabelText - When the text box is blank, we'll display this text instead. The default value is "Search".
  • LabelTextColor - The color of the label text. The default will be "Gray".
  • ShowSectionButton - Decides to show the section button or not. This property is very useful if you allow users to search items based on a set of criteria.
  • SectionsList - Holds a list of sections which correspond to the data columns.
  • SectionsStyle - The style of section items. You can choose between CheckBoxStyle, RadioBoxStyle, or NormalStyle.
  • OnSearch - This event is spawn whenever the search button is clicked or the Enter key is pressed.

Here are some images of the control I created:

clip_image006

Figure 3: A sections list styled by CheckBoxStyle

clip_image008

Figure 4: A sections list styled by RadioBoxStyle

clip_image010

Figure 5: List of previous keywords

Creating the layout

The file Generic.xaml contains the XAML code for laying out the control. All that we're doing is modifying the property template.

XML
<ResourceDictionary> <!-- ... -->
  <Style x:Key="{x:Type l:SearchTextBox}" TargetType="{x:Type l:SearchTextBox}">
    <!-- ... -->
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type l:SearchTextBox}">
            <!- XAML code for creating the layout goes here -->
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

Displaying the lists

To customize a standard list box with the above styles, I added another Resource Dictionary file which is named as ListBoxEx.xaml. The file contains definitions of the three styles. We need an adjustment to the way that our lists' items are rendered. For example, if we want our items to have CheckBoxStyle, we simply alter the template ListBoxItem like this:

XML
<Setter Property="ItemContainerStyle">  
<Setter.Value>
        <Style TargetType="{x:Type ListBoxItem}" >
            <Setter Property="Margin" Value="2" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <CheckBox Focusable="False"
                            IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
                            RelativeSource={RelativeSource TemplatedParent} }">
                            <ContentPresenter></ContentPresenter>
                        </CheckBox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Setter.Value>
</Setter>

With RadioBoxStyle, we must ensure that: at a particular time, there's only one item selected. I don't want to use lengthy C# code to attain this task. Everything seems so easy when every RadioBoxStyle item shares the same Group name:

XML
<Setter Property="ItemContainerStyle">
    <Setter.Value>
        <Style TargetType="{x:Type ListBoxItem}" >
            <Setter Property="Margin" Value="2" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <RadioButton Focusable="False" GroupName="RadioListBoxItems"
                            IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
                            RelativeSource={RelativeSource TemplatedParent} }">
                            <ContentPresenter></ContentPresenter>
                        </RadioButton>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Setter.Value>
</Setter>

So far so good; the remainder is programmatically showing the lists whenever users click on the section button or the previous button.

C#
private Popup m_listPopup = new Popup();private ListBoxEx m_listSection = null;
private ListBoxEx m_listPreviousItem = null;
 
private void BuildPopup()
{
    // initialize the pop up
    m_listPopup.PopupAnimation = PopupAnimation.Fade;
    m_listPopup.Placement = PlacementMode.Relative;
    m_listPopup.PlacementTarget = this;
    m_listPopup.PlacementRectangle = new Rect(0, this.ActualHeight, 30, 30);
    m_listPopup.Width = this.ActualWidth;
    // initialize the sections' list
    if (ShowSectionButton)
    {
        m_listSection = new ListBoxEx((int)m_itemStyle + 
                                  ListBoxEx.ItemStyles.NormalStyle);

        // ...
    }
 
    // initialize the previous items' list
    m_listPreviousItem = new ListBoxEx();
    // ...
}
 
private void HidePopup()
{
    m_listPopup.IsOpen = false;
}
 
private void ShowPopup(UIElement item)
{
    m_listPopup.StaysOpen = true;
    m_listPopup.Child = item;
    m_listPopup.IsOpen = true;
}

private void ChooseSection_MouseDown(object sender, MouseButtonEventArgs e)
{

    if (SectionsList == null)
        return;
    if (SectionsList.Count != 0)
        ShowPopup(m_listSection);
}

private void PreviousItem_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (m_listPreviousItem.Items.Count != 0)
        ShowPopup(m_listPreviousItem);
}

Raising the Search event

Whenever users request to start a new search either by clicking on the search button or entering a keyword, an event OnSearch will be fired. A class named SearchEventArgs provides data for the event.

C#
public class SearchEventArgs: RoutedEventArgs{
    private string m_keyword="";
 
    public string Keyword
    {
        get { return m_keyword; }
        set { m_keyword = value; }
    }
    private List<string> m_sections= new List<string>();
 
    public List<string> Sections
    {
        get { return m_sections; }
        set { m_sections = value; }
    } 
    public SearchEventArgs(): base(){
 
    }
    public SearchEventArgs(RoutedEvent routedEvent): base(routedEvent){
 
    }
}

I've chosen to override the method OnKeyDown to create the event. This is shown below:

C#
protected override void OnKeyDown(KeyEventArgs e) {
    if (e.Key == Key.Escape) {
        this.Text = "";
    }
    else if ((e.Key == Key.Return || e.Key == Key.Enter)) {
        RaiseSearchEvent();
    }
    else {
        base.OnKeyDown(e);
    }
}
 
private void RaiseSearchEvent() {
    if (this.Text == "")
        return;
    if(!m_listPreviousItem.Items.Contains(this.Text))
        m_listPreviousItem.Items.Add(this.Text);
 
 
    SearchEventArgs args = new SearchEventArgs(SearchEvent);
    args.Keyword = this.Text;
    if(m_listSection != null){
        args.Sections = (List<string>)m_listSection.SelectedItems.Cast<string>().ToList();
    }
    RaiseEvent(args);
}

How to use the control

That's it; now the control is ready for action. To use it, you can go through these steps:

  1. Make a reference to the assembly SearchTextBox.dll which contains our control.
  2. Create a namespace in XAML markup like the following in your application XAML namespace:
  3. XML
    <Window x:Class="TestUI.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:l="clr-namespace:UIControls;assembly=SearchTextBox"
            Title="Window1" Height="423" Width="487">
  4. Create an instance of the control in the markup:
  5. XML
    <l:SearchTextBox Height="39" Margin="118,52,116,0" 
          VerticalAlignment="Top" Name="m_txtTest" Background="AliceBlue" />
  6. Implement the C# code-behind:
    • Create a using directive to use the control in the namespace UIControls.
    • C#
      using UIControls;
    • Initialize the control with the appropriate information, for example:
    • C#
      public Window1()
      {
          InitializeComponent();
       
          // Supply the control with the list of sections
          List<string> sections = new List<string> {"Author", 
                                     "Title", "Comment"};
          m_txtTest.SectionsList = sections;
       
          // Choose a style for displaying sections
          m_txtTest.SectionsStyle = SearchTextBox.SectionsStyles.RadioBoxStyle;
       
          // Add a routine handling the event OnSearch
          m_txtTest.OnSearch += new RoutedEventHandler(m_txtTest_OnSearch);
      }
       
      void m_txtTest_OnSearch(object sender, RoutedEventArgs e)
      {
          SearchEventArgs searchArgs = e as SearchEventArgs;
       
          // Display search data
          string sections = "\r\nSections(s): ";
          foreach (string section in searchArgs.Sections)
              sections += (section + "; ");
          m_txtSearchContent.Text = "Keyword: " + searchArgs.Keyword + sections;
      }

Conclusion

I hope you liked this article. Any feedback from you is valuable to me for making the control better. Thanks for reading!

License

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


Written By
Architect VNG Corp
Vietnam Vietnam
I'm a computer freak. Application Security & Big Data are my research fields.

Comments and Discussions

 
QuestionFixed clear/X icon bug and popup placement bug in VS2015 Pin
M. Fletcher (Fletch)13-Apr-17 7:38
M. Fletcher (Fletch)13-Apr-17 7:38 
QuestionCut Copy Paste menu Pin
mhargreaves7-Oct-15 3:50
mhargreaves7-Oct-15 3:50 
Questionbug Pin
ntnk19841-Dec-14 16:22
ntnk19841-Dec-14 16:22 
AnswerRe: bug Pin
Le Duc Anh2-Dec-14 14:03
professionalLe Duc Anh2-Dec-14 14:03 
QuestionGreat tool, but one question Pin
Member 105971415-Nov-14 10:03
Member 105971415-Nov-14 10:03 
QuestionWonderful Pin
minhnguyentk15-Aug-14 10:53
minhnguyentk15-Aug-14 10:53 
QuestionThe 'x' button isn't user friendly. Pin
wa1gon12-Sep-13 7:17
wa1gon12-Sep-13 7:17 
AnswerRe: The 'x' button isn't user friendly. Pin
wpx729-Jan-14 4:09
wpx729-Jan-14 4:09 
QuestionWhere is the part for binding data for searching to searchbox control? Pin
Daisy M11-Aug-13 22:42
Daisy M11-Aug-13 22:42 
AnswerRe: Where is the part for binding data for searching to searchbox control? Pin
Le Duc Anh11-Aug-13 23:34
professionalLe Duc Anh11-Aug-13 23:34 
GeneralRe: Where is the part for binding data for searching to searchbox control? Pin
Daisy M12-Aug-13 0:42
Daisy M12-Aug-13 0:42 
GeneralRe: Where is the part for binding data for searching to searchbox control? Pin
Le Duc Anh12-Aug-13 4:01
professionalLe Duc Anh12-Aug-13 4:01 
Generalmy vote of 4 Pin
ARIA 52-Dec-12 23:39
ARIA 52-Dec-12 23:39 
GeneralMy vote of 5 Pin
Nikhil_S25-Oct-12 2:23
professionalNikhil_S25-Oct-12 2:23 
GeneralRe: My vote of 5 Pin
Le Duc Anh25-Oct-12 3:08
professionalLe Duc Anh25-Oct-12 3:08 
Questionneed help for create same control on windows form Pin
Nilesh Makavana12-Aug-12 8:53
Nilesh Makavana12-Aug-12 8:53 
AnswerRe: need help for create same control on windows form Pin
Le Duc Anh12-Aug-12 16:52
professionalLe Duc Anh12-Aug-12 16:52 
GeneralRe: need help for create same control on windows form Pin
Nilesh Makavana14-Aug-12 19:33
Nilesh Makavana14-Aug-12 19:33 
QuestionClear button is not functioning Pin
NikolayKa4-Apr-12 9:32
NikolayKa4-Apr-12 9:32 
AnswerRe: Clear button is not functioning Pin
Le Duc Anh4-Apr-12 13:45
professionalLe Duc Anh4-Apr-12 13:45 
AnswerRe: Clear button is not functioning Pin
jim19107-Dec-12 11:15
jim19107-Dec-12 11:15 
GeneralRe: Clear button is not functioning Pin
Le Duc Anh7-Dec-12 13:44
professionalLe Duc Anh7-Dec-12 13:44 
GeneralRe: Clear button is not functioning Pin
jim19107-Dec-12 14:23
jim19107-Dec-12 14:23 
Two problems:

1. The previous list is not working any more because the list is always empty;
2. Can't do search any more because 'clear' will appear after the fist letter you entered the textbox and anytime you click on the image it clears the search text.

I haven't figure out the solution yet.
GeneralMy vote of 5 Pin
Dean Oliver1-Feb-12 0:33
Dean Oliver1-Feb-12 0:33 
GeneralMy vote of 5 Pin
Kanasz Robert18-Jan-12 21:38
professionalKanasz Robert18-Jan-12 21:38 

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.