Saturday, May 29, 2010

Useful Snippets of Xaml

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="WidgetSearch3000.Window1" 
    x:Name="MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:local="clr-namespace:WidgetSearch3000" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    DataContext="{Binding ElementName=MainWindow}" 
    Height="300" 
    Icon="SearchIcon.png" 
    Title="Widget 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>

Code behind: (This is not a demonstration of how to format your code behind nor to write clean code!) :-)


namespace WidgetSearch3000 {
    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("Frank", "Smith"));
            m_lsPeople.Add(new Person("Joe", "Yakk"));
            m_lsPeople.Add(new Person("Grant", "Wellington"));
            m_lsPeople.Add(new Person("Lucy", "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();
        }
    }
}

No comments:

Post a Comment