Friday, August 16, 2013

Create Custom Windows in WPF

In this article, I am demonstrating how to create a custom window theme visually using declarative XAML and apply it to windows in your applications.

You can download the full source code here.

The first thing you may want to do in creating a custom window is to actually create a visual template.

The following extension points are supported for current implementation:

1. PART_TITLEBAR (UIElement) - For displaying window title, dragging and maximize / restore operations.
2. PART_MINIMIZE (Button) – Window minimize button
3. PART_MAXIMIZE_RESTORE (Button) – Maximize restore button
4. PART_CLOSE (Button) – Close button
5. PART_LEFT_BORDER (UIElement) – Left resizable border
6. PART_RIGHT_BORDER (UIElement) – Right resizable border
7. PART_TOP_BORDER (UIElement) – Top resizable border
8. PART_BOTTOM_BORDER (UIElement) – Bottom resizable border

One more thing to note is that while defining the window template, you must declare the ContentPresenter(which ultimately contains window content) within AdornerDecorator tag (which is the adorner layer for the window) as this is a WPF requirement.

Here is the template I have created.


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    x:Class="CustomWindow.Resources.Themes.Style">

    <Style TargetType="{x:Type ToolTip}">
        <Setter Property = "Background" Value="#fdfbb6"/>
        <Setter Property = "Foreground" Value="Black"/>
        <Setter Property = "FontSize" Value="12"/>
        <Setter Property = "FontWeight" Value="DemiBold"/>
    </Style>

    <Style TargetType="{x:Type StatusBar}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type StatusBar}">
                    <Border Name="Border" BorderBrush="#BFE3FE" BorderThickness="1" CornerRadius="0" Padding="0" Margin="0">
                        <Border.Background>
                            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                <GradientStop Color="#FFb7b7b7" Offset="1"/>
                                <GradientStop Color="#FFF1EDED" Offset="0"/>
                            </LinearGradientBrush>
                        </Border.Background>
                        <ItemsPresenter Margin="-3"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="WinButton" TargetType="{x:Type Button}">
        <Setter Property="Margin" Value="0,0,4,0" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="Height" Value="20" />
        <Setter Property="Width" Value="20" />
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
                <Setter Property="Cursor" Value="Hand" />
            </Trigger>
        </Style.Triggers>
    </Style>

    <Style x:Key="WinTitle" TargetType="{x:Type Label}">
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Foreground" Value="Black" />
    </Style>

    <LinearGradientBrush x:Key="WinBackgroundBrush" StartPoint="0,0" EndPoint="0,1">
        <LinearGradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Offset="0" Color="White" />
                <GradientStop Offset="1" Color="GhostWhite" />
            </GradientStopCollection>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="WinBackground" StartPoint="0,0" EndPoint="0,1">
        <LinearGradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Offset="0" Color="White" />
                <GradientStop Offset="1" Color="Silver" />
            </GradientStopCollection>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>

    <SolidColorBrush x:Key="WinBorderBrush" Color="Silver"/>

    <Style x:Key="WindowStyle" TargetType="{x:Type Window}">
        <Setter Property="FontSize" Value="12" />
        <Setter Property="Height" Value="25" />
        <Setter Property="Width" Value="100" />
        <Setter Property="ShowActivated" Value="True"/>
        <Setter Property="ResizeMode" Value="NoResize"/>
        <Setter Property="WindowState" Value="Normal"/>
        <Setter Property="ResizeMode" Value="NoResize" />
        <Setter Property="WindowStyle" Value="None" />
        <Setter Property="AllowsTransparency" Value="True" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Grid MouseLeftButtonDown="Window_MouseLeftButtonDown">
                        <Border BorderThickness="1" Background="{DynamicResource WinBackgroundBrush}" BorderBrush="{DynamicResource WinBorderBrush}">
                            <DockPanel LastChildFill="True">
                                <Border x:Name="PART_TITLEBAR" 
                                        Margin="0" 
                                        Height="30" 
                                        DockPanel.Dock="Top" 
                                        CornerRadius="10" 
                                        Background="Transparent">
                                    <DockPanel LastChildFill="False" Background="{DynamicResource WinBackground}" Margin="0">
                                        <Image Source="{TemplateBinding Icon}" Height="20" Width="20"/>
                                        <TextBlock Padding="8,0,0,4" 
                                                   Margin="0"
                                                   VerticalAlignment="Center" 
                                                   FontStretch="UltraExpanded" 
                                                   Foreground="Black" 
                                                   TextTrimming="CharacterEllipsis" 
                                                   TextWrapping="NoWrap" 
                                                   Text="{TemplateBinding Title}"
                                                   FontWeight="DemiBold"
                                                   FontSize="16"/>
                                        <Button x:Name="PART_CLOSE" 
                                                DockPanel.Dock="Right" 
                                                ToolTip="Close"
                                                Click="PART_CLOSE_Click"
                                                Style="{DynamicResource WinButton}">
                                            <Image Source="../Images/close.png"/>
                                        </Button>
                                        <Button x:Name="PART_MAXIMIZE_RESTORE" 
                                                DockPanel.Dock="Right"
                                                ToolTip="Maximize"
                                                Click="PART_MAXIMIZE_RESTORE_Click"
                                                Style="{DynamicResource WinButton}">
                                            <Image Source="../Images/max.ico"/>
                                        </Button>
                                        <Button x:Name="PART_MINIMIZE" 
                                                DockPanel.Dock="Right"
                                                ToolTip="Minimize"
                                                Click="PART_MINIMIZE_Click"
                                                Style="{DynamicResource WinButton}">
                                            <Image Source="../Images/min.png"/>
                                        </Button>
                                    </DockPanel>
                                </Border>

                                <AdornerDecorator DockPanel.Dock="Bottom">
                                    <ContentPresenter Content="{TemplateBinding Content}" Margin="{TemplateBinding Margin}" DataContext="{TemplateBinding DataContext}" ContextMenu="{TemplateBinding ContextMenu}"/>
                                </AdornerDecorator>

                            </DockPanel>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

The window created looks like the following:


Post a Comment