Here's a sample project a team member worked on as an exercise. The object was to write a search interface and come up with a good user interface. This example shows how to highlight text in a string based on the search criteria, a nice data template, and changing the visual selection queue. <Window x:Class="Zeacomsearch3000.Window1" x:Name="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:Zeacomsearch3000" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" DataContext="{Binding ElementName=MainWindow}" Height="300" Icon="SearchIcon.png" Title="Zeacom Searcher 3001" Width="300"> <Window.Resources> <local:TextFormatterConverter x:Key="TextFormatter" /> <Style x:Key="FoundTextStyle" TargetType="{x:Type Run}"> <Setter Property="Foreground" Value="Red" /> <Setter Property="FontWeight" Value="Bold" /> <Setter Property="Run.TextDecorations" Value="Underline" /> </Style> <Style x:Key="searchTextBox" TargetType="TextBox"> <Setter Property="FontSize" Value="12pt" /> <Setter Property="FontFamily" Value="Tahoma" /> <Setter Property="FontWeight" Value="Bold" /> <Setter Property="Foreground" Value="Black" /> <Setter Property="Template"> <Setter.Value> <!-- This is the custom control template we will apply to the TextBox --> <ControlTemplate TargetType="TextBox"> <Border Background="WhiteSmoke" BorderBrush="LightSteelBlue" BorderThickness="6" CornerRadius="7" Padding="7"> <Grid> <ScrollViewer x:Name="PART_ContentHost" /> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <DataTemplate x:Key="ContactTemplate"> <Grid Margin="5, 5, 5, 5"> <Grid.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="1" ScaleY="1" /> </TransformGroup> </Grid.RenderTransform> <Border x:Name="Grid" BorderBrush="Black" BorderThickness="1.5" CornerRadius="10" Grid.Row="1" Height="50" Padding="5" VerticalAlignment="Stretch"> <Border.Background> <SolidColorBrush x:Name="HighlightBrushName" Color="WhiteSmoke" /> </Border.Background> <TextBlock x:Name="Text" FontSize="15" HorizontalAlignment="Stretch" VerticalAlignment="Center"> <TextBlock.IsEnabled> <MultiBinding Converter="{StaticResource TextFormatter}"> <Binding Path="FullName" /> <Binding ElementName="tSearchString" Path="Text" /> <Binding RelativeSource="{RelativeSource Self}" /> <Binding Source="{StaticResource FoundTextStyle}" /> </MultiBinding> </TextBlock.IsEnabled> </TextBlock> </Border> </Grid> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True"> <Setter Property="BitmapEffect" TargetName="Grid"> <Setter.Value> <DropShadowBitmapEffect /> </Setter.Value> </Setter> <Setter Property="Foreground" TargetName="Text" Value="Black" /> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> <ColorAnimation Duration="00:00:01" Storyboard.TargetName="HighlightBrushName" Storyboard.TargetProperty="Color" To="#FFFDFF2F" /> <DoubleAnimation Duration="00:00:00.50" Storyboard.TargetName="Grid" Storyboard.TargetProperty="Height" To="60" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <BeginStoryboard> <Storyboard> <ColorAnimation Duration="00:00:00.50" Storyboard.TargetName="HighlightBrushName" Storyboard.TargetProperty="Color" To="WhiteSmoke" /> <DoubleAnimation Duration="00:00:00.50" Storyboard.TargetName="Grid" Storyboard.TargetProperty="Height" To="50" /> </Storyboard> </BeginStoryboard> </DataTrigger.ExitActions> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="10" /> <RowDefinition Height="50" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid Grid.Row="1" VerticalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition Width="60" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="30" /> </Grid.ColumnDefinitions> <Label x:Name="label1" FontSize="12" FontWeight="Bold" Grid.Column="0" Height="28"> Search: </Label> <TextBox x:Name="tSearchString" Grid.Column="1" HorizontalAlignment="Stretch" Style="{StaticResource searchTextBox}" TextChanged="tSearchString_TextChanged" /> </Grid> <GroupBox x:Name="groupBox1" Grid.Row="2" Header="Results" VerticalAlignment="Stretch"> <ListBox x:Name="listOfPeople" HorizontalContentAlignment="Stretch" ItemsSource="{Binding FoundPeople}" ItemTemplate="{StaticResource ContactTemplate}" Padding="3" ScrollViewer.VerticalScrollBarVisibility="Visible"> <ListBox.Resources> <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" /> </ListBox.Resources> </ListBox> </GroupBox> </Grid> </Window> namespace Zeacomsearch3000 { using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Collections.ObjectModel; public class TextFormatterConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var name = values[0] as String; if (name == null) { return true; } var nameLower = name.ToLower(); var search = values[1] as String; if (search == null) { return true; } var searchLower = search.ToLower(); var t = values[2] as TextBlock; if (t == null) { return true; } var runStyle = values[3] as Style; if (runStyle == null) { return true; } t.Inlines.Clear(); Run run; int index = nameLower.IndexOf(searchLower); if (!searchLower.Equals("")) { while (index != -1) { if (index != 0) { run = new Run(); //run.Foreground = Brushes.Black; run.Text = name.Substring(0, index); t.Inlines.Add(run); } run = new Run(); run.Text = name.Substring(index, search.Length); run.Style = runStyle; t.Inlines.Add(run); name = name.Substring(index + search.Length); nameLower = nameLower.Substring(index + search.Length); index = nameLower.IndexOf(searchLower); } } if (!name.Equals("")) { run = new Run(); //run.Foreground = Brushes.Black; run.Text = name; t.Inlines.Add(run); } return true; //} //return string.Format("this is a test: {0} {1}", values[0], values[1]); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { private List<Person> m_lsPeople; private ObservableCollection<Person> m_lsFound = new ObservableCollection<Person>(); public Window1() { InitializeComponent(); //m_lsFound = new ObservableCollection<Person>(); m_lsPeople = new List<Person>(); m_lsPeople.Add(new Person("Paul", "Machinroe")); m_lsPeople.Add(new Person("Paul", "Smith")); m_lsPeople.Add(new Person("Luan", "Yakk")); m_lsPeople.Add(new Person("Grant", "Wellington")); m_lsPeople.Add(new Person("Giampaolo", "Fiat")); m_lsPeople.Sort(); //DataContext = this; Search(); } public void Search() { // m_lsFound.Clear(); Boolean hasAdded = false; foreach (Person person in m_lsPeople) { if (!person.Contains(tSearchString.Text)) { m_lsFound.Remove(person); } else if (!m_lsFound.Contains(person)) { hasAdded = false; for (int i = 0; i < m_lsFound.Count; i++) { if (person.CompareTo(m_lsFound[i]) < 0) { m_lsFound.Insert(i, person); hasAdded = true; break; } } if (!hasAdded) { m_lsFound.Add(person); } } } } public ObservableCollection<Person> FoundPeople { get { return m_lsFound; } set { m_lsFound = value; } } private void tSearchString_TextChanged(object sender, TextChangedEventArgs e) { Search(); } } } |