Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPFSpark: 4 of n: SparkWindow

0.00/5 (No votes)
19 Jan 2012 1  
A Metro style custom window.

Introduction

This is the fourth article in the WPFSpark series and it was long due! For the past few months, I was busy creating a few more controls for WPFSpark and I am glad to say that the WPFSpark project has finally hit v1.0. If you are reading this article first in the WPFSpark series, WPFSpark is an Open Source project in CodePlex containing a library of user controls providing rich user experience. Till now I have covered three controls in WPFSpark - SprocketControl, ToggleSwitch, and FluidWrapPanel.

I have added four more controls to WPFSpark - SparkWindow, FluidPivotPanel, FluidProgressBar, and FluidStatusBar. I will be describing them in detail, one by one, in this article and the forthcoming articles.

The previous articles in the WPFSpark series can be accessed here:

  1. WPFSpark: 1 of n: SprocketControl
  2. WPFSpark: 2 of n: ToggleSwitch
  3. WPFSpark: 3 of n: FluidWrapPanel

In this article, I describe in detail the fourth control in this library - the SparkWindow control. It derives from System.Windows.Window and provides a rich user experience.

Inspiration

I occasionally create small tools (using WPF) at work to enhance the productivity of my fellow team members (as well as myself). In most of these tools, I ensure that the window has no border and covers the entire desktop area excluding the taskbar. Also, the window doesn't have a separate and distinct title bar of its own. The title bar shares space with the client area. This is because I want the user's undivided attention while using the application. This led to the genesis of SparkWindow allowing me to reuse my code across several applications.

A few months back, when the Windows 8 Developer Preview got released, I was happy to find that Microsoft is following a similar approach for their Metro Apps.

I also learnt a lot from the book WPF Control Development Unleashed by Pavan Podila. It's a really great resource for people who want to delve deep into custom control development.

SparkWindow Demystified

WindowMode

Initially, I created the template for a full screen window. Then later I added the capability to have non-fullscreen fixed size windows.

WindowMode defines whether SparkWindow can exist as a full screen window (which I have termed as a Pane) or as a non-fullscreen fixed size window. It also defines which system buttons will be available in the right side of the titlebar area. The WindowMode enum defines five modes in which the SparkWindow can be:

  • Pane - Fullscreen window with Minimize and Close buttons.
  • PaneCanClose - Fullscreen window with Minimize and Close buttons.
  • CanClose - Non-fullscreen, fixed size window with Close button only.
  • CanMinimize - Non-fullscreen, fixed size window with Minimize and Close buttons.
  • CanMaximize - Non-fullscreen, fixed size window with Minimize, Maximize, and Close buttons.
public enum WindowMode
{
    Pane = 0,
    PaneCanClose = 1,
    CanClose = 2,
    CanMinimize = 3,
    CanMaximize = 4
}

The default WindowMode is WindowMode.Pane.

One more system button, About, can be added by setting the IsAboutEnabled property. The About button displays a Question Mark(?) symbol. I will speak more about it later.

The WPFSpark demo application uses a SparkWindow as its main window with the WindowFrameMode property set to CanMinimize.

SparkWindow

