Home > WPF > WPF Search Text Box

WPF Search Text Box

February 18, 2009

We’ve seen a lot of good advancement in usability in our operating systems and in our programs. One such advancement is the combining of the text label and the text box that it adorns. Not only does the text box hold the label, it also holds the search and clear button. This frees up a lot of space and allows us to create UI that looks like the following:

image 

image

Now, I’m not going to argue who started or came up with this design because I frankly don’t care. :p The point is that it is around and it’s very good use of space and functional design. However, there is a problem; there isn’t a standard control to provide users with this functionality.

Today, let’s solve that problem. :)

First Look

Before we get started, let’s look at the actual control that we’ll be building.

image

As you can see, there are two basic modes that we can be in. The top mode is what we’ll refer to as instant search and the second is delayed search. The difference between the two is fairly straightforward. In the instant search case we expect to have live filtering or searching of data where as in the second we require the search icon to be pressed or the ENTER key to be pressed in order for the search to take place.

If we were getting this from a designer we’d usually have pixel offsets of where everything should be placed, but I don’t want to spend the time creating those images as well. :)

Thinking About the Solution

So we have a few options to achieving the goal with WPF.

The first is to simply create this control by laying out the parts of the control directly on the designer. This has the major downfall of reusability problems but it would work.

The second is to create a user control that encapsulates the structure and functionality of the control. However, this has the drawback of not allowing others to control the look and feel of the control. It does have the great benefit of being able to be designed in either the WPF Designer in Visual Studio or with Expression Blend.

The third is to create a custom control that defines the default look through a style for the type and handles all of the functionality of the control as well. This gives us the greatest control and flexibility but comes at the cost of not being able to design the control with either Visual Studio or Expression Blend.

Since we’re trying to create a control that others can consume and can customize the look and feel of they wish, we need to go with the custom control option.

Ok, so we know the strategy that we’re going to take, let’s start looking into the functionality that we want to expose.

We are going to want the following properties on our control:

  • LabelText – This will be the text that labels the text box box. The default will be “Search”.
  • LabelTextColor – The color of the text for the label. The default will be “Gray”.
  • SearchMode – The type of search that will be taking place: instant or delayed. The default will be instant.
  • HasText – We’ll need this to make some of our triggers easier in our control template.

We’ll also want to expose an event for when the search icon is clicked in the delayed state.

  • SearchClicked – The event to be raised when the search icon is clicked. Note that this will only be raised when the control is in the delayed state.

Getting Started

We now have a pretty good idea of where we are going and we have enough information that we should be able to get there, so let’s get started! :)

For this project, since there really is no designer support for custom controls in any of the products available and we’ll be needed to write some actual code, I’ll be using Visual Studio’s WPF designer.

Ok, so first things first, we need a new project. I’m actually going to be creating two projects:

  1. A WPF application that will consume my custom control (I named mine TestUI), and
  2. The custom control library (I named mine UIControls)

With those created, let’s add a new item to the UIControls project. When the new item dialog comes up, be sure to choose ‘WPF Custom Control’ and name the item “SearchTextBox”.

You should now have a SearchTextBox.cs file in your UIControls project.

Next we’ll want to add a project reference to our TestUI project that points to the UIControls project. After doing that, build the solution so that control library gets built and is available for use within the TestUI project.

Alright, the last little step we need to do is add our xmlns and our control to the XAML.

Open up the Window1.xaml file in the TestUI project. In the Window definition in the XAML editor you should see a few xmlns entries. We need to add another in there. Switch over to the XAML editor and add this:

xmlns:l="clr-namespace:UIControls;assembly=UIControls"

This creates a way to reference our user control using the “l” namespace prefix.

Now we can add an instance of our new custom control. Let’s add the following XAML as a child of the Grid:

<l:SearchTextBox Width="125" Height="21" />

If you build again, hopefully you won’t get any errors! If so, great! Though you might be wondering why you don’t see anything on the Grid – that’s OK, the template that you get defaulted with is a Border with no default properties so it will be transparent.

OK, we’re done! Yeah… not really, but you technically have created a custom control, and maybe your first, so congratulations if it is. :)

Adding the Properties

We’ll go ahead and get the property creation done first as there is a good portion of our UI work that will depend on them.

Open up the SearchTextBox.cs file. You should currently see something that looks like this (I left out the namespace using statements for space consideration):

namespace UIControls {
    public class SearchTextBox : Control {
        static SearchTextBox() {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(SearchTextBox),
                new FrameworkPropertyMetadata(typeof(SearchTextBox)));
        }
    }
}

Alright, we want to create dependency properties for all of the properties. If you don’t know much about dependency properties then check here: http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.aspx.

