Archive for December 2007

Designers Guide to Debugging WPF Applications with Snoop

UPDATE: The makers of Snoop have made an installer that makes this post obsolete. Go get it… it’s well updated and worth it.

Download the super-simple Snoop files. (Just extract and run.)

Snoop is a great tool for debugging WPF applications.

It allows one to see the entire tree of the project that one is working on. For me, this meant that I can find out quickly and easily if the object I’m trying to target with a particular style or template is acutally implementing that style or template. I can check the properties of the items I thought I changed only to discover that nothing is changing. In a recent project, when I was flabergasted as to why my design wasn’t showing up, Snoop helped me determine that it was an error in the code implementation, which is translated roughly into “not my fault”.

 Unfortunately, when I first realized I needed it, it took me a while to figure out how to get it to run.

 “a while” in this instance means 3-4 hours and one hour worth of IM with a developer friend.

The problem that I had was that actually running Snoop isn’t a very designer friendly process. You have to download the source code, convert the file formats, change the read-only settings, and a number of other things before you can get it to work. So I decided to give anyone who isn’t interested in that process a shortcut to debugging their WPF applications.

Just download this and run the snoop.exe file. No Visual Studio nonsense required. Check this link for more information on using Snoop.

How Do I Center Something In My ListView Column?

So the items in your ListView column look like this:

Centered Example 1

And you want them to look like this:

Centered Example 2

If you’re trying to center something within a ListView column, it’s actually pretty simple.

Continue reading ‘How Do I Center Something In My ListView Column?’ »

MarkupExtension

Over at nostatic.com, there is an example of <a href=”http://notstatic.com/archives/118″>using MarkupExtension</a> that that we can access some custom code functionality directly from the XAML.

 In his example, the following XAML accesses a random number generator within the code.

<Button>

   <Button.RenderTransform>

      <RotateTransform Angle="{l:RandomNumber Min=0, Max=360}" />

   </Button.RenderTransform>

</Button>

Pretty cool. Apparently it only works when it isn’t placed inside a template… which makes it of limited usefulness, but still pretty neat.

Building Custom Template-able WPF Controls

Building custom controls in WPF can provide you with lots of flexibility.  It allows you to entirely separate the behavior of the control from the look of the control.  This is the premise behind most of what WPF offers.  In this post I will show you how you can build a simple control similar to the search control in Outlook 2007.

Filter TextBox

Add a new WPF Application project.

New Project

Then add a WPF User Control Library.

Add New Project

Delete the generated UserControl1.xaml that was given to you.

Microsoft Visual Studio

Add a new WPF Custom Control.

Add New Item - FilterTextBox

Your solution should now look like this:

Solution Explorer

The template gives you a FilterTextBox.cs and Generic.xaml file.

public class FilterTextBox : Control
{
    static FilterTextBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterTextBox),
            new FrameworkPropertyMetadata(typeof(FilterTextBox)));
    }
}

The default Generic.xaml is the default look for your custom control, and is found in the Theme directory.  It is just an empty border for now:

<Style TargetType="{x:Type local:FilterTextBox}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Now lets start building the behavior of our control.  We’ll start by adding a ‘Text’ dependency property.  This will be the filter text that the user types in.  Notice that I’ve created callbacks to be notified when the property has changed.  And as always, do NOT put any code within the getter and setter of the CLR property, because WPF bypasses this property at runtime and calls GetValue and SetValue directly.  However the CLR property is still needed to use the property in xaml.

public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register("Text",
                                typeof(String),
                                typeof(FilterTextBox),
                                new UIPropertyMetadata(null,
                                    new PropertyChangedCallback(OnTextChanged),
                                    new CoerceValueCallback(OnCoerceText))
                               );

private static object OnCoerceText(DependencyObject o, Object value)
{
    FilterTextBox filterTextBox = o as FilterTextBox;
    if (filterTextBox != null)
        return filterTextBox.OnCoerceText((String)value);
    else
        return value;
}

private static void OnTextChanged(DependencyObject o,
                                  DependencyPropertyChangedEventArgs e)
{
    FilterTextBox filterTextBox = o as FilterTextBox;
    if (filterTextBox != null)
        filterTextBox.OnTextChanged((String)e.OldValue, (String)e.NewValue);
}

protected virtual String OnCoerceText(String value)
{
    return value;
}

protected virtual void OnTextChanged(String oldValue, String newValue)
{
}

public String Text
{
    // IMPORTANT: To maintain parity between setting a property in XAML
    // and procedural code, do not touch the getter and setter inside
    // this dependency property!
    get
    {
        return (String)GetValue(TextProperty);
    }
    set
    {
        SetValue(TextProperty, value);
    }
}

We’ll want users of the control to be notified when the text in our TextBox has changed, so lets create a ‘TextChanged’ event.