SparkWindow is a custom control which derives from Systems.Windows.Window. By default, the SparkWindow is a lookless control. Lookless controls provide a default template for the control and allow the user to replace the template with their own template without disturbing the control template. There are a few basic steps that must be followed to create a lookless control. Here is what I did to make SparkWindow lookless:

  • Add the SparkWindow class
  • First, I added a C# class called SparkWindow to the project. This would contain the control logic. I will be adding the logic later.

  • Add the Themes\Generic.xaml ResourceDictionary
  • Next, I added a folder called Themes to the project and within it, I added a new ResourceDictionary called Generic.xaml. In this resource dictionary, I added the default styles of SparkWindow and the various system buttons - About, Minimize, Maximize, and Close.

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:wpfspark="clr-namespace:WPFSpark">
        <!-- Minimize Button -->
        <ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly=wpfspark:SparkWindow, 
                                 ResourceId=SparkMinimizeButtonTemplate}"
                         TargetType="{x:Type Button}">
            <Viewbox HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch">
                <Canvas Height="16"
                        Width="16">
                    <Rectangle x:Name="BorderRect"
                               Width="15"
                               Height="15"
                               Stroke="Transparent"
                               Fill="Transparent"
                               StrokeThickness="0.5"></Rectangle>
                    <Line X1="3"
                          Y1="12"
                          X2="12"
                          Y2="12"
                          Stroke="White"
                          StrokeThickness="0.5"></Line>
                </Canvas>
            </Viewbox>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsMouseOver"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Stroke"
                            Value="Gray"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusX"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusY"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="Black"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                    <Setter TargetName="BorderRect"
                            Property="BitmapEffect">
                        <Setter.Value>
                            <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                   GlowSize="5"></OuterGlowBitmapEffect>
                        </Setter.Value>
                    </Setter>
                </Trigger>
                <Trigger Property="Button.IsPressed"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99333333"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    
        <Style x:Key="{ComponentResourceKey TypeInTargetAssembly=
                      wpfspark:SparkWindow, ResourceId=SparkMinimizeButton}"
               TargetType="{x:Type Button}">
            <Setter Property="OverridesDefaultStyle"
                    Value="True" />
            <Setter Property="ToolTip"
                    Value="Minimize"></Setter>
            <Setter Property="Template"
                    Value="{StaticResource {ComponentResourceKey TypeInTargetAssembly=
                            wpfspark:SparkWindow, ResourceId=SparkMinimizeButtonTemplate}}" />
        </Style>
    
        <!-- Maximize Button -->
        <ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly=
                                    wpfspark:SparkWindow, ResourceId=SparkMaximizeButtonTemplate}"
                  TargetType="{x:Type Button}">
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Canvas Height="16"
                        Width="16">
                    <Rectangle x:Name="BorderRect"
                               Width="15"
                               Height="15"
                               Stroke="Transparent"
                               Fill="Transparent"
                               StrokeThickness="0.5"></Rectangle>
                    <Rectangle Canvas.Left="2.5"
                               Canvas.Top="3"
                               Stroke="White"
                               StrokeThickness="0.5"
                               Fill="Transparent"
                               Width="10"
                               Height="10"></Rectangle>
    
                    <Rectangle Canvas.Left="2.5"
                               Canvas.Top="3"
                               Stroke="White"
                               StrokeThickness="0.5"
                               Fill="White"
                               Width="10"
                               Height="2"></Rectangle>
                </Canvas>
            </Viewbox>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsMouseOver"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Stroke"
                            Value="Gray"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusX"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusY"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="Black"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                    <Setter TargetName="BorderRect"
                            Property="BitmapEffect">
                        <Setter.Value>
                            <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                   GlowSize="5"></OuterGlowBitmapEffect>
                        </Setter.Value>
                    </Setter>
                </Trigger>
                <Trigger Property="Button.IsPressed"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99333333"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    
        <Style x:Key="{ComponentResourceKey TypeInTargetAssembly=
                           wpfspark:SparkWindow, ResourceId=SparkMaximizeButton}"
               TargetType="{x:Type Button}">
            <Setter Property="OverridesDefaultStyle"
                    Value="True" />
            <Setter Property="ToolTip"
                    Value="Maximize"></Setter>
            <Setter Property="Template"
                    Value="{StaticResource {ComponentResourceKey TypeInTargetAssembly=
                            wpfspark:SparkWindow, ResourceId=SparkMaximizeButtonTemplate}}" />
        </Style>
    
        <!-- Close Button -->
        <ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly=
                                      wpfspark:SparkWindow, ResourceId=SparkCloseButtonTemplate}"
                   TargetType="{x:Type Button}">
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Canvas Width="16"
                        Height="16">
                    <Rectangle x:Name="BorderRect"
                               Width="15"
                               Height="15"
                               Stroke="Transparent"
                               Fill="Transparent"
                               StrokeThickness="0.5">
                        <Rectangle.BitmapEffect>
                            <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                       GlowSize="0"></OuterGlowBitmapEffect>
                        </Rectangle.BitmapEffect>
                    </Rectangle>
                    <Line X1="3"
                          Y1="3"
                          X2="12"
                          Y2="12"
                          Stroke="White"
                          StrokeThickness="0.5"></Line>
                    <Line X1="12"
                          Y1="3"
                          X2="3"
                          Y2="12"
                          Stroke="White"
                          StrokeThickness="0.5"></Line>
                </Canvas>
            </Viewbox>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsMouseOver"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Stroke"
                            Value="Gray"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusX"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusY"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="Black"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                    <Setter TargetName="BorderRect"
                            Property="BitmapEffect">
                        <Setter.Value>
                            <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                   GlowSize="5"></OuterGlowBitmapEffect>
                        </Setter.Value>
                    </Setter>
                </Trigger>
                <Trigger Property="Button.IsPressed"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99333333"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    
        <Style x:Key="{ComponentResourceKey TypeInTargetAssembly=
                         wpfspark:SparkWindow, ResourceId=SparkCloseButton}"
               TargetType="{x:Type Button}">
            <Setter Property="OverridesDefaultStyle"
                    Value="True" />
            <Setter Property="ToolTip"
                    Value="Close"></Setter>
            <Setter Property="Template"
                    Value="{StaticResource {ComponentResourceKey TypeInTargetAssembly=
                            wpfspark:SparkWindow, ResourceId=SparkCloseButtonTemplate}}" />
        </Style>
    
        <!-- About Button -->
        <ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly=
                                    wpfspark:SparkWindow, ResourceId=SparkAboutButtonTemplate}"
                TargetType="{x:Type Button}">
            <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Canvas Width="16"
                        Height="16">
                    <Rectangle x:Name="BorderRect"
                               Width="15"
                               Height="15"
                               Stroke="Transparent"
                               Fill="Transparent"
                               Margin="0"
                               StrokeThickness="0.5">
                        <Rectangle.BitmapEffect>
                            <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                         GlowSize="0"></OuterGlowBitmapEffect>
                        </Rectangle.BitmapEffect>
                    </Rectangle>
                    <!-- The path geometry for the question mark symbol -->
                    <Path Stroke="LightGray"
                          StrokeThickness="0"
                          VerticalAlignment="Top"
                          HorizontalAlignment="Center"
                          Fill="LightGray"
                          Margin="4.5,-3,0,0"
                          StrokeLineJoin="Round"
                          Data="F1M2.81640625,14.1291275024414L2.87499237060547,14.1315307617188 
                                 2.9311215877533,14.1387405395508 2.98479437828064,14.1507568359375 
                                 3.03601050376892,14.1675796508789 3.08476996421814,14.189208984375 
                                 3.1310727596283,14.2156448364258 3.17491912841797,14.2468872070313 
                                 3.21630859375,14.2829360961914 3.25395965576172,14.3227233886719 
                                 3.28659057617188,14.3651809692383 3.31420135498047,14.4103088378906 
                                 3.3367919921875,14.4581069946289 3.35436248779297,14.5085754394531 
                                 3.36691284179688,14.5617141723633 3.37444305419922,14.6175231933594 
                                 3.376953125,14.6760025024414 3.37444305419922,14.7329864501953 
                                 3.36691284179688,14.787727355957 3.35436248779297,14.8402252197266 
                                 3.3367919921875,14.8904800415039 3.31420135498047,14.9384918212891 
                                 3.28659057617188,14.984260559082 3.25395965576172,15.0277862548828 
                                 3.21630859375,15.0690689086914 3.17491912841797,15.1067199707031 
                                 3.1310727596283,15.1393508911133 3.08476996421814,15.1669616699219 
                                 3.03601050376892,15.1895523071289 2.98479437828064,15.2071228027344 
                                 2.9311215877533,15.2196731567383 2.87499237060547,15.2272033691406 
                                 2.81640625,15.2297134399414 2.75952911376953,15.2272033691406 
                                 2.70510864257813,15.2196731567383 2.65314483642578,15.2071228027344 
                                 2.6036376953125,15.1895523071289 2.55658721923828,15.1669616699219 
                                 2.51199340820313,15.1393508911133 2.46985626220703,15.1067199707031 
                                 2.43017578125,15.0690689086914 2.39412689208984,15.0277862548828 
                                 2.36288452148438,14.984260559082 2.33644866943359,14.9384918212891 
                                 2.3148193359375,14.8904800415039 2.29799652099609,14.8402252197266 
                                 2.28598022460938,14.787727355957 2.27877044677734,14.7329864501953 
                                 2.2763671875,14.6760025024414 2.27877044677734,14.6175231933594 
                                 2.28598022460938,14.5617141723633 2.29799652099609,14.5085754394531 
                                 2.3148193359375,14.4581069946289 2.33644866943359,14.4103088378906 
                                 2.36288452148438,14.3651809692383 2.39412689208984,14.3227233886719 
                                 2.43017578125,14.2829360961914 2.46985626220703,14.2468872070313 
                                 2.51199340820313,14.2156448364258 2.55658721923828,14.189208984375 
                                 2.6036376953125,14.1675796508789 2.65314483642578,14.1507568359375 
                                 2.70510864257813,14.1387405395508 2.75952911376953,14.1315307617188 
                                 2.81640625,14.1291275024414z M3.0283203125,5.13986968994141L3.15366363525391,
                                 5.14211273193359 3.27676391601563,5.14884185791016 3.39762115478516,
                                 5.16005706787109 3.5162353515625,5.17575836181641 3.63260650634766,
                                 5.19594573974609 3.74673461914063,5.22061920166016 
                                 3.85861968994141,5.24977874755859 3.96826171875,5.28342437744141 
                                 4.07496643066406,5.32139587402344 4.17803955078125,5.36353302001953 
                                 4.27748107910156,5.40983581542969 4.373291015625,5.46030426025391 
                                 4.46546936035156,5.51493835449219 4.55401611328125,5.57373809814453 
                                 4.63893127441406,5.63670349121094 4.72021484375,5.70383453369141 
                                 4.79754638671875,5.77497100830078 4.87060546875,5.84995269775391 
                                 4.93939208984375,5.92877960205078 5.00390625,6.01145172119141 
                                 5.06414794921875,6.09796905517578 5.1201171875,6.18833160400391 
                                 5.17181396484375,6.28253936767578 5.21923828125,6.38059234619141 
                                 5.26169586181641,6.48233032226563 5.29849243164063,6.58759307861328 
                                 5.32962799072266,6.69638061523438 5.3551025390625,6.80869293212891 
                                 5.37491607666016,6.92453002929688 5.38906860351563,7.04389190673828 
                                 5.39756011962891,7.16677856445313 5.400390625,7.29319000244141 
                                 5.398681640625,7.38953399658203 5.3935546875,7.48374223709106 
                                 5.385009765625,7.57581377029419 5.373046875,7.66574907302856 
                                 5.357666015625,7.75354814529419 5.3388671875,7.83921051025391 
                                 5.316650390625,7.92273712158203 5.291015625,8.00412750244141 
                                 5.26265716552734,8.08370208740234 5.23226928710938,8.16178131103516 
                                 5.19985198974609,8.23836517333984 5.1654052734375,8.31345367431641 
                                 5.12892913818359,8.38704681396484 5.09042358398438,8.45914459228516 
                                 5.04988861083984,8.52974700927734 5.00732421875,8.59885406494141 
                                 4.96294403076172,8.66667938232422 4.91696166992188,8.73343658447266 
                                 4.86937713623047,8.79912567138672 4.8201904296875,8.86374664306641 
                                 4.76940155029297,8.92729949951172 4.71701049804688,8.98978424072266 
                                 4.66301727294922,9.05120086669922 4.607421875,9.11154937744141 
                                 4.49420166015625,9.23032379150391 4.380126953125,9.34738922119141 
                                 4.26519775390625,9.46274566650391 4.1494140625,9.57639312744141 
                                 4.06092071533203,9.66018676757813 3.97659301757813,9.74066925048828 
                                 3.89643096923828,9.81784057617188 3.8204345703125,9.89170074462891 
                                 3.74860382080078,9.96224975585938 3.68093872070313,10.0294876098633 
                                 3.61743927001953,10.0934143066406 3.55810546875,10.1540298461914 
                                 3.50240325927734,10.2122955322266 3.44979858398438,10.269172668457 
                                 3.40029144287109,10.3246612548828 3.3538818359375,10.3787612915039 
                                 3.31056976318359,10.4314727783203 3.27035522460938,10.482795715332 
                                 3.23323822021484,10.5327301025391 3.19921875,10.5812759399414 
                                 3.16797637939453,10.6289672851563 3.13919067382813,10.6763381958008 
                                 3.11286163330078,10.723388671875 3.0889892578125,10.7701187133789 
                                 3.06757354736328,10.8165283203125 3.04861450195313,10.8626174926758  
                                 3.03211212158203,10.9083862304688 3.01806640625,10.9538345336914 
                                 3.00605010986328,11.0001373291016 2.99563598632813,11.048469543457 
                                 2.98682403564453,11.0988311767578 2.9796142578125,11.1512222290039 
                                 2.97400665283203,11.2056427001953 2.97000122070313,11.262092590332 
                                 2.96759796142578,11.3205718994141 2.966796875,11.3810806274414 
                                 2.9676513671875,11.4587860107422 2.97021484375,11.5346755981445 
                                 2.9744873046875,11.6087493896484 2.98046875,11.6810073852539 
                                 2.9881591796875,11.7514495849609 2.99755859375,11.8200759887695 
                                 3.0086669921875,11.8868865966797 3.021484375,11.9518814086914 
                                 3.03515625,12.0147399902344 3.04882788658142,12.0751419067383 
                                 3.06249976158142,12.1330871582031 3.07617163658142,12.1885757446289 
                                 3.08984351158142,12.2416076660156 3.103515625,12.2921829223633 
                                 3.1171875,12.3403015136719 3.130859375,12.3859634399414 
                                 2.5771484375,12.3859634399414 2.56032562255859,12.3415832519531 
                                 2.54403686523438,12.2938919067383 2.52828216552734,12.2428894042969 
                                 2.5130615234375,12.1885757446289 2.49837493896484,12.1309509277344 
                                 2.48422241210938,12.0700149536133 2.47060394287109,12.0057678222656 
                                 2.45751953125,11.9382095336914 2.44550323486328,11.8690490722656 
                                 2.43508911132813,11.7999954223633 2.42627716064453,11.7310485839844 
                                 2.4190673828125,11.6622085571289 2.41345977783203,11.5934753417969 
                                 2.40945434570313,11.5248489379883 2.40705108642578,11.4563293457031 
                                 2.40625,11.3879165649414 2.40758514404297,11.3085021972656 
                                 2.41159057617188,11.2309036254883 2.41826629638672,11.1551208496094 
                                 2.4276123046875,11.0811538696289 2.43962860107422,11.0090026855469 
                                 2.45431518554688,10.9386672973633 2.47167205810547,10.8701477050781 
                                 2.49169921875,10.8034439086914 2.51412963867188,10.7381286621094 
                                 2.5386962890625,10.6737747192383 2.56539916992188,10.6103820800781 
                                 2.59423828125,10.5479507446289 2.62521362304688,10.4864807128906 
                                 2.6583251953125,10.4259719848633 2.69357299804688,10.3664245605469 
                                 2.73095703125,10.3078384399414 2.77037048339844,10.2499465942383 
                                 2.81170654296875,10.1924819946289 2.85496520996094,10.1354446411133 
                                 2.900146484375,10.0788345336914 2.94725036621094,10.0226516723633 
                                 2.99627685546875,9.96689605712891 3.04722595214844,9.91156768798828 
                                 3.10009765625,9.85666656494141 3.21054077148438,9.74686431884766 
                                 3.3265380859375,9.63620758056641 3.44808959960938,9.52469635009766 
                                 3.5751953125,9.41233062744141 3.68414282798767,9.31214141845703 
                                 3.79223608970642,9.20981597900391 3.89947485923767,9.10535430908203 
                                 4.005859375,8.99875640869141 4.10882568359375,8.88916778564453 
                                 4.205810546875,8.77573394775391 4.29681396484375,8.65845489501953 
                                 4.3818359375,8.53733062744141 4.42183685302734,8.47511291503906 
                                 4.45980834960938,8.41150665283203 4.49575042724609,8.34651184082031 
                                 4.5296630859375,8.28012847900391 4.56154632568359,8.21235656738281 
                                 4.59140014648438,8.14319610595703 4.61922454833984,8.07264709472656 
                                 4.64501953125,8.00070953369141 4.66825103759766,7.92716979980469 
                                 4.68838500976563,7.85181427001953 4.70542144775391,7.77464294433594 
                                 4.7193603515625,7.69565582275391 4.73020172119141,7.61485290527344 
                                 4.73794555664063,7.53223419189453 4.74259185791016,7.44779968261719 
                                 4.744140625,7.36154937744141 4.74216461181641,7.26878356933594 
                                 4.73623657226563,7.17847490310669 4.72635650634766,7.09062242507935 
                                 4.7125244140625,7.00522613525391 4.69474029541016,6.92228698730469 
                                 4.67300415039063,6.84180450439453 4.64731597900391,6.76377868652344 
                                 4.61767578125,6.68820953369141 4.58451080322266,6.61525726318359 
                                 4.54824829101563,6.54508209228516 4.50888824462891,6.47768402099609 
                                 4.4664306640625,6.41306304931641 4.42087554931641,6.35121965408325 
                                 4.37222290039063,6.29215288162231 4.32047271728516,6.23586273193359 
                                 4.265625,6.18235015869141 4.20778656005859,6.13177490234375 
                                 4.14706420898438,6.08429718017578 4.08345794677734,6.0399169921875 
                                 4.0169677734375,5.99863433837891 3.94759368896484,5.96044921875 
                                 3.87533569335938,5.92536163330078 3.80019378662109,5.89337158203125 
                                 3.72216796875,5.86447906494141 3.64168548583984,5.83884429931641 
                                 3.5591733455658,5.81662797927856 3.47463202476501,5.79782915115356 
                                 3.38806128501892,5.78244781494141 3.29946112632751,5.77048492431641 
                                 3.2088315486908,5.76194000244141 3.11617279052734,5.75681304931641 
                                 3.021484375,5.75510406494141 2.94154930114746,5.75598526000977 
                                 2.86238861083984,5.75862884521484 2.78400230407715,5.76303482055664 
                                 2.70639038085938,5.76920318603516 2.62955284118652,5.77713394165039 
                                 2.55348968505859,5.78682708740234 2.47820091247559,5.79828262329102 
                                 2.4036865234375,5.81150054931641 2.32994651794434,5.82648086547852 
                                 2.25698089599609,5.84322357177734 2.18478965759277,5.86172866821289 
                                 2.11337280273438,5.88199615478516 2.0427303314209,5.90402603149414 
                                 1.97286224365234,5.92781829833984 1.90376853942871,5.95337295532227 
                                 1.83544921875,5.98069000244141 1.70059967041016,6.03964996337891 
                                 1.56777954101563,6.10373687744141 1.43698883056641,6.17295074462891 
                                 1.3082275390625,6.24729156494141 1.18149566650391,6.32675933837891 
                                 1.05679321289063,6.41135406494141 0.934120178222656,6.50107574462891 
                                 0.813476622104645,6.59592437744141 0.813476622104645,5.86447906494141
                                 0.935615539550781,5.77518463134766 1.05935668945313,5.69187164306641 
                                 1.18470001220703,5.61454010009766 1.3116455078125,5.54319000244141 
                                 1.44019317626953,5.47782182693481 1.57034301757813,5.41843461990356 
                                 1.70209503173828,5.36502838134766 1.83544921875,5.31760406494141 
                                 1.97147369384766,5.27594757080078 2.11123657226563,5.23984527587891 
                                 2.25473785400391,5.20929718017578 2.4019775390625,5.18430328369141 
                                 2.55295562744141,5.16486358642578 2.70767211914063,5.15097808837891 
                                 2.86612701416016,5.14264678955078 3.0283203125,5.13986968994141z">
                    </Path>
                </Canvas>
            </Viewbox>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsMouseOver"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Stroke"
                            Value="Gray"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusX"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="RadiusY"
                            Value="0"></Setter>
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="Black"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                    <Setter TargetName="BorderRect"
                            Property="BitmapEffect">
                        <Setter.Value>
                            <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                   GlowSize="5"></OuterGlowBitmapEffect>
                        </Setter.Value>
                    </Setter>
                </Trigger>
                <Trigger Property="Button.IsPressed"
                         Value="true">
                    <Setter TargetName="BorderRect"
                            Property="Fill">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0"
                                                 EndPoint="0,1">
                                <GradientStop Color="#99333333"
                                              Offset="0"></GradientStop>
                                <GradientStop Color="#99FFFFFF"
                                              Offset="0.7"></GradientStop>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    
        <Style x:Key="{ComponentResourceKey TypeInTargetAssembly=
                          wpfspark:SparkWindow, ResourceId=SparkAboutButton}"
               TargetType="{x:Type Button}">
            <Setter Property="OverridesDefaultStyle"
                    Value="True" />
            <Setter Property="ToolTip"
                    Value="About"></Setter>
            <Setter Property="Template"
                    Value="{StaticResource {ComponentResourceKey 
                            TypeInTargetAssembly=wpfspark:SparkWindow, 
                            ResourceId=SparkAboutButtonTemplate}}" />
        </Style>
    
        <!-- Window -->
        <ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly=
                              wpfspark:SparkWindow, ResourceId=SparkWindowTemplate}"
                         TargetType="wpfspark:SparkWindow">
            <Border Name="OuterFrame"
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    BorderBrush="{Binding Path=OuterBorderBrush, RelativeSource=
                      {RelativeSource FindAncestor,AncestorType={x:Type wpfspark:SparkWindow}}}"
                    BorderThickness="{Binding Path=OuterBorderThickness, 
                      RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type wpfspark:SparkWindow}}}"
                    CornerRadius="{Binding Path=OuterBorderCornerRadius, 
                      RelativeSource={RelativeSource FindAncestor,
                      AncestorType={x:Type wpfspark:SparkWindow}}}"
                    SnapsToDevicePixels="True"
                    Background="Transparent">
                <Border Name="windowFrame"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        BorderBrush="{Binding Path=InnerBorderBrush, 
                          RelativeSource={RelativeSource FindAncestor,
                          AncestorType={x:Type wpfspark:SparkWindow}}}"
                        BorderThickness="{Binding Path=InnerBorderThickness, 
                          RelativeSource={RelativeSource FindAncestor,
                          AncestorType={x:Type wpfspark:SparkWindow}}}"
                        CornerRadius="{Binding Path=InnerBorderCornerRadius, 
                          RelativeSource={RelativeSource FindAncestor,
                          AncestorType={x:Type wpfspark:SparkWindow}}}"
                        SnapsToDevicePixels="True"
                        Background="{TemplateBinding Background}">
                    <Grid ShowGridLines="False">
                        <!--<Grid.ColumnDefinitions>
                            <ColumnDefinition Width="20"></ColumnDefinition>
                            <ColumnDefinition Width="*"></ColumnDefinition>
                        </Grid.ColumnDefinitions>-->
    
                        <Grid.RowDefinitions>
                            <RowDefinition Height="2"></RowDefinition>
                            <RowDefinition Height="40"></RowDefinition>
                            <RowDefinition Height="2"></RowDefinition>
                            <RowDefinition Height="*"></RowDefinition>
                        </Grid.RowDefinitions>
    
                        <Grid Grid.Row="1"
                              Grid.RowSpan="3"
                              Grid.Column="1"
                              Margin="0,0,0,0">
                            <TextBlock  Name="PART_TitleText"
                                        FontFamily="{TemplateBinding FontFamily}"
                                        FontWeight="{TemplateBinding FontWeight}"
                                        FontSize="{TemplateBinding FontSize}"
                                        Foreground="{TemplateBinding Foreground}"
                                        FontStyle="{TemplateBinding FontStyle}"
                                        VerticalAlignment="Top"
                                        Margin="{Binding Path=TitleMargin, RelativeSource=
                                          {RelativeSource FindAncestor,AncestorType=
                                          {x:Type wpfspark:SparkWindow}}}"
                                        Effect="{Binding Path=TitleEffect, 
                                          RelativeSource={RelativeSource FindAncestor,
                                          AncestorType={x:Type wpfspark:SparkWindow}}}"
                                        Text="{Binding Path=Title, RelativeSource=
                                                {RelativeSource FindAncestor,AncestorType=
                                                {x:Type wpfspark:SparkWindow}}, 
                                                NotifyOnTargetUpdated=True}">
                                            <TextBlock.Triggers>
                                                <EventTrigger RoutedEvent="Binding.TargetUpdated">
                                                    <BeginStoryboard>
                                                        <Storyboard>
                                                            <ThicknessAnimation Duration="0:0:0.3"
                                                                        Storyboard.TargetProperty="Margin"
                                                                        From="400,-30,0,0"
                                                                        To="0,-30,0,0">
                                                                <ThicknessAnimation.EasingFunction>
                                                                    <ExponentialEase EasingMode="EaseOut"
                                                                           Exponent="2"></ExponentialEase>
                                                                </ThicknessAnimation.EasingFunction>
                                                            </ThicknessAnimation>
                                                        </Storyboard>
                                                    </BeginStoryboard>
                                                </EventTrigger>
                                            </TextBlock.Triggers>
                            </TextBlock>
                        </Grid>
    
                        <Border Name="PART_TitleBar"
                                Grid.Row="0"
                                Grid.Column="0"
                                Grid.RowSpan="3"
                                Grid.ColumnSpan="2"
                                Background="Black"
                                Opacity="0" />
    
                        <Grid  Grid.Row="1">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"></ColumnDefinition>
                                <ColumnDefinition Width="40"></ColumnDefinition>
                                <ColumnDefinition Width="40"></ColumnDefinition>
                                <ColumnDefinition Width="40"></ColumnDefinition>
                                <ColumnDefinition Width="40"></ColumnDefinition>
                                <ColumnDefinition Width="5" />
                            </Grid.ColumnDefinitions>
    
                            <Grid Grid.Row="1"
                                  Grid.RowSpan="4"
                                  Grid.Column="1"
                                  Margin="0,0,0,0">
                            </Grid>
                            <Button Name="PART_About"
                                    Margin="0,5,0,0"
                                    Grid.Column="1"
                                    Style="{StaticResource {ComponentResourceKey 
                                      TypeInTargetAssembly=wpfspark:SparkWindow, 
                                      ResourceId=SparkAboutButton}}">
                            </Button>
                            <Button Name="PART_Minimize"
                                    Margin="0,5,0,0"
                                    Grid.Column="2"
                                    Style="{StaticResource {ComponentResourceKey 
                                      TypeInTargetAssembly=wpfspark:SparkWindow, 
                                      ResourceId=SparkMinimizeButton}}">
                            </Button>
                            <Button Name="PART_Maximize"
                                    Margin="0,5,0,0"
                                    Grid.Column="3"
                                    Style="{StaticResource {ComponentResourceKey 
                                      TypeInTargetAssembly=wpfspark:SparkWindow, 
                                      ResourceId=SparkMaximizeButton}}">
                            </Button>
                            <Button Name="PART_Close"
                                    Margin="0,5,0,0"
                                    Grid.Column="4"
                                    Style="{StaticResource {ComponentResourceKey 
                                      TypeInTargetAssembly=wpfspark:SparkWindow, 
                                      ResourceId=SparkCloseButton}}"></Button>
                        </Grid>
    
                        <!-- Content -->
                        <AdornerDecorator Grid.Row="3"
                                          Grid.Column="0">
                            <ContentPresenter />
                        </AdornerDecorator>
                    </Grid>
                </Border>
            </Border>
        </ControlTemplate>
    
        <Style TargetType="{x:Type wpfspark:SparkWindow}">
            <Setter Property="AllowsTransparency"
                    Value="False"></Setter>
            <Setter Property="ResizeMode"
                    Value="NoResize"></Setter>
            <Setter Property="MinHeight"
                    Value="100"></Setter>
            <Setter Property="MinWidth"
                    Value="200"></Setter>
            <Setter Property="MaxWidth"
                    Value="{DynamicResource {x:Static 
                      SystemParameters.MaximizedPrimaryScreenWidthKey}}"></Setter>
            <Setter Property="MaxHeight"
                    Value="{DynamicResource {x:Static 
                      SystemParameters.MaximizedPrimaryScreenHeightKey}}"></Setter>
            <Setter Property="WindowStyle"
                    Value="None"></Setter>
            <Setter Property="Template"
                    Value="{StaticResource {ComponentResourceKey 
                      TypeInTargetAssembly=wpfspark:SparkWindow, 
                      ResourceId=SparkWindowTemplate}}" />
        </Style>
    </ResourceDictionary>

    In the style, I have set AllowsTransparency as False in order to provide better performance. For more details on the impact of the AllowsTransparency property on the performance of a window, check out this blog by Jeremy Alles.The MaxWidth and MaxHeight properties are bound to SystemParameters.MaximizedPrimaryScreenWidthKey and SystemParameters.MaximizedPrimaryScreenHeightKey, respectively so that when they are in maximized state, they do not overlap the taskbar.

    If you notice, while setting the key (x:Key) for resources, instead of providing a simple string, I have used the ComponentResourceKey to provide unique key names to the resources. This helps in avoiding name collisions. A ComponentResourceKey has two properties -TypeInTargetAssembly (of type System.Type) and ResourceId (of type System.String).

    The SparkWindow is surrounded by two borders - the OuterBorder and the InnerBorder. The window content lies within the InnerBorder. The color, corner radius and thickness of these borders can be set using the properties of the SparkWindow (see SparkWindow Properties table below).

    Whenever the Title property of the SparkWindow is set, it animates the title. This is achieved by listening to the Binding.TargetUpdated RoutedEvent.

    <TextBlock  Name="PART_TitleText"
        FontFamily="{TemplateBinding FontFamily}"
        FontWeight="{TemplateBinding FontWeight}"
        FontSize="{TemplateBinding FontSize}"
        Foreground="{TemplateBinding Foreground}"
        FontStyle="{TemplateBinding FontStyle}"
        VerticalAlignment="Top"
        Margin="{Binding Path=TitleMargin, RelativeSource={RelativeSource 
                 FindAncestor,AncestorType={x:Type wpfspark:SparkWindow}}}"
        Effect="{Binding Path=TitleEffect, RelativeSource={RelativeSource 
                 FindAncestor,AncestorType={x:Type wpfspark:SparkWindow}}}"
        Text="{Binding Path=Title, RelativeSource={RelativeSource 
                 FindAncestor,AncestorType={x:Type wpfspark:SparkWindow}}, 
                 NotifyOnTargetUpdated=True}">
            <TextBlock.Triggers>
                <EventTrigger RoutedEvent="Binding.TargetUpdated">
                    <BeginStoryboard>
                        <Storyboard>
                            <ThicknessAnimation Duration="0:0:0.3"
                                                Storyboard.TargetProperty="Margin"
                                                From="200,-30,0,0"
                                                To="0,-30,0,0">
                                <ThicknessAnimation.EasingFunction>
                                    <ExponentialEase EasingMode="EaseOut"
                                                     Exponent="2"></ExponentialEase>
                                </ThicknessAnimation.EasingFunction>
                            </ThicknessAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </TextBlock.Triggers>
    </TextBlock>
  • Override metadata
  • Next, to tell WPF that the default style has to be picked up from the above resource dictionary, I had to override the metadata for DefaultStyleKeyProperty in the static constructor for the SparkWindow class.

    /// <summary>
    /// Static ctor
    /// </summary>
    static SparkWindow()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(SparkWindow), 
                 new FrameworkPropertyMetadata(typeof(SparkWindow)));
    }
  • Set the ThemeInfo attribute
  • To ensure that the default style for SparkWindow is picked up from the containing assembly (WPFSpark.dll) itself, I modified the ThemeInfo attribute defined in AssemblyInfo.cs.

    [assembly: ThemeInfo(
        ResourceDictionaryLocation.None,
        ResourceDictionaryLocation.SourceAssembly
    )]

    The first parameter tells where the theme specific resource dictionaries are located and the second parameter tells where the generic resource dictionary is located. Both parameters are of type ResourceDictionaryLocation. Setting the second parameter to ResourceDictionaryLocation.SourceAssembly tells WPF to look for the generic resource dictionary within WPFSpark.dll.

  • Defining the Template Parts
  • The control logic for SparkWindow (defined in SparkWindow.cs) requires access to some of the elements defined in the default style. Therefore, as per the convention, these controls are given descriptive names starting with "PART_" in the default template. Then add TemplatePartAttribute to the SparkWindow class.

    [TemplatePart(Name = "PART_About", Type = typeof(Button))]
        [TemplatePart(Name = "PART_Minimize", Type = typeof(Button))]
        [TemplatePart(Name = "PART_Close", Type = typeof(Button))]
        public class SparkWindow : Window
        {
         ...

    Then, override the OnApplyTemplate() method to find the required control using the GetTemplateChild() method.

Here is the control logic defined in SparkWindow.cs:

namespace WPFSpark
{
    /// <summary>
    /// Class which provides the implementation of a custom window
    /// </summary>
    [TemplatePart(Name = "PART_About", Type = typeof(Button))]
    [TemplatePart(Name = "PART_Minimize", Type = typeof(Button))]
    [TemplatePart(Name = "PART_Close", Type = typeof(Button))]
    public class SparkWindow : Window
    {
        #region Fields

        Button aboutButton = null;
        Button minimizeButton = null;
        Button maximizeButton = null;
        Button closeButton = null;
        Border titleBar = null;

        #endregion

        #region Dependency Properties

        ...

        #endregion

        #region Construction / Initialization

        /// <summary>
        /// Static ctor
        /// </summary>
        static SparkWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SparkWindow), 
                       new FrameworkPropertyMetadata(typeof(SparkWindow)));
        }

        /// <summary>
        /// Ctor
        /// </summary>
        public SparkWindow()
        {
            // Default WindowFrameMode is Pane
            this.WindowFrameMode = WindowMode.Pane;
        }

        #endregion

        #region Overrides

        /// <summary>
        /// Override which is called when the template is applied
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Detach previously attached event handlers, if any
            Unsubscribe();

            // Get all the controls in the template
            GetTemplateParts();
        }

        /// <summary>
        /// Handles the closing event
        /// </summary>
        /// <param name="e">CancelEventArgs</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            // Unsubscribe to events
            if (aboutButton != null)
                aboutButton.Click -= OnAbout;

            if (minimizeButton != null)
                minimizeButton.Click -= OnMinimize;

            if (maximizeButton != null)
                maximizeButton.Click -= OnMaximize;

            if (closeButton != null)
                closeButton.Click -= OnClose;

            if (titleBar != null)
                titleBar.MouseLeftButtonDown -= OnTitleBarMouseDown;

            base.OnClosing(e);
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Detach previously attached event handlers, if any
        /// </summary>
        private void Unsubscribe()
        {
            // PART_About
            if (aboutButton != null)
            {
                aboutButton.Click -= OnAbout;
            }

            // PART_Minimize
            if (minimizeButton != null)
            {
                minimizeButton.Click -= OnMinimize;
            }

            // PART_Maximize
            if (maximizeButton != null)
            {
                maximizeButton.Click -= OnMaximize;
            }

            // PART_Close
            if (closeButton != null)
            {
                closeButton.Click -= OnClose;
            }

            // PART_TitleBar
            if (titleBar != null)
            {
                titleBar.MouseLeftButtonDown -= OnTitleBarMouseDown;
            }
        }

        /// <summary>
        /// Gets the required controls in the template
        /// </summary>
        protected void GetTemplateParts()
        {
            // PART_About
            aboutButton = GetChildControl<Button>("PART_About");
            if (aboutButton != null)
            {
                aboutButton.Click += new RoutedEventHandler(OnAbout);
            }

            // PART_Minimize
            minimizeButton = GetChildControl<Button>("PART_Minimize");
            if (minimizeButton != null)
            {
                minimizeButton.Click += new RoutedEventHandler(OnMinimize);
            }

            // PART_Maximize
            maximizeButton = GetChildControl<Button>("PART_Maximize");
            if (maximizeButton != null)
            {
                maximizeButton.Click += new RoutedEventHandler(OnMaximize);
            }

            // PART_Close
            closeButton = GetChildControl<Button>("PART_Close");
            if (closeButton != null)
            {
                closeButton.Click += new RoutedEventHandler(OnClose);
            }

            // PART_TitleBar
            titleBar = GetChildControl<Border>("PART_TitleBar");
            if (titleBar != null)
            {
                titleBar.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseDown);
            }

            // PART_TitleText
            TextBlock tb = this.GetChildControl<TextBlock>("PART_TitleText");
            if (tb == null)
                return;

            // Update the margin for the TitleText trigger
            UpdateTriggerMargin(tb, TitleMargin);
            // Update the system control buttons in the window frame
            UpdateWindowFrame(WindowFrameMode);
            // Update the location of the About button
            UpdateAboutButton(IsAboutEnabled);
        }

        /// <summary>
        /// Update the system control buttons in the window frame
        /// </summary>
        /// <param name="winMode">Window mode</param>
        private void UpdateWindowFrame(WindowMode winMode)
        {
            switch (winMode)
            {
                // Only close button should be visible if the mode is CanClose/PaneCanClose
                case WindowMode.CanClose:
                case WindowMode.PaneCanClose:
                    if (minimizeButton != null)
                        minimizeButton.Visibility = Visibility.Collapsed;
                    if (maximizeButton != null)
                        maximizeButton.Visibility = Visibility.Collapsed;
                    break;

                // Only minimize and close buttons should be visible if the mode is Pane/CanMinimize
                case WindowMode.Pane:
                case WindowMode.CanMinimize:
                default:
                    if (minimizeButton != null)
                    {
                        minimizeButton.Visibility = Visibility.Visible;
                        Grid.SetColumn(minimizeButton, 3);
                    }
                    if (maximizeButton != null)
                        maximizeButton.Visibility = Visibility.Collapsed;
                    break;

                // All buttons - minimize, maximize and close will be visible if the mode is CanMaximize
                case WindowMode.CanMaximize:
                    if (minimizeButton != null)
                    {
                        minimizeButton.Visibility = Visibility.Visible;
                        Grid.SetColumn(minimizeButton, 2);
                    }
                    if (maximizeButton != null)
                    {
                        maximizeButton.Visibility = Visibility.Visible;
                    }
                    break;
            }

            // If the mode is Pane/PaneCanClose then the window should be in maximized state
            if ((WindowFrameMode == WindowMode.Pane) || (WindowFrameMode == WindowMode.PaneCanClose))
            {
                this.WindowState = WindowState.Maximized;
            }
        }

        /// <summary>
        /// Updates the location and visibility of the About button
        /// </summary>
        /// <param name="isEnabled"></param>
        private void UpdateAboutButton(bool isEnabled)
        {
            if (aboutButton == null)
                return;

            if (IsAboutEnabled)
            {
                // Show the About button
                aboutButton.Visibility = Visibility.Visible;

                // Set the location of the about button based 
                // on the visibility of the minimize and maximize buttons
                switch (WindowFrameMode)
                {
                    case WindowMode.CanClose:
                    case WindowMode.PaneCanClose:
                        Grid.SetColumn(aboutButton, 3);
                        break;
                    case WindowMode.Pane:
                    case WindowMode.CanMinimize:
                        Grid.SetColumn(aboutButton, 2);
                        break;
                    case WindowMode.CanMaximize:
                    default:
                        Grid.SetColumn(aboutButton, 1);
                        break;
                }
            }
            else
            {
                // Hide the About button
                aboutButton.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// Generic method to get a control from the template
        /// </summary>
        /// <typeparam name="T">Type of the control</typeparam>
        /// <param name="ctrlName">Name of the control in the template</param>
        /// <returns>Control</returns>
        protected T GetChildControl<T>(string ctrlName) where T : DependencyObject
        {
            T ctrl = GetTemplateChild(ctrlName) as T;
            return ctrl;
        }

        /// <summary>
        /// Updates the margin used for animating the Window Title when the window loads.
        /// </summary>
        /// <param name="tb">TextBlock which displays the window title</param>
        /// <param name="margin">New Margin</param>
        private void UpdateTriggerMargin(TextBlock tb, Thickness margin)
        {
            if ((tb.Triggers == null) || (tb.Triggers.Count == 0))
                return;

            foreach (EventTrigger trigger in tb.Triggers)
            {
                if (trigger.RoutedEvent.Name == "TargetUpdated")
                {
                    if ((trigger.Actions != null) && (trigger.Actions.Count > 0))
                    {
                        BeginStoryboard bsb = trigger.Actions[0] as BeginStoryboard;
                        if (bsb != null)
                        {
                            foreach (Timeline timeLine in bsb.Storyboard.Children)
                            {
                                ThicknessAnimation anim = timeLine as ThicknessAnimation;
                                if (anim != null)
                                {
                                    Thickness startThickness = 
                                      new Thickness(margin.Left + 200, margin.Top, 
                                      margin.Right, margin.Bottom);
                                    anim.SetValue(ThicknessAnimation.FromProperty, startThickness);
                                    anim.SetValue(ThicknessAnimation.ToProperty, margin);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Toggles the state of the window to maximized if the state is normal else vice-versa
        /// </summary>
        private void ToggleMaximize()
        {
            WindowState = (WindowState == WindowState.Maximized) ? 
                                 WindowState.Normal : WindowState.Maximized;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the MouseDown event on the title bar.
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">MouseButtonEventArgs</param>
        void OnTitleBarMouseDown(object sender, MouseButtonEventArgs e)
        {
            // If the user has clicked on the title bar twice then toggle the 
            // state of the window (if window is maximizable)
            if (WindowFrameMode == WindowMode.CanMaximize && e.ClickCount == 2)
            {
                ToggleMaximize();
                return;
            }

            // Allow the user to drag the window to a new location
            this.DragMove();
        }

        /// <summary>
        /// Overridable event handler for the event raised when About button is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">RoutedEventArgs</param>
        protected virtual void OnAbout(object sender, RoutedEventArgs e)
        {
            // Do nothing here
            // Derived classes should override this method and handle it themselves.
        }

        /// <summary>
        /// Overridable event handler for the event raised when Minimize button is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">RoutedEventArgs</param>
        protected virtual void OnMinimize(object sender, RoutedEventArgs e)
        {
            WindowState = WindowState.Minimized;
        }

        /// <summary>
        /// Overridable event handler for the event raised when Maximize button is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">RoutedEventArgs</param>
        protected virtual void OnMaximize(object sender, RoutedEventArgs e)
        {
            ToggleMaximize();
        }

        /// <summary>
        /// Overridable event handler for the event raised when Close button is clicked
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">RoutedEventArgs</param>
        protected virtual void OnClose(object sender, RoutedEventArgs e)
        {
            Close();
        }

        #endregion
    }
}

SparkWindow properties

Dependency PropertyTypeDescriptionDefault Value
InnerBorderBrush Brush Gets or sets the Brush for the InnerBorder of the SparkWindow. Brushes.White
InnerBorderCornerRadius CornerRadius Gets or sets the CornerRadius of the InnerBorder of the SparkWindow. 0,0,0,0
InnerBorderThickness Thickness Gets or sets the thickness of the InnerBorder of the SparkWindow. 0,0,0,0
IsAboutEnabled Boolean Gets or sets whether the About button should be displayed in the titlebar. False
OuterBorderBrush Brush Gets or sets the Brush for the OuterBorder of the SparkWindow. Brushes.Black
OuterBorderCornerRadius CornerRadius Gets or sets the CornerRadius of the InnerBorder of the SparkWindow. 0,0,0,0
OuterBorderThickness Thickness Gets or sets the thickness of the OuterBorder of the SparkWindow. 0,0,0,0
TitleMargin Margin Gets or sets the Margin for the Title of the SparkWindow. 0,0,0,0
TitleEffect Effect Gets or sets the various effects (DropShadowEffect, BlurEffect, etc.) for the Title of the SparkWindow. null
WindowFrameMode WPFSpark.WindowMode Gets or sets the WindowMode of the SparkWindow. WPFSpark.WindowMode.Pane

Using SparkWindow in your application

In order to use SparkWindow in your application, you must follow these steps:

  1. Add a reference to WPFSpark.dll to your application project.
  2. Right-click on your project and select Add > Window. Give it a name.
  3. In the XAML and the code-behind for the newly added window, change the base class to SparkWindow.
  4. <wpfspark:SparkWindow x:Class="WpfApplication2.CustomWindow"
                          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                          xmlns:wpfspark="clr-namespace:WPFSpark;assembly=WPFSpark"
                          Title="CustomWindow"
                          Height="300"
                          Width="300"
                          WindowFrameMode="Pane"
                          InnerBorderBrush="White"
                          InnerBorderCornerRadius="2"
                          InnerBorderThickness="1"
                          OuterBorderBrush="Black"
                          OuterBorderCornerRadius="2"
                          OuterBorderThickness="1"
                          IsAboutEnabled="True">                   
        <Grid>
    
        </Grid>
    </wpfspark:SparkWindow>
    using WPFSpark;
    
    namespace WpfApplication2
    {
        /// <summary>
        /// Interaction logic for CustomWindow.xaml
        /// </summary>
        public partial class CustomWindow : SparkWindow
        {
            public CustomWindow()
            {
                InitializeComponent();
            }
        }
    }
  5. If you have enabled the About button, then you can handle its click by overriding the OnAbout method. Similarly, you can override the OnMinimize, OnMaximize, OnClosing methods to handle the minimize, maximize, and close operations.
  6. protected override void OnAbout(object sender, RoutedEventArgs e)
    {
        base.OnAbout(sender, e);
    
        // Add your handler here
    }
    
    protected override void OnMinimize(object sender, RoutedEventArgs e)
    {
        base.OnMinimize(sender, e);
    
        // Add your handler here
    }
    
    protected override void OnMaximize(object sender, RoutedEventArgs e)
    {
        base.OnMaximize(sender, e);
    
        // Add your handler here
    }
    
    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
        base.OnClosing(e);
    
        // Add your handler here
    }

Providing your own Style/Template for SparkWindow

It is possible to override the Style or Template of the system buttons of SparkWindow (or of SparkWindow itself). If you are providing a new template for the buttons, you also need to provide a new template for the SparkWindow, in which you will be referring to the new styles for the system buttons. When you override the template of SparkWindow, you must ensure that the buttons have the same name as defined in the original template of SparkWindow (i.e., PART_About, PART_Minimize, PART_Maximize, PART_Close), otherwise SparkWindow will not be able to link the Click event of these buttons to their respective functionality. Here is an example, where I have overridden the button templates to display glyphs from the fonts Wingdings and Wingdings 2 as their content:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:wpfspark="clr-namespace:WPFSpark;assembly=WPFSpark">

    <Style x:Key="CustomMinimizeButtonStyle" TargetType="{x:Type Button}"> 
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                        <Canvas Height="16" Width="16">
                            <Rectangle x:Name="BorderRect"
                                       Width="15" 
                                       Height="15" 
                                       Stroke="Transparent" 
                                       Fill="Transparent"
                                       StrokeThickness="0.5"></Rectangle>
                            <TextBlock x:Name="MinText" 
                                       FontFamily="Wingdings" 
                                       Foreground="White" 
                                       FontSize="14" 
                                       Text="ê" 
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"></TextBlock>
                        </Canvas>
                    </Viewbox>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Button.IsMouseOver"
                                 Value="true">
                            <Setter TargetName="BorderRect"
                                    Property="Stroke"
                                    Value="Gray"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="Black"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="BitmapEffect">
                                <Setter.Value>
                                    <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                           GlowSize="5"></OuterGlowBitmapEffect>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="Button.IsPressed"
                                 Value="true">
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99333333"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style x:Key="CustomMaximizeButtonStyle"
           TargetType="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Viewbox HorizontalAlignment="Stretch"
                             VerticalAlignment="Stretch">
                        <Canvas Height="16"
                                Width="16">
                            <Rectangle x:Name="BorderRect"
                                       Width="15"
                                       Height="15"
                                       Stroke="Transparent"
                                       Fill="Transparent"
                                       StrokeThickness="0.5"></Rectangle>
                            <TextBlock x:Name="MinText"
                                       FontFamily="Wingdings"
                                       Foreground="White"
                                       FontSize="14"
                                       Text="é"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"></TextBlock>
                        </Canvas>
                    </Viewbox>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Button.IsMouseOver"
                                 Value="true">
                            <Setter TargetName="BorderRect"
                                    Property="Stroke"
                                    Value="Gray"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="Black"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="BitmapEffect">
                                <Setter.Value>
                                    <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                           GlowSize="5"></OuterGlowBitmapEffect>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="Button.IsPressed"
                                 Value="true">
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99333333"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style x:Key="CustomCloseButtonStyle"
           TargetType="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Viewbox HorizontalAlignment="Stretch"
                             VerticalAlignment="Stretch">
                        <Canvas Height="16"
                                Width="16">
                            <Rectangle x:Name="BorderRect"
                                       Width="15"
                                       Height="15"
                                       Stroke="Transparent"
                                       Fill="Transparent"
                                       StrokeThickness="0.5"></Rectangle>
                            <TextBlock x:Name="MinText"
                                       FontFamily="Wingdings 2"
                                       Foreground="White"
                                       FontSize="14"
                                       Text="Ñ"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"></TextBlock>
                        </Canvas>
                    </Viewbox>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Button.IsMouseOver"
                                 Value="true">
                            <Setter TargetName="BorderRect"
                                    Property="Stroke"
                                    Value="Gray"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="Black"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="BitmapEffect">
                                <Setter.Value>
                                    <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                           GlowSize="5"></OuterGlowBitmapEffect>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="Button.IsPressed"
                                 Value="true">
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99333333"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="CustomAboutButtonStyle"
           TargetType="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Viewbox HorizontalAlignment="Stretch"
                             VerticalAlignment="Stretch">
                        <Canvas Height="16"
                                Width="16">
                            <Rectangle x:Name="BorderRect"
                                       Width="15"
                                       Height="15"
                                       Stroke="Transparent"
                                       Fill="Transparent"
                                       StrokeThickness="0.5"></Rectangle>
                            <TextBlock x:Name="MinText"
                                       FontFamily="Wingdings"
                                       Foreground="White"
                                       FontSize="14"
                                       Text="µ"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"></TextBlock>
                        </Canvas>
                    </Viewbox>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Button.IsMouseOver"
                                 Value="true">
                            <Setter TargetName="BorderRect"
                                    Property="Stroke"
                                    Value="Gray"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="Black"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="BitmapEffect">
                                <Setter.Value>
                                    <OuterGlowBitmapEffect GlowColor="LawnGreen"
                                                           GlowSize="5"></OuterGlowBitmapEffect>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="Button.IsPressed"
                                 Value="true">
                            <Setter TargetName="MinText"
                                    Property="Foreground"
                                    Value="LawnGreen"></Setter>
                            <Setter TargetName="BorderRect"
                                    Property="Fill">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0,0"
                                                         EndPoint="0,1">
                                        <GradientStop Color="#99333333"
                                                      Offset="0"></GradientStop>
                                        <GradientStop Color="#99FFFFFF"
                                                      Offset="0.7"></GradientStop>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="CustomSparkWindowStyle" TargetType="{x:Type wpfspark:SparkWindow}">
        <Setter Property="AllowsTransparency"
                Value="False"></Setter>
        <Setter Property="ResizeMode"
                Value="NoResize"></Setter>
        <Setter Property="MinHeight"
                Value="100"></Setter>
        <Setter Property="MinWidth"
                Value="200"></Setter>
        <Setter Property="MaxWidth"
                Value="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenWidthKey}}"></Setter>
        <Setter Property="MaxHeight"
                Value="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenHeightKey}}"></Setter>
        <Setter Property="WindowStyle"
                Value="None"></Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type wpfspark:SparkWindow}">
                    <Border Name="OuterFrame"
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            BorderBrush="{Binding Path=OuterBorderBrush, 
                              RelativeSource={RelativeSource FindAncestor,
                              AncestorType={x:Type wpfspark:SparkWindow}}}"
                            BorderThickness="{Binding Path=OuterBorderThickness, 
                              RelativeSource={RelativeSource FindAncestor,
                              AncestorType={x:Type wpfspark:SparkWindow}}}"
                            CornerRadius="{Binding Path=OuterBorderCornerRadius, 
                              RelativeSource={RelativeSource FindAncestor,
                              AncestorType={x:Type wpfspark:SparkWindow}}}"
                            SnapsToDevicePixels="True"
                            Background="Transparent">
                        <Border Name="windowFrame"
                                HorizontalAlignment="Stretch"
                                VerticalAlignment="Stretch"
                                BorderBrush="{Binding Path=InnerBorderBrush, RelativeSource=
                                  {RelativeSource FindAncestor,AncestorType=
                                  {x:Type wpfspark:SparkWindow}}}"
                                BorderThickness="{Binding Path=InnerBorderThickness, 
                                  RelativeSource={RelativeSource FindAncestor,
                                  AncestorType={x:Type wpfspark:SparkWindow}}}"
                                CornerRadius="{Binding Path=InnerBorderCornerRadius, 
                                  RelativeSource={RelativeSource FindAncestor,
                                  AncestorType={x:Type wpfspark:SparkWindow}}}"
                                SnapsToDevicePixels="True"
                                Background="{TemplateBinding Background}">
                            <Grid ShowGridLines="False">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="2"></RowDefinition>
                                    <RowDefinition Height="40"></RowDefinition>
                                    <RowDefinition Height="2"></RowDefinition>
                                    <RowDefinition Height="*"></RowDefinition>
                                </Grid.RowDefinitions>

                                <Grid Grid.Row="1"
                                      Grid.RowSpan="3"
                                      Grid.Column="1"
                                      Margin="0,0,0,0">
                                    <TextBlock  Name="PART_TitleText"
                                                FontFamily="{TemplateBinding FontFamily}"
                                                FontWeight="{TemplateBinding FontWeight}"
                                                FontSize="{TemplateBinding FontSize}"
                                                Foreground="{TemplateBinding Foreground}"
                                                FontStyle="{TemplateBinding FontStyle}"
                                                VerticalAlignment="Top"
                                                Margin="{Binding Path=TitleMargin, 
                                                  RelativeSource={RelativeSource FindAncestor,
                                                  AncestorType={x:Type wpfspark:SparkWindow}}}"
                                                Effect="{Binding Path=TitleEffect, 
                                                  RelativeSource={RelativeSource FindAncestor,
                                                  AncestorType={x:Type wpfspark:SparkWindow}}}"
                                                Text="{Binding Path=Title, RelativeSource=
                                                  {RelativeSource FindAncestor,AncestorType=
                                                  {x:Type wpfspark:SparkWindow}}, 
                                                  NotifyOnTargetUpdated=True}">
                                        <TextBlock.Triggers>
                                            <EventTrigger RoutedEvent="Binding.TargetUpdated">
                                                <BeginStoryboard>
                                                    <Storyboard>
                                                        <ThicknessAnimation Duration="0:0:0.3"
                                                                            Storyboard.TargetProperty="Margin"
                                                                            From="400,-30,0,0"
                                                                            To="0,-30,0,0">
                                                            <ThicknessAnimation.EasingFunction>
                                                                <ExponentialEase EasingMode="EaseOut"
                                                                                 Exponent="2"></ExponentialEase>
                                                            </ThicknessAnimation.EasingFunction>
                                                        </ThicknessAnimation>
                                                    </Storyboard>
                                                </BeginStoryboard>
                                            </EventTrigger>
                                        </TextBlock.Triggers>
                                    </TextBlock>
                                </Grid>

                                <Border Name="PART_TitleBar"
                                        Grid.Row="0"
                                        Grid.Column="0"
                                        Grid.RowSpan="3"
                                        Grid.ColumnSpan="2"
                                        Background="Black"
                                        Opacity="0" />

                                <Grid  Grid.Row="1">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"></ColumnDefinition>
                                        <ColumnDefinition Width="40"></ColumnDefinition>
                                        <ColumnDefinition Width="40"></ColumnDefinition>
                                        <ColumnDefinition Width="40"></ColumnDefinition>
                                        <ColumnDefinition Width="40"></ColumnDefinition>
                                        <ColumnDefinition Width="5" />
                                    </Grid.ColumnDefinitions>

                                    <Grid Grid.Row="1"
                                          Grid.RowSpan="4"
                                          Grid.Column="1"
                                          Margin="0,0,0,0">
                                    </Grid>
                                    <Button Name="PART_About"
                                            Margin="0,5,0,0"
                                            Grid.Column="1"
                                            Style="{StaticResource CustomAboutButtonStyle}">
                                    </Button>
                                    <Button Name="PART_Minimize"
                                            Margin="0,5,0,0"
                                            Grid.Column="2"
                                            Style="{StaticResource CustomMinimizeButtonStyle}">
                                    </Button>
                                    <Button Name="PART_Maximize"
                                            Margin="0,5,0,0"
                                            Grid.Column="3"
                                            Style="{StaticResource CustomMaximizeButtonStyle}">
                                    </Button>
                                    <Button Name="PART_Close"
                                            Margin="0,5,0,0"
                                            Grid.Column="4"
                                            Style="{StaticResource CustomCloseButtonStyle}"></Button>
                                </Grid>

                                <!-- Content -->
                                <AdornerDecorator Grid.Row="3" Grid.Column="0">
                                    <ContentPresenter />
                                </AdornerDecorator>
                            </Grid>
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

You can use the above style on any SparkWindow.

EndPoint

In the default style for SparkWindow, the ResizeMode is set to NoResize as SparkWindow does not support resizing currently. I am planning to bring this feature in the future version of WPFSpark.

History

  • December 21, 2011: WPFSpark v1.0 released.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here