Here’s what the code should look like after you create all of the dependency properties mentioned above. Of course, you can do this in a few different ways, this is just one way to do it.

namespace UIControls {
    public enum SearchMode { 

        Instant,
        Delayed,
    }

    public class SearchTextBox : TextBox {  
        public static DependencyProperty LabelTextProperty = 

            DependencyProperty.Register(
                "LabelText",
                typeof(string),
                typeof(SearchTextBox));

        public static DependencyProperty LabelTextColorProperty =
            DependencyProperty.Register(
                "LabelTextColor",
                typeof(Brush),
                typeof(SearchTextBox));

        public static DependencyProperty SearchModeProperty =
            DependencyProperty.Register(
                "SearchMode",
                typeof(SearchMode),
                typeof(SearchTextBox),
                new PropertyMetadata(SearchMode.Instant));

        private static DependencyPropertyKey HasTextPropertyKey =
            DependencyProperty.RegisterReadOnly(
                "HasText",
                typeof(bool),
                typeof(SearchTextBox),
                new PropertyMetadata());
        public static DependencyProperty HasTextProperty = HasTextPropertyKey.DependencyProperty;

        static SearchTextBox() { 

            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(SearchTextBox),
                new FrameworkPropertyMetadata(typeof(SearchTextBox)));
        }

        protected override void OnTextChanged(TextChangedEventArgs e) { 
            base.OnTextChanged(e);
            HasText = Text.Length != 0; 
        }

        public string LabelText {
            get { return (string)GetValue(LabelTextProperty); }
            set { SetValue(LabelTextProperty, value); }
        }

        public Brush LabelTextColor {
            get { return (Brush)GetValue(LabelTextColorProperty); }
            set { SetValue(LabelTextColorProperty, value); }
        }

        public SearchMode SearchMode {
            get { return (SearchMode)GetValue(SearchModeProperty); }
            set { SetValue(SearchModeProperty, value); }
        }

        public bool HasText {
            get { return (bool)GetValue(HasTextProperty); }
            private set { SetValue(HasTextPropertyKey, value); }
        }
    }
}

So there is definitely a lot of code in there, but the majority of it is just setting up the dependency properties. But here are the highlights:

  1. SearchTextBox derives from TextBox now instead of Control
  2. SearchMode enum defined
  3. OnTextChanged implemented to set our HasText dependency project

Creating the Look and Feel

We now have pretty much all of the functionality defined that we’ll be using so we can focus on getting the control looking like we want it.

The first thing we want to tackle is the look of the control when it has no text it in and does not have keyboard focus nor has the mouse over it. We’ll call this the control’s default state.

Let’s first take a look at the basic idea of the structure and then I’ll provide the XAML that we’ll need to put in the Generic.xaml resource dictionary.

  • Border – The outermost border of the control.
    • Grid – Provides an easy way to layout the two sections of the control we’ll have.
      • ScrollViewPresenter – This provides us the basic functionality of a text box control.
      • Label – Used for our overlay text.
      • Border – The border around our search icon, used for mouse over effects.
        • Image – The image control used for the search and delete icon.

That is the basic hierarchy that we’ll be creating. And here is the style that we’ll be using for the template:

<Style x:Key="{x:Type l:SearchTextBox}" TargetType="{x:Type l:SearchTextBox}">
  <Setter Property="Background" Value="{StaticResource SearchTextBox_Background}" />
  <Setter Property="BorderBrush" Value="{StaticResource SearchTextBox_Border}" />
  <Setter Property="Foreground" Value="{StaticResource SearchTextBox_Foreground}" />
  <Setter Property="BorderThickness" Value="1" />
  <Setter Property="SnapsToDevicePixels" Value="True" />
  <Setter Property="LabelText" Value="Search" />
  <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
  <Setter Property="LabelTextColor" Value="{StaticResource SearchTextBox_LabelTextColor}" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type l:SearchTextBox}">
        <Border x:Name="Border"
                Padding="2"
                Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <Grid x:Name="LayoutGrid">
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="*" />
              <ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                Path=ActualHeight}" />
            </Grid.ColumnDefinitions>
            <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" />
            <Label x:Name="LabelText"
                   Grid.Column="0"
                   Foreground="{Binding RelativeSource={RelativeSource TemplatedParent},
                                        Path=LabelTextColor}"
                   Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelText}"
                   Padding="2,0,0,0"
                   FontStyle="Italic" />
              <Border x:Name="PART_SearchIconBorder"
                      Grid.Column="1"
                      BorderThickness="1"
                      Padding="1"
                      VerticalAlignment="Stretch"
                      HorizontalAlignment="Stretch"
                      BorderBrush="{StaticResource SearchTextBox_SearchIconBorder}"
                      Background="{StaticResource SearchTextBox_SearchIconBackground}">
                <Image x:Name="SearchIcon"
                       Stretch="None"
                       Width="15"
                       Height="15"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Source="pack://application:,,,/UIControls;component/Images/search.png" />
             </Border>
          </Grid>
        </Border> 
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

