Fun with fonts

So I’ve been playing around with the RichTextBox for WPF and decided that it would be a great idea to add font selection to the code. Obviously, this being WPF, I didn’t want to just list the fonts out, I wanted to list the fonts out in exactly the way they’d be displayed. In other words, I want the font name to be written out using the font itself. By now it shouldn’t come as a surprise to you that this is extremely easy to do in WPF.

First of all, it’s really easy to get a list of the fonts. .NET provides a handy little class cunningly enough known as InstalledFontCollection, so we’ll wrap that up in a handy list ready for use:

 

using System;
using System.Collections.Generic;
using System.Drawing.Text;
using System.Drawing;

namespace FontManager
{
    public class InstalledFonts : List<FontFamily>
    {
        public InstalledFonts()
        {
            InstalledFontCollection fonts = new InstalledFontCollection();
            this.AddRange(fonts.Families);
        }
    }
}

This class just wraps up the installed font families into a handy dataprovider format. This is all about being nice and blend-friendly.

Next we want to define a usercontrol to display the fonts. Something to note about this control; we display the data in a virtualizing stack panel – if you don’t, you could end up waiting quite a while for the first display of the font.

<UserControl
    x:Class="FontManager.InstalledFontDisplay"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:drawing="clr-namespace:System.Drawing;assembly=System.Drawing"
    xmlns:m="clr-namespace:FontManager"
    xmlns:sys="clr-namespace:System.Collections.Generic;assembly=mscorlib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style x:Key="FontStyle">
            <Setter Property="Control.FontFamily" Value="{Binding Name}" />
            <Setter Property="Control.FontSize" Value="16" />
        </Style>
        <DataTemplate x:Key="FontTemplate">
            <StackPanel VirtualizingStackPanel.IsVirtualizing="True">
                <TextBlock
                    Text="{Binding Name}"
                    ToolTip="{Binding Name}"
                    Style="{StaticResource FontStyle}" />
            </StackPanel>
        </DataTemplate>
        <ObjectDataProvider x:Key="FontProvider" ObjectType="{x:Type m:InstalledFonts}"/>
    </UserControl.Resources>
    <ComboBox
            VerticalAlignment="Top"
            ItemsSource="{Binding Source={StaticResource FontProvider}}"
            ItemTemplate="{StaticResource FontTemplate}" />

</UserControl>

That’s it – that’s all there is to displaying your font names in the appropriate font. It is so easy, and yet another reason to love WPF. Go on – you know you love it.

Advertisement

Binding to a single object.

So today, on CodeProject, one of the WPF regulars posed a question about whether or not you could only bind to ObservableCollection objects, and if you could bind to single objects, how could you guarantee two way databinding goodness. As you may well imagine, I promptly answered that you could indeed bind to a single object and implement two way databinding with it. The discussion also posed the question, could WPF bind to properties that linked into child objects and display these – from separate usercontrols.

This seemed to me to be an ideal candidate for demonstrating the beauty and wonder that is the WPF databinding infrastructure. If you’ve only ever been exposed to WinForms or ASP.NET databinding, then you are in for a major treat – WPF (and Silverlight) provide so much more bang for your buck with binding that it’s hard to see how we managed before. First of all, let’s take a look at our business object classes, Benefit and Customer. Both of these classes implement INotifyPropertyChanged so that we get access to the notification mechanism that supports two way binding.

First of all, here’s Benefit.cs:

Benefit.cs

And here’s Customer.cs:

Customer.cs

As you can see, there’s nothing out of the ordinary in these classes – they don’t do anything clever. They are just your bog standard, basic model classes.

Now, here’s the main window XAML:

Window1.xaml

The TextBoxes are bound to the relevant entries in the Customer class using the familiar {Binding Path=…} syntax. But wait, where’s the Benefit class in all this? I added it for a reason, yet there’s no sign of it. Well, as you can see – there’s a reference to a UserControl (BenefitsControl), and we are going to perform the actual binding in this class. Here’s the definition of BenefitsControl.xaml:

benefitscontrol.xaml

Look – there’s the binding Benefits.ProviderName, but would it surprise you to know that I’m not going to put the data class anywhere near this control. I’m going to set the DataContext on the parent control and let the usercontrol “find” the data there. Here’s the implementation of the code behind the parent XAML (Window1.cs):

window1.cs

So – here’s the application in all its glory:

runningapplication

Well – now that we’ve covered the code, how does the databinding actually work? How does the user control actually get its binding to work? This is where the wonder that is WPF actually comes into play. If the binding can’t find data in the data context at the current level, it works its way back up the logical tree until it can. Seriously – I love this stuff, and I’d hate to go back from it.

The code for this application can be downloaded here. Note that you’ll need to change the extension from .doc to .zip.

customerboundsamplezip

Watermarked TextBox

Well, I love mucking around in code and I recently decided to see how easy it would be to knock together a user control that would work in both WPF and Silverlight. If you keep it simple, it is really really easy to do. To this end, I present to you the WatermarkTextbox.

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace WaterBox
{
 
public class WatermarkTextBox : TextBox, INotifyPropertyChanged
 
{
   
public WatermarkTextBox()
    {
      ApplyWatermark();
    }

 

    protected virtual void Changed(string propertyName)
    {
      ApplyWatermark();
     
PropertyChangedEventHandler handler = PropertyChanged;
     
if (handler != null)
      {
        handler(
this, new PropertyChangedEventArgs(propertyName));
      }
    }

    private string _watermarkText = “Watermark”;
   
public string WatermarkText
    {
     
get
     
{
       
return _watermarkText;
      }
     
set
     
{
       
if (_watermarkText != value)
        {
         
if (string.IsNullOrEmpty(Text) || Text == _watermarkText)
          {
            Text =
value;
          }
          _watermarkText =
value;
          Changed(
“WatermarkText”

);
        }
      }
    } 
 

 

 

    private System.Windows.Media.Brush _watermarkForeground = new SolidColorBrush(Colors.LightGray);
   
public System.Windows.Media.Brush WatermarkForeground
    {
     
get
     
{
       
return _watermarkForeground;
      }
     
set
     
{
       
if (_watermarkForeground != value)
        {
          _watermarkForeground =
value;
          Changed(
“WatermarkForeground”

);
        }
      }
    } 
 

 

 

    private System.Windows.Media.Brush _watermarkBackground = new SolidColorBrush(Colors.Yellow);
   
public System.Windows.Media.Brush WatermarkBackground
    {
     
get
     
{
       
return _watermarkBackground;
      }
     
set
     
{
       
if (_watermarkBackground != value)
        {
          _watermarkBackground =
value;
         Changed(
“WatermarkBackground”

);
        }
      }
    } 
 

 

 

    private System.Windows.Media.Brush _standardForeground = new SolidColorBrush(Colors.Black);
   
public System.Windows.Media.Brush StandardForeground
    {
     
get
     
{
       
return _standardForeground;
      }
      set
     
{
       
if (_standardForeground != value)
        {
          _standardForeground =
value;
          Changed(
“StandardForeground”

);
        }
      }
    } 
 

 

 

    private System.Windows.Media.Brush _standardBackground = new SolidColorBrush(Colors.White);
   
public System.Windows.Media.Brush StandardBackground
    {
     
get
     
{
       
return _standardBackground;
      }
     
set
     
{
       
if (_standardBackground != value)
        {
          _standardBackground =
value;
          Changed(
“StandardBackground”);
        }
      }
   

} 
 

 

 

    private void 

SetStandard()
    {
      Foreground = StandardForeground;
      Background = StandardBackground;
    } 
 

 

 

    protected override void OnGotFocus(RoutedEventArgs  e)
    {
     
if (Text == WatermarkText)
      {
        Text =
string.Empty;
        SetStandard();
      }
     
base

.OnGotFocus(e);
    } 
 

 

 

    protected override void OnLostFocus(RoutedEventArgs e)
    {
      Text = Text.Trim();
     
if (Text.Length == 0)
      {
        ApplyWatermark();
      }
     
base

.OnLostFocus(e);
    } 
 

 

 

    private void ApplyWatermark()
    {
     
if (string.IsNullOrEmpty(Text) || Text == WatermarkText)
      {
        Text = WatermarkText;
        Foreground = WatermarkForeground;
        Background = WatermarkBackground;
      }
    }

    #region

 
INotifyPropertyChanged Members
   
public event PropertyChangedEventHandler PropertyChanged;
   
#endregion
 
}
}

 

Please feel free to use this class as you like. It comes with no warranty because it’s intended as a proof of concept only. It can do with a tidy up, and it should use Coerce values but I leave that for you to do.