Tuesday, 10 October 2006

The next challenge that I am going to share my solution with you is the Office 2007 like window style. As you know, the Office 2007 applications are using the layered windows functionality to display rounded corners and a custom title bar. This is how the typical  window in the new Office looks like:

 

Since the June CTP, WPF allows you to create layered windows by setting the AllowsTransparency to true, Background to Transparent and WindowsStyle to none. Result of that will be a transparent window with no titlebar or borders, so it is our task to draw a non-client area. It didn’t take long time for me to create XAML to display a titlebar with images on a particular window, but what do I do in order to re-use this style on any window in my app? It took me a while to figure out (trial and error again and again…) that the only way right now to declare a style/template for a Window is to do it in the App.xaml, so my first version contained all the xaml and a code in the App.xaml and App.xaml.cs which I didn’t feel was a good way to encapsulate your logic in a re-usable component. Luckily for me, I got to work with Josh Smith who knows quite a few things about WPF, showed me a way how to move my logic into a separate re-usable files. Which is to create XAML file as a ResourceDictionary and a corresponding code file. Don’t forget to declare x:Class attribute in order to associate the code file with this XAML:


  x:Class="OfficeStyleWindowProject.OfficeStyleWindow"
  xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml
   xmlns:ctl="clr-namespace:Controls.Local"
    >

The App.xaml will have to be changed as well:


<Application.Resources>
   <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
         <ResourceDictionary Source="OfficeStyleWindow.xaml" />
       <ResourceDictionary.MergedDictionaries>
   <ResourceDictionary>
                     

Of course as soon as I moved to the “templated” world, the event handlers stopped working and I had to do the following: First of all, I created a few custom controls: ImageButton and TitleBar. I would imagine the ImageButton should be a pretty useful control on its own. It implements a few dependency properties ImageNormal, ImageOver and ImageDown which you would use to assign an appropriate images to a button. This control provides similar functionality to what Josh had described in his blog.
There is nothing too fancy in the TitleBar except for a way to hook up into the CloseButton, MinButton and MaxButton Click events. Here’s a snippet that shows how I did it:

First we need to hook up into the control’s Loaded event:

public TitleBar()
{

   this.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarLeftButtonDown);                   

this.MouseDoubleClick += new MouseButtonEventHandler(TitleBar_MouseDoubleClick);
   this.Loaded += new RoutedEventHandler(TitleBar_Loaded);

}

Second, in the loaded event we need to use the FindName method to get to the instances of the buttons:

void TitleBar_Loaded(object sender, RoutedEventArgs e)
{
      closeButton = (ImageButton)this.Template.FindName("CloseButton", this);
      minButton = (ImageButton)this.Template.FindName("MinButton", this);
      maxButton = (ImageButton)this.Template.FindName("MaxButton", this);
 
      closeButton.Click += new RoutedEventHandler(CloseButton_Click);
      minButton.Click += new RoutedEventHandler(MinButton_Click)
      maxButton.Click += new RoutedEventHandler(MaxButton_Click);
}

Overall I am pretty happy with the result. The code has become completely encapsulated in a separate and re-usable files and can be easily added to any project. Here's how the window looks in the test project:

You can download the project with the complete source code from here:

OfficeStyleWindow.zip (39.66 KB)
WPF
10/10/2006 17:43:35 (GMT Daylight Time, UTC+01:00)  #     |