There we have it. :) I’m not really going to explain all of XAML up there – hopefully it’s fairly straight forward, but as always, if you have questions, let me know.

The resources that were defined above are below, you just need to add these to the Generic.xaml file as well. I’m going to go ahead and give you all of the resources that we’ll be using.

<SolidColorBrush x:Key="SearchTextBox_Background" Color="White" />
<SolidColorBrush x:Key="SearchTextBox_Foreground" Color="Black" />
<LinearGradientBrush x:Key="SearchTextBox_Border" StartPoint="0,0" EndPoint="0,1">
  <GradientStop Color="#FFABADB3" Offset="0.05" />
  <GradientStop Color="#FFE2E3EA" Offset="0.07" />
  <GradientStop Color="#FFE3E9EF" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="SearchTextBox_BorderMouseOver" StartPoint="0,0" EndPoint="0,1">
  <GradientStop Color="#FF5C97C1" Offset="0.05" />
  <GradientStop Color="#FFB9D7EB" Offset="0.07" />
  <GradientStop Color="#FFC7E2F1" Offset="1" />
</LinearGradientBrush>
<SolidColorBrush x:Key="SearchTextBox_SearchIconBorder" Color="White" />
<SolidColorBrush x:Key="SearchTextBox_SearchIconBackground" Color="White" />
<LinearGradientBrush x:Key="SearchTextBox_SearchIconBorder_MouseOver" StartPoint="0,0" EndPoint="0,1" >
  <GradientStop Color="#FFFFFFFF" Offset="0" />
  <GradientStop Color="#FFE5F4FC" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="SearchTextBox_SearchIconBackground_MouseOver" StartPoint="0,0" EndPoint="0,1" >
  <GradientStop Color="#FFE7F5FD" Offset="0" />
  <GradientStop Color="#FFD2EDFC" Offset="0.5" />
  <GradientStop Color="#FFB6E3FD" Offset="0.51" />
  <GradientStop Color="#FF9DD5F3" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="SearchTextBox_SearchIconBorder_MouseDown" StartPoint="0,0" EndPoint="0,1" >
  <GradientStop Color="#FFFFFFFF" Offset="0" />
  <GradientStop Color="#FFE5F4FC" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="SearchTextBox_SearchIconBackground_MouseDown" StartPoint="0,0" EndPoint="0,1" >
  <GradientStop Color="#FFE7F5FD" Offset="0" />
  <GradientStop Color="#FFD2EDFC" Offset="0.5" />
  <GradientStop Color="#FFB6E3FD" Offset="0.51" />
  <GradientStop Color="#FF9DD5F3" Offset="1" />
</LinearGradientBrush>
<SolidColorBrush x:Key="SearchTextBox_LabelTextColor" Color="Gray" />

And the images that are used here:

clear search

Place those images in an “Images” folder in your project, set them to be a resource and to always copy or copy if newer in the properties for the item.

Voila, if all went well, you should see something like this when you run your application.

image

Handling the Mouse Over and Keyboard Focus

The next thing we’ll tackle with this is getting the styling right for when the mouse is over the control and when the keyboard has focus. Both of these will have the same look so we’ll handle them together.

Unfortunately we won’t be having the “cool” animation of the mouse over border color transitioning in as there are some issues with the ListBoxChrome type that is used to achieve that effect (basically they have some hard-coded colors in there). We’d have to write our own chrome class and that is just simply out of scope for this article.

In order to handle the mouse over and the keyboard focus, we’re going to have to pay attention to the mode that we are in.

Here are the triggers that we need to add to our control template:

<Trigger Property="IsMouseOver" Value="True">
  <Setter Property="BorderBrush" Value="{StaticResource SearchTextBox_BorderMouseOver}" />
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
  <Setter Property="BorderBrush" Value="{StaticResource SearchTextBox_BorderMouseOver}" />
</Trigger>

That will give us the nice little border effect when the mouse is over or when the control has keyboard focus.

Handling Having Text and Being In Different Modes

Next we’ll handle the triggers for when the control is in different modes.

<Trigger Property="HasText" Value="True">
  <Setter Property="Visibility" TargetName="LabelText" Value="Hidden" />