public static readonly RoutedEvent TextChangedEvent =
    EventManager.RegisterRoutedEvent("TextChanged",
                                    RoutingStrategy.Bubble,
                                    typeof(RoutedEventHandler),
                                    typeof(FilterTextBox));

public event RoutedEventHandler TextChanged
{
    add { AddHandler(TextChangedEvent, value); }
    remove { RemoveHandler(TextChangedEvent, value); }
}

Now lets go back and modify our OnTextChanged method to raise our TextChanged event.

protected virtual void OnTextChanged(String oldValue, String newValue)
{
    // fire text changed event
    this.RaiseEvent(new RoutedEventArgs(FilterTextBox.TextChangedEvent, this));
}

With the base behavior mostly done, we can move on to creating a generic look for our control.  Lets add a DockPanel with a Button and a TextBox, the Button docked to the right.  Lets also bind the ‘Text’ property of the TextBox to the ‘Text’ property of our control.  We want the ‘UpdateSourceTrigger’ to be ‘PropertyChanged’ so that the ‘TextChanged’ event we created will be fired every time the user types something into the TextBox.  Notice that we don’t want the TextBox to have a border because then we’d have two borders.

<ControlTemplate TargetType="{x:Type local:FilterTextBox}">
  <Border
      Background="{TemplateBinding Background}"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}"
      CornerRadius="3">
    <DockPanel
      LastChildFill="True"
      Margin="1">
      <Button
        x:Name="PART_ClearFilterButton"
        Content="X"
        Width="20"
        ToolTip="Clear Filter"
        DockPanel.Dock="Right" />
      <TextBox
        x:Name="PART_FilterTextBox"
        Text="{Binding Path=Text,
                      Mode=TwoWay,
                      UpdateSourceTrigger=PropertyChanged,
                      RelativeSource={RelativeSource TemplatedParent}}"
        BorderBrush="{x:Null}"
        BorderThickness="0"
        VerticalAlignment="Center" />
    </DockPanel>
  </Border>
</ControlTemplate>

Take special note of the names of the controls.  They both begin with ‘PART_’.  This is the standard way in WPF to signify controls that need to be replaced if you decide to change the template of the control.  If someone templates your control, you need some way to identify that the types used for your parts need to be ones that your control can use.  You can do this by using the TemplatePart attribute and giving it the name and type of your control parts.

[TemplatePart(Name = "PART_FilterTextBox", Type = typeof(TextBox))]
[TemplatePart(Name = "PART_ClearFilterButton", Type = typeof(Button))]
public class FilterTextBox : Control
{
    ...
}

We only want the ‘Clear Filter’ button to be showing when there is text in the TextBox, so lets create a DataTrigger to accomplish that for us.

<ControlTemplate TargetType="{x:Type local:FilterTextBox}">
  <Border>
    ...
  </Border>
  <ControlTemplate.Triggers>
    <DataTrigger Binding="{Binding Path=Text.Length, ElementName=PART_FilterTextBox}" Value="0">
      <Setter TargetName="PART_ClearFilterButton" Property="Visibility" Value="Collapsed" />
    </DataTrigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

Now we want to be able to handle when a user clicks on the ‘Clear Filter’ button.  You do this by overriding the OnApplyTemplate method.  You can get a reference to your controls by calling GetTemplateChild and passing the name of the control.  When the user clicks the ‘Clear Filter’ button we just want to remove any text in the TextBox.  We’ll use the same strategy to get a reference to the TextBox control.

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    Button clearFilterButton = base.GetTemplateChild("PART_ClearFilterButton") as Button;
    if (clearFilterButton != null)
    {
        clearFilterButton.Click += new RoutedEventHandler(ClearFilterButton_Click);
    }
}

private void ClearFilterButton_Click(Object sender, RoutedEventArgs e)
{
    TextBox textBox = base.GetTemplateChild("PART_FilterTextBox") as TextBox;

    if (textBox != null)
    {
        textBox.Text = String.Empty;
    }
}

In the WPF Application project add a reference to the custom control project.

Add Reference

Now you can create a namespace for the controls project, labeled as controls here, and add your control to the form.

<Window
    x:Class="FilterTextBoxDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:FilterTextBox;assembly=FilterTextBox"
    Title="FilterTextBox Demo"
    Height="300"
    Width="300">
  <StackPanel>
    <controls:FilterTextBox />
  </StackPanel>
</Window>

And we have a working control!

Filter TextBox Demo

To make sure that the control still works when templated, lets go ahead and make a simple template for it.

