WPF Search Text Box
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:
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.
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:
- A WPF application that will consume my custom control (I named mine TestUI), and
- 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:
- SearchTextBox derives from TextBox now instead of Control
- SearchMode enum defined
- 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.
- Grid – Provides an easy way to layout the two sections of the control we’ll have.
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:
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.
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:
- Hiding the place holder text (i.e. Search)
- Changing the search icon when in instant mode.
- Changing the border brush and background when the mouse is over the search icon.
- 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:
- 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.
- 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
Great article. Many thanks for sharing.
No problem, glad someone out there liked it.
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.
The file is corrupt. Cannot download
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!
Nice post David!
Do you mind me using these sources for property grid project (http://www.codeplex.com/wpfpropertygrid)?
Thanks in advance.
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!
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!
Anyone able to get this? I’m getting a login error.
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.
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?
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.
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!
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?
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:
I can see that worked well.
OK I will try again – ADD
“”
to the Trigger Property IsMouseOver section.
Once again – I can see that worked out about the same..
OK I will try again – ADD
Setter Property=”Cursor” Value=”IBeam”
properly enclosed.
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.
I really like the styling of WPF improvement from winforms. And the explanation is very nice
I’m a beginner in WPF and this tutorial really opened my eyes on custom controls matter. Thank you very much for making it.
@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
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
I’ll see if I can’t create a Silverlight version sometime soon.
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
…problem solved. Nothing to do with your SearchTextBox
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?
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?
Would it be possible to somehow bind this to a list and search the list for an item?
Excellant work.
Great Post!!!
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.
Great job. Thanks for sharing.
This is great! Thanks for posting!
Hey man,
Love the article!!!!
Thx!
This is really great stuff! Exactly what I’m looking for… Got a version for Silverlight?
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
download does not work
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
i could get only the message boxes and nothing else.
what might be the problem?
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?
NVM, I was coding on a static predicate function, thats why.. o_O
Thanks for sharing!! Awsome job!!
I used to use the WPF version, now I am gonna grab the SL version!!
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.
Big ups man….you are the best!
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
Awesome!
I cant stop myself from using this control.
Cute and thoroughly explained
Thanks for your effort.
Great work!
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!
Really cool, very big thanks. Is there any license on your code? I am sorry about placing comment to old post.
You may use whatever license you want.
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.
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
The link for Silverlight don’t work!
Somebody can help me???
Thanks!
The link for Silverlight version don’t work!
Somebody can help me???
Thanks!
Is any possible add SearchCommand instead of Search Event?
Thanks.
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?
Honestly, I’m not sure. It’s been a very long time since I’ve done any WPF programming.
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.
Awsome.
Thanks.
really great solutions
i wonder why microsoft people have not embedded yet in WPF
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
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.
Super post.. expecting more from you