Creating a search box using WPF

galbru picture galbru · Jun 11, 2013 · Viewed 13.5k times · Source

I'm trying to create a search box with the controls TextBox and ListBox. When I'm on the TextBox starting to type an event handler GotFocus open the ListBox. I want the ListBox to be closed when the TextBox not on focus. I tried to use the LostFocus event in the TextBox, but it didn't work.

In which event handler should I use? I didn't find a good example implements this mechanism.

Edit: I have something like that:

<Canvas Name="m_MainCanvas" Grid.ColumnSpan="2" >
<Rectangle MouseDown="Canvas_MouseDown" Fill="White" Height="280" HorizontalAlignment="Left" Margin="256,12,0,0" x:Name="m_MainRectangle" RadiusX="0" RadiusY="0" Stroke="Black" StrokeThickness="3" VerticalAlignment="Top" Width="238" />
<TextBox Height="23" Margin="10,0,0,210" Name="m_SearchTextBox" VerticalAlignment="Bottom" BorderThickness="0.5" BorderBrush="#69000000" TextChanged="m_SearchTextBox_TextChanged" FontFamily="Kristen ITC" Text="" FontSize="14" FontWeight="Black" HorizontalAlignment="Left" Width="165" Canvas.Top="86" Canvas.Left="274" LostFocus="m_SearchTextBox_LostFocus"/>
<ListBox  ItemTemplate="{DynamicResource ListBoxItemDataTemplate}" ItemsSource="{Binding}" Name="m_FriendsSearchList" Visibility="Hidden" Background="#FFBCEB85"  Width="181" Height="193"  Canvas.Left="283" Canvas.Top="118">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <EventSetter Event="MouseDoubleClick" Handler="HandleDoubleClickItemToSelect" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

The MouseDown event in the Rectangle control is used to get the TextBox out of focus, but it isn't working.

Answer

Viv picture Viv · Jun 11, 2013

something like:

<StackPanel>
  <TextBox>
    <TextBox.Triggers>
      <EventTrigger RoutedEvent="GotFocus">
        <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation Duration="0:0:0"
                              Storyboard.TargetName="lb"
                              Storyboard.TargetProperty="(ListBox.Opacity)"
                              To="1" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
      <EventTrigger RoutedEvent="LostFocus">
        <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation Duration="0:0:0"
                              Storyboard.TargetName="lb"
                              Storyboard.TargetProperty="(ListBox.Opacity)"
                              To="0" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
    </TextBox.Triggers>
  </TextBox>
  <ListBox x:Name="lb"
            Opacity="0">
    <ListBoxItem Content="A" />
    <ListBoxItem Content="B" />
  </ListBox>
</StackPanel>

However in the above approach the ListBox is still holding the space it resides in. if we instead toggle Visibility to Collapsed, every-time the ListBox becomes Visible other control's are going to start moving to accomodate that.

To avoid both these cases, such things are normally implemented via a Popup

<StackPanel>
  <TextBox x:Name="tb" />
  <Popup Width="{Binding RelativeSource={RelativeSource Self},
                          Path=PlacementTarget.ActualWidth}"
          Placement="Bottom"
          PlacementTarget="{Binding ElementName=tb}">
    <Popup.Style>
      <Style TargetType="{x:Type Popup}">
        <Setter Property="IsOpen"
                Value="False" />
        <Style.Triggers>
          <DataTrigger Binding="{Binding ElementName=tb,
                                          Path=IsFocused}"
                        Value="True">
            <Setter Property="IsOpen"
                    Value="True" />
          </DataTrigger>
        </Style.Triggers>
      </Style>
    </Popup.Style>
    <ListBox>
      <ListBoxItem Content="A" />
      <ListBoxItem Content="B" />
    </ListBox>
  </Popup>
</StackPanel>

^^ You'll still see the same output however you no longer have any control's that have to change position due to the popup changing open / close states.

Update:

Download Link

Remember the Trigger is set based on the TextBox having / losing focus. IF you click somewhere that does not take focus, then you will not see the Popup(ListBox) disappear.