</Trigger>
<MultiTrigger>
  <MultiTrigger.Conditions>
    <Condition Property="HasText" Value="True" />
    <Condition Property="SearchMode" Value="Instant" />
  </MultiTrigger.Conditions>
  <Setter Property="Source"
          TargetName="SearchIcon"
          Value="pack://application:,,,/UIControls;component/Images/clear.png" />
</MultiTrigger>
<MultiTrigger>
  <MultiTrigger.Conditions>
    <Condition Property="IsMouseOver" SourceName="PART_SearchIconBorder" Value="True" />
    <Condition Property="HasText" Value="True" /> 
  </MultiTrigger.Conditions>
  <Setter Property="BorderBrush"
          TargetName="PART_SearchIconBorder"
          Value="{StaticResource SearchTextBox_SearchIconBorder_MouseOver}" />
  <Setter Property="Background"
          TargetName="PART_SearchIconBorder"
          Value="{StaticResource SearchTextBox_SearchIconBackground_MouseOver}" />
</MultiTrigger>
<MultiTrigger>
  <MultiTrigger.Conditions>
    <Condition Property="IsMouseOver" SourceName="PART_SearchIconBorder" Value="True" />
    <Condition Property="HasText" Value="True" /> 
  </MultiTrigger.Conditions>
  <Setter Property="BorderBrush"
          TargetName="PART_SearchIconBorder"
          Value="{StaticResource SearchTextBox_SearchIconBorder_MouseOver}" />
  <Setter Property="Background"
          TargetName="PART_SearchIconBorder"
          Value="{StaticResource SearchTextBox_SearchIconBackground_MouseOver}" />
</MultiTrigger>
<MultiTrigger>
  <MultiTrigger.Conditions>
    <Condition Property="IsMouseOver" SourceName="PART_SearchIconBorder" Value="True" />
    <Condition Property="IsMouseLeftButtonDown" Value="True" />
    <Condition Property="HasText" Value="True" />
  </MultiTrigger.Conditions>
  <Setter Property="Padding"
          TargetName="PART_SearchIconBorder"
          Value="2,0,0,0" />
  <Setter Property="BorderBrush"
          TargetName="PART_SearchIconBorder"
          Value="{StaticResource SearchTextBox_SearchIconBorder_MouseDown}" />
  <Setter Property="Background"
          TargetName="PART_SearchIconBorder"
          Value="{StaticResource SearchTextBox_SearchIconBackground_MouseDown}" />
</MultiTrigger>

In here we basically have four different triggers happening:

  1. Hiding the place holder text (i.e. Search)
  2. Changing the search icon when in instant mode.
  3. Changing the border brush and background when the mouse is over the search icon.
  4. Changing the look of the search icon when it’s depressed.

You’ll notice that there is a property that doesn’t exist on our control: IsMouseLeftButtonDown. Well, we are going to need to add a new property to support this. Here’s the code we need to add to our code file:

private static DependencyPropertyKey IsMouseLeftButtonDownPropertyKey =
    DependencyProperty.RegisterReadOnly(
        "IsMouseLeftButtonDown",
        typeof(bool),
        typeof(SearchTextBox),
        new PropertyMetadata());
public static DependencyProperty IsMouseLeftButtonDownProperty =
    IsMouseLeftButtonDownPropertyKey.DependencyProperty;

public bool IsMouseLeftButtonDown {
    get { return (bool)GetValue(IsMouseLeftButtonDownProperty); }
    private set { SetValue(IsMouseLeftButtonDownPropertyKey, value); }
}

That’s great and all, but we’re not doing anything to set this property. We’ll need to handle a few mouse events in our code.

public override void OnApplyTemplate() {
    base.OnApplyTemplate(); 

    Border iconBorder = GetTemplateChild("PART_SearchIconBorder") as Border;
    if (iconBorder != null) {
        iconBorder.MouseLeftButtonDown += new MouseButtonEventHandler(IconBorder_MouseLeftButtonDown);
        iconBorder.MouseLeftButtonUp += new MouseButtonEventHandler(IconBorder_MouseLeftButtonUp);
        iconBorder.MouseLeave += new MouseEventHandler(IconBorder_MouseLeave);
    }
}

private void IconBorder_MouseLeftButtonDown(object obj, MouseButtonEventArgs e) {
    IsMouseLeftButtonDown = true;
}

private void IconBorder_MouseLeftButtonUp(object obj, MouseButtonEventArgs e) {
    if (!IsMouseLeftButtonDown) return; 
    IsMouseLeftButtonDown = false;
}

private void IconBorder_MouseLeave(object obj, MouseEventArgs e) {

    IsMouseLeftButtonDown = false;
}

That should about do it for getting our IsMouseLeftButtonDown property working, nothing to it. ;)

Event Notification For Search

