自定义WPF 窗口样式
原文:自定义WPF 窗口样式
Normal
0
false
7.8 pt
0
2
false
false
false
EN-US
ZH-CN
X-NONE
自定义
Window
在客户端程序中,经常需要用到自定义一个
Window
,大部分是为了好看吧。做了很多研究和实践之后,觉得需要把这个过程写下来,以供查阅。
WPF
提供的丰富的功能使得自定义
Window
变得简单,但是也不是一个简单的
Style
就能做到的事情。虽然
WPF
中的控件是
Lookless
的,但是
Window
类有他自己的特殊之处,做个简单的实验就能看出,对于普通的
WPF
控件,用
XamlWriter.Write
方法就能将某个类型对象的模板输出出来,这样就可以看到该控件的内部构造。但是如果输出
Window
对象的默认模板,就会发现模板非常简单,其中并没有包含标题栏以及最大化最小化按钮的定义,具体的实现不得而知,但是至少说明
Window
的默认
Style
不是按照
WPF
的规范来实现的。
为了实现任意风格的
Window
就需要重写
Window
的默认模板,第一步要做的就是创建
Window
的一个派生类,并创建自定义
Style
,然后重写
DefaultStyleKey
属性让
WPF
引擎来将样式和
Window
的派生类装载到一起。具体做法如下:
1.
创建一个
WPF Application
项目
2.
在项目中添加
Themes
文件夹,并在该文件夹下添加名称为
Generic.xaml
的
ResourceDictionary
。该文件夹和文件的名称和位置都是固定的,也就是说必须这么做才能让
WPF
引擎让自定义控件和默认
Style
协同工作,因为微软对此进行了硬编码。
3.
创建自定义窗口类为
HeaderedWindow
,为什么定义为
HeaderedWindow
呢?因为我觉得
Window
对象更像是一个
HeaderedContentControl
而不是一个
ContentControl
,因为它的标题栏更像是一个
Header
,而不仅仅是为了显示图标和标题!
HeaderedWindow
类的定义如下:
public
class
HeaderedWindow
: Window
{
static
HeaderedWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(typeof
(HeaderedWindow
)));
}
}
4.
在
Generic.xaml
文件中添加
HeaderedWindow
的默认样式,如下:
<
Style
TargetType
="{
x
:
Type
l
:
HeaderedWindow
}">
<
Setter
Property
="WindowStyle"
Value
="None"/>
<
Setter
Property
="ResizeMode"
Value
="NoResize"/>
<
Setter
Property
="Background"
Value
="Gray"/>
<
Setter
Property
="BorderBrush"
Value
="#FF5A3D1C"/>
<
Setter
Property
="BorderThickness"
Value
="1"/>
<
Setter
Property
="MinWidth"
Value
="90"/>
<
Setter
Property
="MinHeight"
Value
="33"/>
<
Setter
Property
="VerticalContentAlignment"
Value
="Stretch"/>
<
Setter
Property
="HorizontalContentAlignment"
Value
="Stretch"/>
</
Style
>
最重要的两个属性是
WindowStyle
和
ResizeMode
,将
WindowStyle
属性设置为
None
将会去除默认的标题栏,将
ResizeMode
设置为
NoResize
将会去除
Window
边框,该边框在
Window 7
比较难看,不过却有他的作用,
就是用来调整窗口大小,我们把它去掉了,就意味着我们需要自己来编写调整窗口大小的代码了。
5.
测试一下这个
HeaderedWindow
吧,编写如下测试代码:
<
local
:
HeaderedWindow
x
:
Class
="CustomWindowDemo.MainWindow"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns
:
local
="clr-namespace:CustomWindowDemo"
Title
="MainWindow"
Height
="350"
Width
="525">
<
Grid
>
</
Grid
>
</
local
:
HeaderedWindow
>
后置代码类必须要从
HeaderedWindow
继承才行。运行程序发现窗口黑乎乎一片。这是因为我们还没有重写
Window
的
ControlTemplate
的缘故。
6.
一个窗口应该有如下基本要素
a)
图标,就是
Icon
属性,自定义窗口应该有将其显示出来的能力
b)
标题,就是
Title
属性,自定窗口应该将其显示在标题栏中
c)
最大化,最小化,关闭按钮
d)
为了美观,
Window
的边框应该定义出来。但是当窗口最大化的时候应该隐藏边框,扩大窗口的使用面积,这也是
Windows
下窗口的标准行为。
e)
八个方向的
Resizer
应该定义出来,这些
Resize
其实就是一些透明的
Thumb
控件,用来支持拖动事件,并改变窗口的定位和高宽。
对于
HeaderedWindow
还有几个扩展要素需要提供:
a)
自定义
Header
属性,用户可以重新定义
Window
的
Header
,在这里我们将
Window
的
Icon
和
Title
定义为他的默认
Header
,所以有一个
ShowDefaultHeader
属性用来控制默认
Header
的可见性。
Header
属性是一个
Object
对象,其用法和
HeaderedContentControl
控件的
Header
几乎一模一样。
b)
ShowResizeGrip
属性,用来控制是否在右下角显示一个抓手模样的图形,用来指示用此处可以拖动,当然不显示也可以调整窗口大小,不过显示这个抓手增加了易用性。
c)
CanResize
属性,由于我们在样式中将窗口设置
ResizeMode
为
NoResize
,所以需要提供一个额外的属性用来设置当前窗口是否可以调整大小。当
CanResize
为
False
的时候,所有可以调整窗口大小的控件将被隐藏。
d)
最后一个比较有意思的属性就是
IsFullScreenMaximize
,当该属性设置为
True
的时候,那么当最大化的时候,窗口将把任务栏覆盖住。是真正意义上的“最大化”了。
7.
如果想制作不规则形状的窗口,需要将
AllowTransparency
属性设置为
true
,不过当此属性为
true
的时候,该
WPF
窗口将不能作为
WinForm
控件或者
ActiveX
控件的宿主了。所以需要慎重考虑。
最后附上全部代码以及使用方式,以供查阅!
Generic.xaml
的内容:
<
ResourceDictionary
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns
:
sys
="clr-namespace:System;assembly=mscorlib"
xmlns
:
l
="clr-namespace:CustomWindowDemo">
<
l
:
BoolToVisibilityConverter
x
:
Key
="BoolToVisibilityConverter"></
l
:
BoolToVisibilityConverter
>
<
Style
x
:
Key
="styleWindowButtonMinimize"
BasedOn
="{
x
:
Null
}"
TargetType
="{
x
:
Type
Button
}">
<
Setter
Property
="Template">
<
Setter.Value
>
<
ControlTemplate
TargetType
="{
x
:
Type
Button
}">
<
Grid
x
:
Name
="buttonClose">
<
Ellipse
Stroke
="{
x
:
Null
}"
StrokeThickness
="1"
x
:
Name
="btnEllipse" >
<
Ellipse.Fill
>
<
RadialGradientBrush
>
<
GradientStop
Color
="#BFABA7A4"
Offset
="0.777"/>
<
GradientStop
Color
="#FF897F77"
Offset
="1"/>
</
RadialGradientBrush
>
</
Ellipse.Fill
>
</
Ellipse
>
<
Path
x
:
Name
="iconMin"
Width
="11.1641"
Height
="2.06641"
Canvas.Left
="3.97266"
Canvas.Top
="8.51962"
Stretch
="Fill"
Fill
="#FFFFFFFF"
Data
="F1 M
5.00659,8.51962C 4.43613,8.51831 3.97266,8.98184 3.97266,9.55347C
3.97266,10.1238 4.435,10.586 5.00659,10.586L 14.1028,10.586C 14.6733,10.586
15.1353,10.1238 15.1367,9.55215C 15.1367,8.98056 14.6733,8.51962
14.1028,8.51962L 5.00659,8.51962 Z "/>
<
ContentPresenter
SnapsToDevicePixels
="{
TemplateBinding
SnapsToDevicePixels
}"
HorizontalAlignment
="{
TemplateBinding
HorizontalContentAlignment
}"
VerticalAlignment
="{
TemplateBinding
VerticalContentAlignment
}"
RecognizesAccessKey
="True"/>
</
Grid
>
<
ControlTemplate.Triggers
>
<
Trigger
Property
="IsFocused"
Value
="True"/>
<
Trigger
Property
="IsDefaulted"
Value
="True"/>
<
Trigger
Property
="IsMouseOver"
Value
="True">
<
Setter
Property
="Fill"
Value
="#FF5D4E3E"
TargetName
="btnEllipse"/>
</
Trigger
>
<
Trigger
Property
="IsPressed"
Value
="True"/>
<
Trigger
Property
="IsEnabled"
Value
="False"/>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
<
Style
x
:
Key
="styleWindowButtonMaximize"
BasedOn
="{
x
:
Null
}"
TargetType
="{
x
:
Type
ToggleButton
}">
<
Setter
Property
="Template">
<
Setter.Value
>
<
ControlTemplate
TargetType
="{
x
:
Type
ToggleButton
}">
<
Grid
>
<
Ellipse
x
:
Name
="btnEllipse"
Stroke
="{
x
:
Null
}"
StrokeThickness
="1">
<
Ellipse.Fill
>
<
RadialGradientBrush
>
<
GradientStop
Color
="#BFABA7A4"
Offset
="0.777"/>
<
GradientStop
Color
="#FF897F77"
Offset
="1"/>
</
RadialGradientBrush
>
</
Ellipse.Fill
>
</
Ellipse
>
<
Path
x
:
Name
="iconMax"
Width
="12.4011"
Height
="9.93359"
Stretch
="Fill"
Fill
="#FFFFFFFF"
Data
="F1 M
390.641,289.034C 389.073,289.034 387.8,290.308 387.8,291.875L 387.8,296.125C
387.8,297.692 389.073,298.967 390.641,298.967L 397.362,298.967C 398.927,298.967
400.201,297.692 400.201,296.125L 400.201,291.875C 400.201,290.308
398.927,289.034 397.362,289.034L 390.641,289.034 Z M 389.399,296.125L
389.399,291.875C 389.399,291.19 389.956,290.634 390.641,290.634L
397.362,290.634C 398.045,290.634 398.602,291.19 398.602,291.875L
398.602,296.125C 398.602,296.81 398.045,297.367 397.362,297.367L
390.641,297.367C 389.956,297.367 389.399,296.81 389.399,296.125 Z "/>
<
Path
x
:
Name
="iconRestore"
Visibility
="Collapsed"
Width
="11.6719"
Height
="9.48242"
Stretch
="Fill"
Fill
="#FFFFFFFF"
Data
="F1 M
411.826,302.421C 411.802,301.775 411.28,301.259 410.629,301.259L
404.483,301.259C 403.836,301.257 403.312,301.775 403.286,302.421L
403.277,302.421C 403.277,302.421 403.277,303.254 403.277,304.177L
407.126,304.177C 407.984,304.177 408.679,304.853 408.722,305.728L 408.722,307.822L
410.629,307.822C 411.294,307.822 411.834,307.283 411.836,306.615C
411.836,306.507 411.836,302.421 411.836,302.421L 411.826,302.421 Z M
407.126,304.956L 400.979,304.956C 400.545,304.955 400.19,305.301
400.164,305.728L 400.164,309.925C 400.164,310.374 400.531,310.741
400.981,310.741L 407.126,310.741C 407.575,310.741 407.942,310.374
407.944,309.923L 407.934,305.742C 407.918,305.301 407.565,304.956
407.126,304.956 Z "/>
<
ContentPresenter
SnapsToDevicePixels
="{
TemplateBinding
SnapsToDevicePixels
}"
HorizontalAlignment
="{
TemplateBinding
HorizontalContentAlignment
}"
VerticalAlignment
="{
TemplateBinding
VerticalContentAlignment
}"
RecognizesAccessKey
="True"/>
</
Grid
>
<
ControlTemplate.Triggers
>
<
Trigger
Property
="IsFocused"
Value
="True"/>
<
Trigger
Property
="IsMouseOver"
Value
="True">
<
Setter
Property
="Fill"
Value
="#FF6E8F9A"
TargetName
="btnEllipse"/>
</
Trigger
>
<
Trigger
Property
="IsPressed"
Value
="True"/>
<
Trigger
Property
="IsEnabled"
Value
="False"/>
<
Trigger
Property
="IsChecked"
Value
="True">
<
Setter
TargetName
="iconMax"
Property
="Visibility"
Value
="Collapsed" />
<
Setter
TargetName
="iconRestore"
Property
="Visibility"
Value
="Visible" />
</
Trigger
>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
<
Style
x
:
Key
="styleMainWindowButtonClose"
BasedOn
="{
x
:
Null
}"
TargetType
="{
x
:
Type
Button
}">
<
Setter
Property
="Template">
<
Setter.Value
>
<
ControlTemplate
TargetType
="{
x
:
Type
Button
}">
<
Grid
x
:
Name
="buttonClose">
<
Ellipse
Stroke
="{
x
:
Null
}"
StrokeThickness
="1"
x
:
Name
="btnEllipse" >
<
Ellipse.Fill
>
<
RadialGradientBrush
>
<
GradientStop
Color
="#BFABA7A4"
Offset
="0.777"/>
<
GradientStop
Color
="#FF897F77"
Offset
="1"/>
</
RadialGradientBrush
>
</
Ellipse.Fill
>
</
Ellipse
>
<
Path
x
:
Name
="iconX"
Width
="8.50003"
Height
="8.50006"
Stretch
="Fill"
Fill
="#FF8D1200"
Data
="F1 M
401.281,299.818L 398.795,297.333L 401.281,294.847C 401.684,294.445
401.684,293.79 401.281,293.386C 400.877,292.982 400.222,292.982 399.82,293.386L
397.334,295.872L 394.848,293.386C 394.445,292.982 393.79,292.982
393.386,293.386C 392.982,293.79 392.982,294.445 393.386,294.848L 395.872,297.334L
393.387,299.818C 392.984,300.222 392.982,300.876 393.387,301.281C
393.791,301.684 394.445,301.683 394.848,301.279L 397.333,298.795L
399.818,301.281C 400.222,301.684 400.877,301.684 401.281,301.281C
401.684,300.877 401.684,300.222 401.281,299.818 Z "/>
<
ContentPresenter
SnapsToDevicePixels
="{
TemplateBinding
SnapsToDevicePixels
}"
HorizontalAlignment
="{
TemplateBinding
HorizontalContentAlignment
}"
VerticalAlignment
="{
TemplateBinding
VerticalContentAlignment
}"
RecognizesAccessKey
="True"/>
</
Grid
>
<
ControlTemplate.Triggers
>
<
Trigger
Property
="IsFocused"
Value
="True"/>
<
Trigger
Property
="IsDefaulted"
Value
="True"/>
<
Trigger
Property
="IsMouseOver"
Value
="True">
<
Setter
Property
="Fill"
Value
="#FF333332"
TargetName
="btnEllipse"/>
<
Setter
Property
="Fill"
Value
="#FFFFFFFF"
TargetName
="iconX"/>
</
Trigger
>
<
Trigger
Property
="IsPressed"
Value
="True"/>
<
Trigger
Property
="IsEnabled"
Value
="False"/>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
<!--Window Style-->
<
Style
TargetType
="{
x
:
Type
l
:
HeaderedWindow
}">
<
Setter
Property
="WindowStyle"
Value
="None"/>
<
Setter
Property
="ResizeMode"
Value
="NoResize"/>
<
Setter
Property
="Background">
<
Setter.Value
>
<
LinearGradientBrush
MappingMode
="Absolute"
StartPoint
="327.657227,-92.486328"
EndPoint
="495.033722,694.958313">
<
LinearGradientBrush.Transform
>
<
MatrixTransform
Matrix
="2.000000,0.000000,-0.000000,-1.000000,-4.095703,626.486816"
/>
</
LinearGradientBrush.Transform
>
<
GradientStop
Offset
="0.743034"
Color
="LightPink" />
<
GradientStop
Offset
="0.835913"
Color
="Red" />
<
GradientStop
Offset
="0.840783"
Color
="DarkRed" />
<
GradientStop
Offset
="0.842105"
Color
="Red" />
<
GradientStop
Offset
="0.990868"
Color
="LightPink" />
</
LinearGradientBrush
>
</
Setter.Value
>
</
Setter
>
<
Setter
Property
="BorderBrush"
Value
="#FF5A3D1C"/>
<
Setter
Property
="BorderThickness"
Value
="1"/>
<
Setter
Property
="MinWidth"
Value
="90"/>
<
Setter
Property
="MinHeight"
Value
="33"/>
<
Setter
Property
="VerticalContentAlignment"
Value
="Stretch"/>
<
Setter
Property
="HorizontalContentAlignment"
Value
="Stretch"/>
<
Setter
Property
="Template">
<
Setter.Value
>
<
ControlTemplate
TargetType
="{
x
:
Type
l
:
HeaderedWindow
}">
<
Border
BorderBrush
="{
TemplateBinding
BorderBrush
}"
BorderThickness
="{
TemplateBinding
BorderThickness
}">
<
Grid
SnapsToDevicePixels
="True"
Background
="{
TemplateBinding
Background
}">
<
Grid.RowDefinitions
>
<
RowDefinition
Height
="33" />
<
RowDefinition
/>
</
Grid.RowDefinitions
>
<!-- Window header-->
<
Grid
Name
="PART_HeaderContainer"
Background
="Transparent">
<
StackPanel
Orientation
="Horizontal"
Margin
="4,0"
Visibility
="{
Binding
RelativeSource
={
x
:
Static
RelativeSource
.TemplatedParent},
Path
=ShowDefaultHeader,
Converter
={
StaticResource
BoolToVisibilityConverter
}}">
<
Image
Height
="24"
Width
="24"
HorizontalAlignment
="Left"
VerticalAlignment
="Center"
Source
="{
TemplateBinding
Icon
}"/>
<
TextBlock
Text
="{
TemplateBinding
Title
}"
Margin
="4,0"
VerticalAlignment
="Center"
FontFamily
="Arial"
FontSize
="15"
Foreground
="#FFFFFFFF"
TextWrapping
="NoWrap"/>
</
StackPanel
>
<
StackPanel
HorizontalAlignment
="Right"
Orientation
="Horizontal"
Margin
="0,0,10,0">
<
Button
x
:
Name
="PART_MinimizeButton"
IsTabStop
="False"
Cursor
="Hand"
Style
="{
StaticResource
styleWindowButtonMinimize
}"
Width
="19"
Height
="19"
Margin
="0,0,4,0"
ToolTip
="Minimize"
Visibility
="{
Binding
RelativeSource
={
x
:
Static
RelativeSource
.TemplatedParent},
Path
=CanResize,
Converter
={
StaticResource
BoolToVisibilityConverter
}}"/>
<
ToggleButton
x
:
Name
="PART_RestoreButton"
IsTabStop
="False"
Cursor
="Hand"
Style
="{
StaticResource
styleWindowButtonMaximize
}"
Width
="19"
Height
="19"
Margin
="0,0,4,0"
ToolTip
="Maximize"
Visibility
="{
Binding
RelativeSource
={
x
:
Static
RelativeSource
.TemplatedParent},
Path
=CanResize,
Converter
={
StaticResource
BoolToVisibilityConverter
}}"/>
<
Button
x
:
Name
="PART_CloseButton"
IsTabStop
="False"
Cursor
="Hand"
Style
="{
StaticResource
styleMainWindowButtonClose
}"
Width
="19"
Height
="19"
Margin
="0,0,0,0"
ToolTip
="Close"/>
</
StackPanel
>
<
ContentPresenter
ContentSource
="Header"
VerticalAlignment
="Stretch"
HorizontalAlignment
="Stretch"/>
</
Grid
>
<
Grid
Grid.RowSpan
="2"
x
:
Name
="PART_ResizerContainers"
Visibility
="Hidden">
<
Grid.Resources
>
<
sys
:
Double
x
:
Key
="StraightResizerSize">
8
</
sys
:
Double
>
<
sys
:
Double
x
:
Key
="SlantResizerSize">
16
</
sys
:
Double
>
<
Style
TargetType
="{
x
:
Type
Thumb
}">
<
Setter
Property
="Template">
<
Setter.Value
>
<
ControlTemplate
>
<
Rectangle
Fill
="Transparent"/>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
</
Grid.Resources
>
<
Thumb
Width
="{
StaticResource
StraightResizerSize
}"
VerticalAlignment
="Stretch"
HorizontalAlignment
="Left"
Cursor
="SizeWE"
x
:
Name
="PART_LeftResizer"/>
<
Thumb
Height
="{
StaticResource
StraightResizerSize
}"
VerticalAlignment
="Top"
HorizontalAlignment
="Stretch"
Cursor
="SizeNS"
x
:
Name
="PART_TopResizer"/>
<
Thumb
Width
="{
StaticResource
StraightResizerSize
}"
VerticalAlignment
="Stretch"
HorizontalAlignment
="Right"
Cursor
="SizeWE"
x
:
Name
="PART_RightResizer"/>
<
Thumb
Height
="{
StaticResource
StraightResizerSize
}"
VerticalAlignment
="Bottom"
HorizontalAlignment
="Stretch"
Cursor
="SizeNS"
x
:
Name
="PART_BottomResizer"/>
<
ResizeGrip
Width
="{
StaticResource
SlantResizerSize
}"
Height
="{
StaticResource
SlantResizerSize
}"
HorizontalAlignment
="Right"
VerticalAlignment
="Bottom"
Visibility
="{
Binding
RelativeSource
={
x
:
Static
RelativeSource
.TemplatedParent},
Path
=ShowResizeGrip,
Converter
={
StaticResource
BoolToVisibilityConverter
}}"/>
<
Thumb
Width
="{
StaticResource
SlantResizerSize
}"
Height
="{
StaticResource
SlantResizerSize
}"
HorizontalAlignment
="Right"
VerticalAlignment
="Bottom"
Cursor
="SizeNWSE"
x
:
Name
="PART_BottomRightResizer"/>
<
Thumb
Width
="{
StaticResource
SlantResizerSize
}"
Height
="{
StaticResource
SlantResizerSize
}"
HorizontalAlignment
="Right"
VerticalAlignment
="Top"
Cursor
="SizeNESW"
x
:
Name
="PART_TopRightResizer"/>
<
Thumb
Width
="{
StaticResource
SlantResizerSize
}"
Height
="{
StaticResource
SlantResizerSize
}"
HorizontalAlignment
="Left"
VerticalAlignment
="Top"
Cursor
="SizeNWSE"
x
:
Name
="PART_TopLeftResizer"/>
<
Thumb
Width
="{
StaticResource
SlantResizerSize
}"
Height
="{
StaticResource
SlantResizerSize
}"
HorizontalAlignment
="Left"
VerticalAlignment
="Bottom"
Cursor
="SizeNESW"
x
:
Name
="PART_BottomLeftResizer"/>
</
Grid
>
<
Border
x
:
Name
="PART_ContentBorder"
Grid.Row
="1"
BorderBrush
="#FF5A3D1C"
BorderThickness
="2"
Margin
="8,0,8,8"
ClipToBounds
="True">
<
AdornerDecorator
>
<
ContentPresenter
Margin
="{
TemplateBinding
Padding
}"
VerticalAlignment
="{
TemplateBinding
VerticalContentAlignment
}"
HorizontalAlignment
="{
TemplateBinding
HorizontalContentAlignment
}"/>
</
AdornerDecorator
>
</
Border
>
</
Grid
>
</
Border
>
<
ControlTemplate.Triggers
>
<
MultiTrigger
>
<
MultiTrigger.Conditions
>
<
Condition
Property
="CanResize"
Value
="True"/>
<
Condition
Property
="WindowState"
Value
="Normal"/>
</
MultiTrigger.Conditions
>
<
Setter
TargetName
="PART_ResizerContainers"
Property
="Visibility"
Value
="Visible"/>
</
MultiTrigger
>
<
Trigger
Property
="WindowState"
Value
="Maximized">
<
Setter
TargetName
="PART_ContentBorder"
Property
="Margin"
Value
="0"/>
<
Setter
TargetName
="PART_ContentBorder"
Property
="BorderThickness"
Value
="0,2,0,0"/>
</
Trigger
>
</
ControlTemplate.Triggers
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
</
ResourceDictionary
>
以上样式中使用了一个Converter类,代码如下:
Normal
0
7.8 pt
0
2
false
false
false
EN-US
ZH-CN
X-NONE
namespace
CustomWindowDemo
{
public
class
BoolToVisibilityConverter
: IValueConverter
{
#region
IValueConverter Members
public
object
Convert(object
value, Type
targetType, object
parameter, System.Globalization.CultureInfo
culture)
{
return
(bool
)value ? Visibility
.Visible
: Visibility
.Hidden;
}
public
object
ConvertBack(object
value, Type
targetType, object
parameter, System.Globalization.CultureInfo
culture)
{
throw
new
NotImplementedException
();
}
#endregion
}
}
HeaderedWindow
的代码:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Windows;
using
System.Windows.Input;
using
System.Windows.Markup;
using
System.Reflection;
using
System.Windows.Media.Animation;
using
System.Windows.Controls;
using
System.Windows.Controls.Primitives;
using
System.Diagnostics;
namespace
CustomWindowDemo
{
[TemplatePart
(Name
= HeaderContainerName, Type = typeof
(FrameworkElement
))]
[TemplatePart
(Name
= MinimizeButtonName, Type = typeof
(Button
))]
[TemplatePart
(Name
= RestoreButtonName, Type = typeof
(ToggleButton
))]
[TemplatePart
(Name
= CloseButtonName, Type = typeof
(Button
))]
[TemplatePart
(Name
= TopResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= LeftResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= RightResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= BottomResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= BottomRightResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= TopRightResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= TopLeftResizerName, Type = typeof
(Thumb
))]
[TemplatePart
(Name
= BottomLeftResizerName, Type = typeof
(Thumb
))]
public
class
HeaderedWindow
: Window
{
#region
Template Part Name
private
const
string
HeaderContainerName = "PART_HeaderContainer"
;
private
const
string
MinimizeButtonName = "PART_MinimizeButton"
;
private
const
string
RestoreButtonName = "PART_RestoreButton"
;
private
const
string
CloseButtonName = "PART_CloseButton"
;
private
const
string
TopResizerName = "PART_TopResizer"
;
private
const
string
LeftResizerName = "PART_LeftResizer"
;
private
const
string
RightResizerName = "PART_RightResizer"
;
private
const
string
BottomResizerName = "PART_BottomResizer"
;
private
const
string
BottomRightResizerName = "PART_BottomRightResizer"
;
private
const
string
TopRightResizerName
= "PART_TopRightResizer"
;
private
const
string
TopLeftResizerName = "PART_TopLeftResizer"
;
private
const
string
BottomLeftResizerName = "PART_BottomLeftResizer"
;
#endregion
#region
Dependency Properties
public
static
readonly
DependencyProperty
ShowDefaultHeaderProperty =
DependencyProperty
.Register("ShowDefaultHeader"
, typeof
(bool
), typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(true
));
public
static
readonly
DependencyProperty
ShowResizeGripProperty =
DependencyProperty
.Register("ShowResizeGrip"
, typeof
(bool
), typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(false
));
public
static
readonly
DependencyProperty
CanResizeProperty =
DependencyProperty
.Register("CanResize"
, typeof
(bool
), typeof
(HeaderedWindow
), new
FrameworkPropertyMetadata
(true
));
public
static
readonly
DependencyProperty
HeaderProperty =
DependencyProperty
.Register("Header"
, typeof
(object
), typeof
(HeaderedWindow
), new
FrameworkPropertyMetadata
(null
, OnHeaderChanged));
public
static
readonly
DependencyProperty
HeaderTemplateProperty =
DependencyProperty
.Register("HeaderTemplate"
, typeof
(DataTemplate
),
typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(null
));
public
static
readonly
DependencyProperty
HeaderTempateSelectorProperty =
DependencyProperty
.Register("HeaderTempateSelector"
, typeof
(DataTemplateSelector
),
typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(null
));
public
static
readonly
DependencyProperty
IsFullScreenMaximizeProperty =
DependencyProperty
.Register("IsFullScreenMaximize"
, typeof
(bool
), typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(false
));
private
static
void
OnHeaderChanged(DependencyObject
sender, DependencyPropertyChangedEventArgs
e)
{
HeaderedWindow
win = sender as
HeaderedWindow
;
win.RemoveLogicalChild(e.OldValue);
win.AddLogicalChild(e.NewValue);
}
public
bool
ShowDefaultHeader
{
get
{ return
(bool
)GetValue(ShowDefaultHeaderProperty);
}
set
{
SetValue(ShowDefaultHeaderProperty, value
); }
}
public
bool
CanResize
{
get
{ return
(bool
)GetValue(CanResizeProperty);
}
set
{
SetValue(CanResizeProperty, value
); }
}
public
bool
ShowResizeGrip
{
get
{ return
(bool
)GetValue(ShowResizeGripProperty);
}
set
{
SetValue(ShowResizeGripProperty, value
); }
}
public
object
Header
{
get
{ return
(object
)GetValue(HeaderProperty);
}
set
{
SetValue(HeaderProperty, value
); }
}
public
DataTemplate
HeaderTemplate
{
get
{ return
(DataTemplate
)GetValue(HeaderTemplateProperty);
}
set
{
SetValue(HeaderTemplateProperty, value
); }
}
public
DataTemplateSelector
HeaderTempateSelector
{
get
{ return
(DataTemplateSelector
)GetValue(HeaderTempateSelectorProperty);
}
set
{
SetValue(HeaderTempateSelectorProperty, value
);
}
}
public
bool
IsFullScreenMaximize
{
get
{ return
(bool
)GetValue(IsFullScreenMaximizeProperty);
}
set
{
SetValue(IsFullScreenMaximizeProperty, value
);
}
}
#endregion
static
HeaderedWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof
(HeaderedWindow
),
new
FrameworkPropertyMetadata
(typeof
(HeaderedWindow
)));
}
#region
Private Fields
private
FrameworkElement
headerContainer;
private
Button
minimizeButton;
private
ToggleButton
restoreButton;
private
Button
closeButton;
private
Thumb
topResizer;
private
Thumb
leftResizer;
private
Thumb
rightResizer;
private
Thumb
bottomResizer;
private
Thumb
bottomRightResizer;
private
Thumb
topRightResizer;
private
Thumb
topLeftResizer;
private
Thumb
bottomLeftResizer;
#endregion
public
override
void
OnApplyTemplate()
{
base
.OnApplyTemplate();
headerContainer = GetTemplateChild<FrameworkElement
>(HeaderContainerName);
headerContainer.MouseLeftButtonDown +=
HeaderContainerMouseLeftButtonDown;
closeButton = GetTemplateChild<Button
>(CloseButtonName);
closeButton.Click += delegate
{ Close(); };
restoreButton = GetTemplateChild<ToggleButton
>(RestoreButtonName);
restoreButton.Checked += delegate
{ ChangeWindowState(WindowState
.Maximized);
};
restoreButton.Unchecked += delegate
{ ChangeWindowState(WindowState
.Normal);
};
StateChanged += new
EventHandler
(HeaderedWindowStateChanged);
minimizeButton = GetTemplateChild<Button
>(MinimizeButtonName);
minimizeButton.Click += delegate
{ ChangeWindowState(WindowState
.Minimized);
};
topResizer = GetTemplateChild<Thumb
>(TopResizerName);
topResizer.DragDelta += new
DragDeltaEventHandler
(ResizeTop);
leftResizer = GetTemplateChild<Thumb
>(LeftResizerName);
leftResizer.DragDelta += new
DragDeltaEventHandler
(ResizeLeft);
rightResizer = GetTemplateChild<Thumb
>(RightResizerName);
rightResizer.DragDelta += new
DragDeltaEventHandler
(ResizeRight);
bottomResizer = GetTemplateChild<Thumb
>(BottomResizerName);
bottomResizer.DragDelta += new
DragDeltaEventHandler
(ResizeBottom);
bottomRightResizer = GetTemplateChild<Thumb
>(BottomRightResizerName);
bottomRightResizer.DragDelta += new
DragDeltaEventHandler
(ResizeBottomRight);
topRightResizer = GetTemplateChild<Thumb
>(TopRightResizerName);
topRightResizer.DragDelta += new
DragDeltaEventHandler
(ResizeTopRight);
topLeftResizer = GetTemplateChild<Thumb
>(TopLeftResizerName);
topLeftResizer.DragDelta += new
DragDeltaEventHandler
(ResizeTopLeft);
bottomLeftResizer = GetTemplateChild<Thumb
>(BottomLeftResizerName);
bottomLeftResizer.DragDelta += new
DragDeltaEventHandler
(ResizeBottomLeft);
}
private
T
GetTemplateChild<T>(string
childName) where
T : FrameworkElement
,
new
()
{
return
(GetTemplateChild(childName) as
T) ?? new
T();
}
private
void
HeaderContainerMouseLeftButtonDown(object
sender, MouseButtonEventArgs
e)
{
if
(e.ClickCount == 1)
{
DragMove();
}
else
{
ChangeWindowState(WindowState == WindowState
.Maximized ? WindowState
.Normal
: WindowState
.Maximized);
}
}
private
void
ChangeWindowState(WindowState
state)
{
if
(state
== WindowState
.Maximized)
{
if
(!IsFullScreenMaximize && IsLocationOnPrimaryScreen())
{
MaxHeight = SystemParameters
.WorkArea.Height;
MaxWidth = SystemParameters
.WorkArea.Width;
}
else
{
MaxHeight = double
.PositiveInfinity;
MaxWidth = double
.PositiveInfinity;
}
}
WindowState = state;
}
private
void
HeaderedWindowStateChanged(object
sender, EventArgs
e)
{
if
(WindowState == WindowState
.Minimized)
{
restoreButton.IsChecked = null
;
}
else
{
restoreButton.IsChecked = WindowState
== WindowState
.Maximized;
}
}
private
bool
IsLocationOnPrimaryScreen()
{
return
Left < SystemParameters
.PrimaryScreenWidth
&& Top < SystemParameters
.PrimaryScreenHeight;
}
#region
Resize
private
void
ResizeBottomLeft(object
sender, DragDeltaEventArgs
e)
{
ResizeLeft(sender, e);
ResizeBottom(sender, e);
}
private
void
ResizeTopLeft(object
sender, DragDeltaEventArgs
e)
{
ResizeTop(sender, e);
ResizeLeft(sender, e);
}
private
void
ResizeTopRight(object
sender, DragDeltaEventArgs
e)
{
ResizeRight(sender, e);
ResizeTop(sender, e);
}
private
void
ResizeBottomRight(object
sender, DragDeltaEventArgs
e)
{
ResizeBottom(sender, e);
ResizeRight(sender, e);
}
private
void
ResizeBottom(object
sender, DragDeltaEventArgs
e)
{
if
(ActualHeight <= MinHeight && e.VerticalChange < 0)
{
return
;
}
if
(double
.IsNaN(Height))
{
Height = ActualHeight;
}
Height += e.VerticalChange;
}
private
void
ResizeRight(object
sender, DragDeltaEventArgs
e)
{
if
(ActualWidth <= MinWidth && e.HorizontalChange < 0)
{
return
;
}
if
(double
.IsNaN(Width))
{
Width = ActualWidth;
}
Width += e.HorizontalChange;
}
private
void
ResizeLeft(object
sender, DragDeltaEventArgs
e)
{
if
(ActualWidth <= MinWidth && e.HorizontalChange > 0)
{
return
;
}
if
(double
.IsNaN(Width))
{
Width = ActualWidth;
}
Width -= e.HorizontalChange;
Left += e.HorizontalChange;
}
private
void
ResizeTop(object
sender, DragDeltaEventArgs
e)
{
if
(ActualHeight <= MinHeight && e.VerticalChange > 0)
{
return
;
}
if
(double
.IsNaN(Height))
{
Height = ActualHeight;
}
Height -= e.VerticalChange;
Top += e.VerticalChange;
}
#endregion
}
}
使用
HeaderedWindow
的代码:
XAML
:
<
local
:
HeaderedWindow
x
:
Class
="CustomWindowDemo.MainWindow"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns
:
local
="clr-namespace:CustomWindowDemo"
Title
="MainWindow"
Height
="350"
Width
="525">
<
Grid
>
</
Grid
>
</
local
:
HeaderedWindow
>
.cs
public
partial
class
MainWindow
:
HeaderedWindow
{
public
MainWindow()
{
InitializeComponent();
}
}
最后的
Window
外观如下所示:
自定义WPF 窗口样式的更多相关文章
- 自定义alert窗口样式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- cesium自定义气泡窗口infoWindow
一.自定义气泡窗口与cesium默认窗口效果对比: 1.cesium点击弹出气泡窗口显示的位置固定在地图的右上角,默认效果: 2.对于习惯arcgis或者openlayer气泡窗口样式的giser来说 ...
- cesium 之自定义气泡窗口 infoWindow 篇
前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 自 ...
- WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)
WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...
- WPF 自定义的窗口拖动
WPF原有的窗口样式太丑,当我们重新定义窗口时,则需要添加一些额外的功能,如拖动~ 1.在界面上对布局元素如Grid,添加委托事件: MouseLeftButtonDown="UIEleme ...
- WPF界面设计技巧(4)—自定义列表项样式
原文:WPF界面设计技巧(4)-自定义列表项样式 有前面修改按钮样式的基础,我们可以尝试来定制一个即好看又好用的 ListBox ,今天先来讲“好看”部分. 打开 Microsoft Visual S ...
- WPF 自定义键盘焦点样式(FocusVisualStyle)
WPF 自带的键盘焦点样式是与传统控件样式搭配的,但 WPF 凭着其强大的自定义样式的能力,做出与传统控件样式完全不同风格的 UI 简直易如反掌.这时,其自带的键盘焦点样式(FocusVisualSt ...
- WPF窗口模板——Style样式
通用模板,窗口样式 <!-- 通用窗口模板 --> <ControlTemplate x:Key="CustomWindowTemplate" TargetTyp ...
- WPF 通过Win32SDK修改窗口样式
使用函数为 SetWindowLong GetWindowLong 注册函数 [DllImport("user32.dll", EntryPoint = "GetWind ...
随机推荐
- 源码笔记---MBProgressHUD
前言 作为初学者,想要快速提高自己的水平,阅读一些优秀的第三方源代码是一个非常好的途径.通过看别人的代码,可以学习不一样的编程思路,了解一些没有接触过的类和方法. MBProgressHUD是一个非常 ...
- 【例题 6-4 UVA - 11988】Broken Keyboard (a.k.a. Beiju Text)
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 会链表的插入操作的话.这个就不难了. 放置两个哨兵节点. 然后模拟插入一个节点的过程就好. 实时修改光标就好->即下一个插入的 ...
- Instruction-Set Support for Invocation of VMM-Configured Services without VMM Intervention
A processing core comprising instruction execution logic circuitry and register space. The register ...
- SoC的Testbench中的简易bus_monitor(加入print函数)
SoC的Testbench中的简易bus_monitor(加入print函数) 主要思路 向固定地址写信息 使用工具链将C写的print/printf函数编译成hex文件 在testbench中创建b ...
- [Angular] Configurable Angular Components - Content Projection and Input Templates
We are going to have a modal component: <au-modal > </au-modal> And we can pass default ...
- [Angular2 Animation] Basic animation
@Component({ selector: 'app-courses', templateUrl: './courses.component.html', styleUrls: ['./course ...
- HDU 3974 Assign the task 并查集
http://acm.hdu.edu.cn/showproblem.php?pid=3974 题目大意: 一个公司有N个员工,对于每个员工,如果他们有下属,那么他们下属的下属也是他的下属. 公司会给员 ...
- Python安装后,Python IDLE等的位置
安装后,Python的目录下有如下文件:
- 数学之路-python计算实战(7)-机器视觉-图像产生加性零均值高斯噪声
图像产生加性零均值高斯噪声.在灰度图上加上噪声,加上噪声的方式是每一个点的灰度值加上一个噪声值.噪声值的产生方式为Box-Muller算法生成高斯噪声. 在计算机模拟中,常常须要生成正态分布的数值.最 ...
- Python数据分析环境和工具
一.数据分析工作环境 Anaconda: Anaconda(水蟒)是一个科学计算软件发行版,集成了大量常用扩展包的环境,包含了 Python 解释器,conda 包管理工具,以及 NumPy.Pand ...