Posted by Joseph on 1/10/2006 10:20 PM | Comments (1)

To try to get “out of my comfort zone” (which is pretty small to begin with) in WPF I decided to try and emulate some of the UI enhancements I've heard about in Office 12. One of them is called “live preview” which shows you in real-time what potential changes (like increasing the font size, making something bold etc) will look like in your document as you you mouse over things. I implemented a “live preview” of the font changing. This was cool because I got to implement a font-selection dialog in WPF (creating a collection of system fonts and binding it to a dropdown list in WPF took about 5 lines of C# and 3 lines of XAML), and play with a few styles and such. In addition to this I had a look at manipulating the super-flexible WPF RichTextBox. I'm pretty sure if you didn't care about interoperability with MSWord you could build a fully-featured word processor using this control in a few days. One challenge was that none of the WPF UI elements provide a “hover” event (the font is only supposed to update if you hover over the same item for a little while). I had to fake it (poorly) using a timer. There are a few other glaring imperfections too - for example if you “preview” a font change but then don't actually select anything then your document DOESN'T go back to the way it was before...but hey - its a demo.
font selection dropdown list selecting some fonts ala Office12 LivePreview

The reason the font dropdown list is so wide is because some of the names of the crazy fonts I installed to play around with this feature. There is a video of it in action also here [180 KB].

Here is the XAML (for the Dec 05 CTP):

<?Mapping XmlNamespace="urn:JCooney.Net.SeeingIsBelieving" ClrNamespace="SeeingIsBelieving" ?>
<Window x:Class="SeeingIsBelieving.Window1"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    xmlns:local="urn:JCooney.Net.SeeingIsBelieving" 
    Title="SeeingIsBelieving"
    Loaded="mainWindowLoaded"
    Width="450"
    Height="200"
    >
  <Window.Resources>
    <local:FontNameList x:Key="InstalledFonts"/>
    <DataTemplate x:Key="FontComboTemplate">
      <TextBlock Text="{Binding}" FontFamily="{Binding}" MouseEnter="fontSelectionMouseEnter"></TextBlock>
    </DataTemplate>
    <LinearGradientBrush x:Key="SelectedItemBackground" StartPoint="0,0" EndPoint="0,1">
      <LinearGradientBrush.GradientStops>
        <GradientStop Color="#e0e4ee" Offset="0.1"/>
        <GradientStop Color="#c7d1e4" Offset="0.4"/>
        <GradientStop Color="#acbbd9" Offset="0.5"/>
        <GradientStop Color="#d5dcec" Offset="0.95"/>
      </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    <Style TargetType="{x:Type ComboBoxItem}">
      <Setter Property="BorderThickness" Value="1" />
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Background" Value="{StaticResource SelectedItemBackground}"/>
          <Setter Property="BorderBrush" Value="Blue"/>
          <Setter Property="Foreground" Value="Black"/>
        </Trigger>
        <Trigger Property="IsKeyboardFocused" Value="True">
          <Setter Property="Background" Value="{StaticResource SelectedItemBackground}"/>
          <Setter Property="BorderBrush" Value="Blue"/>
          <Setter Property="Foreground" Value="Black"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>
  <DockPanel LastChildFill="True" >
    <DockPanel LastChildFill="False" DockPanel.Dock="Top">
      <ComboBox DockPanel.Dock="Right" x:Name="fontComboBox" 
        FontSize="20" 
        MouseLeave="fontComboMouseLeave" 
        SelectionChanged="fontComboSelectionChanged" 
        ItemsSource="{StaticResource InstalledFonts}" 
        ItemTemplate="{StaticResource FontComboTemplate}"/>
      <TextBlock DockPanel.Dock="Right">Fonts:</TextBlock>
    </DockPanel>
    <Border BorderBrush="Navy" CornerRadius="5" BorderThickness="2">
    <RichTextBox x:Name="testInput" AcceptsReturn="True" Padding="10,10,10,10" FontSize="20">
      <FlowDocument>
        <Paragraph>The quick brown fox jumps over the lazy dog.</Paragraph>
      </FlowDocument>
    </RichTextBox>
    </Border>  
  </DockPanel>
</Window>

Here is the C# (also for the Dec 05 CTP)

using System;
using System.Drawing;
using System.Drawing.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace SeeingIsBelieving
{
    /// 
    /// Interaction logic for Window1.xaml
    /// 
    public partial class Window1 : Window
    {
        System.Windows.Threading.DispatcherTimer _hoverTimer = new System.Windows.Threading.DispatcherTimer();
        string _fontFamilyName;
        public Window1()
        {
            InitializeComponent();
        }
        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
        }
        private void mainWindowLoaded(object sender, RoutedEventArgs e)
        {
            _hoverTimer.Interval = new TimeSpan(0, 0, 0, 2);
            _hoverTimer.Tick += new EventHandler(_hoverTimerTick);
        }
        void _hoverTimerTick(object sender, EventArgs e)
        {
            SetTextSelectionFont(_fontFamilyName);
        }
        private void SetTextSelectionFont(string fontFamilyName)
        {
            System.Windows.Media.FontFamily f = new System.Windows.Media.FontFamily(fontFamilyName);
            testInput.Selection.ApplyPropertyValue(TextElement.FontFamilyProperty, f);
        }
        private void fontSelectionMouseEnter(object sender, RoutedEventArgs e)
        {
            TextBlock t = (TextBlock)sender;
            _fontFamilyName = t.Text;
            _hoverTimer.IsEnabled = false;
            _hoverTimer.IsEnabled = true;
        }
        private void fontComboSelectionChanged(object sender, RoutedEventArgs e)
        {
            SetTextSelectionFont((string)fontComboBox.SelectedItem);
        }
        private void fontComboMouseLeave(object sender, RoutedEventArgs e)
        {
            _hoverTimer.IsEnabled = false;
        }
    }
    public class FontNameList : System.Collections.Generic.List
    {
        public FontNameList()
        {
            using (InstalledFontCollection fonts = new InstalledFontCollection())
            {
                foreach (System.Drawing.FontFamily f in fonts.Families)
                {
                    base.Add(f.Name);
                }
            }
        }
    }
}

Comments

JosephCooney on 1/10/2006 11:50 PM That should be "a font-selection drop-down" not "a font-selection dialog". I would change it except .Text/FreeTextBox drops all my XAML formatting when I try to do so.
Comments are closed