The last thing we need to do is add an event that our control consumers (i.e. the people using our control) can add a handler to in order to know when a search should happen.

There are two strategies that we want here:

  1. When in instant mode, we want a timer to trigger the event after the last character is entered. We want a timer because we don’t necessarily want every keystroke to raise the event.
  2. When in delayed mode, we want the event raised on ENTER or when the search icon is clicked.

To go about implementing the first of these strategies, we are going to need another property:

public static DependencyProperty SearchEventTimeDelayProperty =
    DependencyProperty.Register(
        "SearchEventTimeDelay",
        typeof(Duration),
        typeof(SearchTextBox),
        new FrameworkPropertyMetadata(
            new Duration(new TimeSpan(0, 0, 0, 0, 500)),
            new PropertyChangedCallback(OnSearchEventTimeDelayChanged)));

public Duration SearchEventTimeDelay {
    get { return (Duration)GetValue(SearchEventTimeDelayProperty); }
    set { SetValue(SearchEventTimeDelayProperty, value); }
}

static void OnSearchEventTimeDelayChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
    SearchTextBox stb = o as SearchTextBox;
    if (stb != null) {
        stb.searchEventDelayTimer.Interval = ((Duration)e.NewValue).TimeSpan;
        stb.searchEventDelayTimer.Stop();
    }
}

This property will be used to expose the timer delay before the event is raised. The default is half a second (0.5s).

The next thing we need to do is add the event, we’ll call it Search.

public static readonly RoutedEvent SearchEvent =
    EventManager.RegisterRoutedEvent(
        "Search",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(SearchTextBox));

public event RoutedEventHandler Search {
    add { AddHandler(SearchEvent, value); }
    remove { RemoveHandler(SearchEvent, value); }
}

We’ll also want to create a helper function that will raise this event for us.

private void RaiseSearchEvent() {
    RoutedEventArgs args = new RoutedEventArgs(SearchEvent);
    RaiseEvent(args);
}

The get the timer to work, we’re going to actually need to create a timer object. We’ll also want to add some code to our constructor to perform some default initialization.

private DispatcherTimer searchEventDelayTimer;
public SearchTextBox() : base() {
    searchEventDelayTimer = new DispatcherTimer();
    searchEventDelayTimer.Interval = SearchEventTimeDelay.TimeSpan;
    searchEventDelayTimer.Tick += new EventHandler(OnSeachEventDelayTimerTick);
}

void OnSeachEventDelayTimerTick(object o, EventArgs e) {
    searchEventDelayTimer.Stop();
    RaiseSearchEvent();
}

The OnTextChanged method will need to be updated so that it stops and starts the timer correctly when in instant mode.

protected override void OnTextChanged(TextChangedEventArgs e) {
    base.OnTextChanged(e);
    HasText = Text.Length != 0;

    if (SearchMode == SearchMode.Instant) {
        searchEventDelayTimer.Stop();
        searchEventDelayTimer.Start();
    }
}

To enable the second scenario, we need to update our mouse and key handling methods.

private void IconBorder_MouseLeftButtonUp(object obj, MouseButtonEventArgs e) {
    if (!IsMouseLeftButtonDown) return;

    if (HasText && SearchMode == SearchMode.Instant) {
        this.Text = "";
    }
    if (HasText && SearchMode == SearchMode.Delayed) {
        RaiseSearchEvent();
    }
   
    IsMouseLeftButtonDown = false;
}

protected override void OnKeyDown(KeyEventArgs e) {

    if (e.Key == Key.Escape && SearchMode == SearchMode.Instant) {
        this.Text = "";
    }
    else if ((e.Key == Key.Return || e.Key == Key.Enter) &&  SearchMode == SearchMode.Delayed) {
        RaiseSearchEvent();
    }
    else {
        base.OnKeyDown(e);
    }
}

Alright, there we have it. I know this was a really long post but I hope that you found it at least helpful. I’ve chosen to let the code do most of the explanation so if anything is confusing or if you have questions about anything, please let me know and I’ll be glad to answer them.

Here’s the sample project so you can look at all of the bits in one place: SearchTextBox.zip