<Window ...>
  <Window.Resources>
    <Style x:Key="LOCAL_MyStyle" TargetType="{x:Type controls:FilterTextBox}">
      <Setter Property="BorderBrush" Value="CornFlowerBlue" />
      <Setter Property="BorderThickness" Value="2" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type controls:FilterTextBox}">
            <Border
              Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              CornerRadius="10">
              <DockPanel
                LastChildFill="True"
                Margin="5">
                <Button
                  x:Name="PART_ClearFilterButton"
                  Content="--"
                  Width="30"
                  ToolTip="Clear Filter"
                  DockPanel.Dock="Left" />
                <TextBox
                  x:Name="PART_FilterTextBox"
                  Text="{Binding Path=Text,
                          Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged,
                          RelativeSource={RelativeSource TemplatedParent}}"
                  BorderBrush="{x:Null}"
                  BorderThickness="0"
                  VerticalAlignment="Center" />
              </DockPanel>
            </Border>
            <ControlTemplate.Triggers>
              <DataTrigger Binding="{Binding Path=Text.Length, ElementName=PART_FilterTextBox}" Value="0">
                <Setter TargetName="PART_ClearFilterButton" Property="Visibility" Value="Collapsed" />
              </DataTrigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <StackPanel>
    <controls:FilterTextBox
      BorderBrush="#ACBFE4"
      Margin="10"
      TextChanged="FilterTextBox_TextChanged"/>
    <controls:FilterTextBox
      Style="{StaticResource LOCAL_MyStyle}"
      Margin="10"
      TextChanged="FilterTextBox_TextChanged"/>
  </StackPanel>
</Window>

Hope that helps!

Joe

Ellipsis and TextWrapping in a ListView Column

In response to the questions:

How do I make it so that, instead of cutting off the text in my column when I shorten it, my text displays ellipsis?

and

How do I make it so that my text wraps when I shorten my columns?

I’ll deal with the first one first and the second one… second.

That sounded better in my head than it looks on the screen.

With the first question… when a column is shortened, this is what normally happens:

CellTemplate Default Text

And, instead, we want this to happen: 

CellTemplate Ellipss

Continue reading ‘Ellipsis and TextWrapping in a ListView Column’ »

Styling A ListView Column Using Blend

So you’ve got a listview and you want the columns (not the rows) to look a certain way. (If you are in any way confused, head over here.)

In this post, we’re going to build a template for a column and style that template so the data in that column is all styled the same way, but different from the data in the rest of the list view. Kind of like this:

CellTemplate Example

If you’re following from the RSS Feed post: go to the listview in the main composition and right click on your listivew and go to “Edit Other Templates -> Edit Generated Item (ItemTemplate) -> Edit a Copy…”

Not following from the RSS Feed post: go to the listview in your composition, right click on your listview and co to “Edit Other Templates -> Edit Generated Item (ItemTempalte) -> Create Empty…”

Edit Item Template 2

 Name it whatever you like (I named mine “TitleTemplate”) and hit OK.

Continue reading ‘Styling A ListView Column Using Blend’ »

How do I connect an RSS Feed to my ListView/ListBox/Whatever?

When I’m doing the initial design for a project (while the developers are working on silly things like data validation and security), I often find myself in need of data so that I can see my project in action. The developers, of course, have no interest in conjuring up test data at that phase in the project, so I started using RSS feeds as data sources for my initial designs.

Not all RSS feeds are equal in the eyes of Blend. I use one of the New York Times RSS feeds, which always work flawlessly.

So we’re looking at the listview/listbox/whatever in our project. Right click on it (in the composition or in the Objects and Timeline pane, it doesn’t matter) and select the “Bind ItemsSource to Data”.

ItemSourceMenu

Continue reading ‘How do I connect an RSS Feed to my ListView/ListBox/Whatever?’ »

ListView FAQ

Please feel free to post additional questions in the comments section. I’ll get to them as I can and then link the help here.

That’s all I got for now. Ask me some questions if you want more stuff.

If you’re looking for something more generic, check out my WPF Designers Guide to the ListView, which covers the broader topics in the ListView.

How Do I Set Up Grid Lines for my ListView?

So you want grid lines in your listview, huh? Something that looks a little like this?

 listView Grid Lines

OK, we can do this the easy way and the hard way.
Continue reading ‘How Do I Set Up Grid Lines for my ListView?’ »

Getting the Popup to Close When It Loses Focus

On a recent project, I spent (I kid you not) over three hours trying to get a stinking popup to close when I clicked somewhere else in the application.

 Figuring it was a focus issue, I tried the following triggers:

  • IsFocused
  • IsKeyboardFocused
  • IsKeyboardFocusWithin
  • IsMouseCapture
  • IsMouseCaptureWithin

All to no avail.

Turns out that the only thing I had to do was set the StaysOpen property of the Popup thus:

<Popup StaysOpen=”False>

Handy little property. I just wish that it was the default.