About these ads
Categories: WPF
  1. February 19, 2009 at 1:11 pm | #1

    Great article. Many thanks for sharing.

  2. February 19, 2009 at 1:57 pm | #2

    No problem, glad someone out there liked it. :)

  3. Christopher Hujanen
    February 19, 2009 at 5:31 pm | #3

    Excellent post David. I really liked your “Thinking About the Solution” section as it’s nice to not only see the solution but why you went with it versus the alternatives.

  4. benny
    February 20, 2009 at 12:21 pm | #4

    The file is corrupt. Cannot download

  5. February 20, 2009 at 1:05 pm | #5

    Hey Benny, not sure what the problem is. I followed the link and it worked fine on my side on two different computers. One computer I used Window’s built-in zip handling, the other I used WinRAR. I also tried from both IE 8 and Chrome.

    Can you try it again?

    Thanks!

  6. February 24, 2009 at 3:31 am | #6

    Nice post David!
    Do you mind me using these sources for property grid project (http://www.codeplex.com/wpfpropertygrid)?
    Thanks in advance.

  7. February 24, 2009 at 9:06 am | #7

    Be my guest. You should not use the icons though as I ripped those from a screenshot of Windows Explorer. Glad you liked the control!

  8. February 26, 2009 at 7:55 am | #8

    Oh my god this post is priceless!

    I will be sure using these techniques in my next WPF App, thank you very much for your time!

  9. Rico Alexander
    February 27, 2009 at 12:00 pm | #9

    Anyone able to get this? I’m getting a login error.

  10. February 27, 2009 at 12:26 pm | #10

    Hmm… I’m not seeing this. You shouldn’t need to log in at all as the files are publicly available.

    Can you try again?

    Thanks.

  11. March 1, 2009 at 12:30 pm | #11

    I think you can also move the [PART_SearchIconBorder] into a Button and set [SearchIcon] as its content. Then override its style. The Button way would simplify handling the keyboard and mouse interaction in a elegant way.

    What do you think?

  12. March 1, 2009 at 7:10 pm | #12

    Yeah, you could use a button there if you wanted. You get some of the mouse handling for free, though there is nothing about keyboard handling that changes except by using a button you need to make sure that the IsTabStop property is set to false on it otherwise when you TAB through the controls on your, you’ll hit that button. If that is what you want, then don’t worry about; I didn’t want that for this control as there are other keyboard means to clear and search for the items.

    Also, by using a button you force people to have a button as their container which is a little heavier of an element. I force people to use border, but I think the trade off is worth it. Also, in the case of a button you have to re-template it as well as provide different styles that get triggered on the mouse over.

    I’m not sure using a button is more elegant, it does save you a few lines of code (only on the order of 10) but requires more work in the template and styling department.

  13. Anthony Brien
    March 2, 2009 at 2:38 pm | #13

    Very nice article! I had coded the exact same control myself using Windows Forms, and your article gives me great insight on how to go about implementing custom controls using WPF. I haven’t fully grasped the WPF mindset yet but this article goes a long way helping me do that!

  14. Zakster
    April 9, 2009 at 5:32 am | #14

    I feel like I must be doing something goofy. Every time I try to make a custom control based on the textbox (based on my own experiments plus following this post to T) I wind up with a textbox that doesn’t use the “I Beam” mouse cursor when I mouse over it. It’s just the regular arrow. Any clue as to what I’m missing?

  15. rykk
    July 21, 2009 at 3:02 pm | #15

    Thanks very much for this article.
    I ported it to VB and it works very well.

    Related to the post directly above
    To get the I beam cursor to appear modify the Trigger Property IsMouseOver section.

    From:

    To:

  16. rykk
    July 21, 2009 at 3:05 pm | #16

    I can see that worked well.
    OK I will try again – ADD
    “”
    to the Trigger Property IsMouseOver section.

  17. rykk
    July 21, 2009 at 3:07 pm | #17

    Once again – I can see that worked out about the same..
    OK I will try again – ADD

    Setter Property=”Cursor” Value=”IBeam”
    properly enclosed.

  18. John
    July 26, 2009 at 7:18 pm | #18

    I’ve been looking all over for this, and this is the best I’ve seen. I’m using it for a personal project of mine, but I had no problem getting it setup and then hosting it inside of an ElementHost in a WinForms application.. works amazing. Love it. :)

  19. August 29, 2009 at 6:08 am | #19

    I really like the styling of WPF improvement from winforms. And the explanation is very nice

  20. September 23, 2009 at 2:45 am | #20

    I’m a beginner in WPF and this tutorial really opened my eyes on custom controls matter. Thank you very much for making it.

  21. Berryl
    October 18, 2009 at 9:24 am | #21

    @David – Great job of presenting a slick control with a slick presentation of design concepts and usage (and letting the code speak for itself!).

    @All – has anyone made a unit test for this? I’d like to see how you did it if so.

    Cheers

  22. madalinamazilu
    November 11, 2009 at 4:42 am | #22

    Very interesting post and also very useful, but I was wondering if I could get some help to do the same thing in silverlight as I am new to both silverlight and WPF.
    Thanks

  23. November 11, 2009 at 10:37 am | #23

    I’ll see if I can’t create a Silverlight version sometime soon.

  24. February 28, 2010 at 10:45 am | #24

    Hi,

    at first: thanks for this great article!
    My problem:
    I try binding to the Text-Property and get allways an “System.InvalidOperationException” (at PresentationCore.dll).

    An idea what I`m doing wrong?

    eddy

  25. March 2, 2010 at 4:03 am | #25

    …problem solved. Nothing to do with your SearchTextBox ;-)

  26. Joe
    March 2, 2010 at 3:12 pm | #26

    I’m having some problems getting this to work. I follow everything in this post and use the final sample, but I’m only getting the visual aspects with none of the functionality. For example, I cannot type into the textbox or click on the search icon, or have any of the triggers actually fire to toggle the visual appearance. What could I possibly be doing wrong?

  27. Joe
    March 2, 2010 at 4:58 pm | #27

    Ok, I know what happened. I’m using Prism 1.2, which provides a DelegateCommand and the ability to extend custom commanding to controls. As such, my user interactions are not powered by events, they are powered by commands that are implemented on the ViewModel (or some other custom class).

    I’ve attempted to extend the SearchTextBox with a “SearchBehavior” that forwards the Search event to a command. This is explained here under the topic “Extending Command Support”:
    http://msdn.microsoft.com/en-us/library/dd458928.aspx

    In implementing this new behavior and hooking it up to the SearchTextBox, the UI for the box is now completely unresponsive as described. It’s not the code that causes the issue, it’s when I then specify an actual command (i.e. .

    David, do you have any experience with this kind of issue?

  28. Nathan
    March 12, 2010 at 1:53 pm | #28

    Would it be possible to somehow bind this to a list and search the list for an item?

  29. May 11, 2010 at 5:18 am | #29

    Excellant work.

  30. Veer
    July 16, 2010 at 3:59 am | #30

    Great Post!!!

  31. Macabre Sunsets
    August 8, 2010 at 11:42 pm | #31

    Though this post is old, I would like to make a comment about definingthe image of the SearchTextbox in XAML. This can be useful in cases of many SearchTextboxes in different places.

    Add to SearchTextBox.cs the following:

    public static DependencyProperty IconProperty =
    DependencyProperty.Register(“Icon”, typeof(Uri), typeof(SearchTextBox), new PropertyMetadata(new Uri(@”pack://application:,,,/UIControls;component/Images/search-thumb.png”)));

    and

    public Uri Icon
    {
    get { return (Uri)GetValue(IconProperty); }
    set { SetValue(IconProperty, value); }
    }

    On the “public override void OnApplyTemplate()” function add the following:

    Image image = iconBorder.Child as Image;
    image.Source = new BitmapImage(new Uri(Icon.OriginalString));

    Now you will be able to set the image through XAML like this:

    Perhaps it is not the best way, but it works.
    Hope someone may find it useful.

  32. Saravanan
    September 27, 2010 at 12:41 pm | #32

    Great job. Thanks for sharing.

  33. Brandon
    October 29, 2010 at 8:00 am | #33

    This is great! Thanks for posting!

  34. November 5, 2010 at 5:02 am | #34

    Hey man,

    Love the article!!!!

    Thx!

  35. December 11, 2010 at 5:45 am | #35

    This is really great stuff! Exactly what I’m looking for… Got a version for Silverlight? ;)

  36. December 13, 2010 at 5:09 am | #36

    I’ve made a Silverlight version of this control and posted it here: http://cid-04ff010431edad04.office.live.com/self.aspx/.Public/UIControls^_35.zip

  37. dfzs
    December 23, 2010 at 4:33 pm | #37

    download does not work

  38. December 23, 2010 at 5:17 pm | #38

    If you’re referring to the Silverlight version, WordPress botched the URL. Try this: http://cid-04ff010431edad04.office.live.com/self.aspx/.Public/UIControls%5E_35.zip

  39. Ummar Muhammad
    February 25, 2011 at 7:33 am | #39

    i could get only the message boxes and nothing else.
    what might be the problem?

  40. AJ
    May 10, 2011 at 10:21 pm | #40

    First of all, great job on this, I absolutely love your control.
    The only problem I am having is, I cannot seem to access the control from the code behind even when I have set the x:Name… any idea?

  41. AJ
    May 10, 2011 at 10:40 pm | #41

    NVM, I was coding on a static predicate function, thats why.. o_O

  42. May 16, 2011 at 10:11 pm | #42

    Thanks for sharing!! Awsome job!!
    I used to use the WPF version, now I am gonna grab the SL version!!

  43. Juan Francisco
    July 1, 2011 at 1:18 am | #43

    Thanks a lot, very good job. I have a question, is it possible to personalize the text that appears in the search box? I mean the word “Search” that appears before you write something.

  44. Campilax!
    August 1, 2011 at 10:10 pm | #44

    Big ups man….you are the best!

  45. Sancio
    September 25, 2011 at 2:08 pm | #45

    I have a problem, if i place the SearchTextBox inside a menu, the left mouse click does not work. Every where else it work… Maybe because it is a non-menu item (the textbox i mean) , please help

  46. November 18, 2011 at 10:51 pm | #46

    Awesome!
    I cant stop myself from using this control.
    Cute and thoroughly explained :)
    Thanks for your effort.
    Great work!

  47. March 9, 2012 at 10:21 am | #47

    I very much enjoyed reading your article. I did find that as our designers prepare styles for various widgets, they changed the font, etc. on labels. This affects the labelText property of this “control.” My workaround now is to define in code a SearchLabel class that extends from Label but not mark it as SetResourceReference(…) to a Label.

    Is it possible to do this “extension” just in the control template and not add code?

    thanks!

  48. Kirill
    March 9, 2012 at 10:42 am | #48

    Really cool, very big thanks. Is there any license on your code? I am sorry about placing comment to old post.

  49. March 9, 2012 at 11:49 am | #49

    You may use whatever license you want.

  50. aniNithi
    May 7, 2012 at 3:12 am | #50

    Hi David,

    I am new to WPF, I tried doing this, when I build my uicontrol ,no error but in design I am getting System.windows.Style. Please, can you help in solving this.

  51. aniNithi
    May 9, 2012 at 10:24 pm | #51

    Hi Every one,

    Can anybody help me in solving an error. TargetName property cannot be set on a style setter. I have place triggers and multitriggers in inside

  52. Lelo
    June 1, 2012 at 3:14 am | #52

    The link for Silverlight don’t work!
    Somebody can help me???
    Thanks!

  53. Lelo
    June 1, 2012 at 3:15 am | #53

    The link for Silverlight version don’t work!
    Somebody can help me???
    Thanks!

  54. EricYu
    August 5, 2012 at 6:50 pm | #54

    Is any possible add SearchCommand instead of Search Event?
    Thanks.

  55. T3Jn
    September 6, 2012 at 11:07 am | #55

    This is a great control – thanks!! Was wondering if I am missing something though, trying to bind the Text property to a ViewModel, yet every time the Search event fires the text in the text box is not bound. Now I know this because binding happens when the control loses focus, but is there an eligant way of forcing the binding before the search event fires?

  56. September 6, 2012 at 12:20 pm | #56

    Honestly, I’m not sure. It’s been a very long time since I’ve done any WPF programming.

  57. T3Jn
    September 6, 2012 at 12:59 pm | #57

    Ok – got a solution, if anyone is looking, modify the RaisSearchEvent() from this:

    private void RaiseSearchEvent()
    {
    RoutedEventArgs args = new RoutedEventArgs(SearchEvent);
    RaiseEvent(args);
    }

    to this:

    private void RaiseSearchEvent()
    {
    if (GetBindingExpression(TextBox.TextProperty) != null)
    {
    GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }
    RoutedEventArgs args = new RoutedEventArgs(SearchEvent);
    RaiseEvent(args);
    }

    Hope it helps.

  58. 20082008
    December 12, 2012 at 3:21 am | #58

    Awsome. :) Thanks.

  59. Mikhail
    February 1, 2013 at 3:28 am | #59

    really great solutions

    i wonder why microsoft people have not embedded yet in WPF

  60. February 12, 2013 at 7:46 am | #60

    This is really interesting, You are a very professional
    blogger. I have joined your feed and sit up for seeking more of your fantastic post.
    Also, I have shared your site in my social networks

  61. March 2, 2013 at 9:00 pm | #61

    More CFM is directly proportional to the cool air. Pendant lamps – Pendant lights are
    a good source of job lighting and they can be lowered or elevated depending on
    your wants. Manipulating the effects of lighting has since been known as one way of
    redecorating one’s office and house.

  62. vijay
    March 21, 2013 at 5:48 am | #62

    Super post.. expecting more from you :)

  1. February 19, 2009 at 10:23 pm | #1
  2. February 26, 2009 at 2:12 am | #2
  3. February 26, 2009 at 5:03 am | #3
  4. February 27, 2009 at 12:15 pm | #4
  5. August 20, 2009 at 10:40 am | #5
  6. August 16, 2010 at 8:03 am | #6
  7. May 29, 2011 at 7:03 am | #7
  8. February 20, 2012 at 5:19 pm | #8
  9. April 1, 2013 at 8:48 pm | #9
Comments are closed.
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: