WPF桌面端开发16-资源(Resources)之全局样式设置及自定义组件

前言

建议在开发前把项目用到的颜色、前景、背景、字体、转换器、公共样式都提取到公共文件中。

项目实战

全局样式引用

正确写法

不要让项目出现多层ResourceDictionary.MergedDictionaries嵌套,会出现运行的时候找不到资源的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<Application
x:Class="SchoolClient.MyApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Z.Views;assembly=Z"
StartupUri="Wins/WelcomeWin.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/ResCommonBase.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResCommonConverter.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleWin.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleButton.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleText.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleListBoxItem.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleScrollViewer.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleComboBox.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleTimePicker.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleCheckBox.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style BasedOn="{StaticResource MyButton}" TargetType="Button" />
<Style BasedOn="{StaticResource ZRoundButtonStyle}" TargetType="views:ZRoundButton" />
<Style BasedOn="{StaticResource ForScrollbar}" TargetType="ScrollBar" />
<Style BasedOn="{StaticResource ListBoxItemContainerStyle1}" TargetType="ListBoxItem" />
<Style BasedOn="{StaticResource ForScrollviewer}" TargetType="ScrollViewer" />
</ResourceDictionary>
</Application.Resources>
</Application>

错误写法

注意

下面的这种多层ResourceDictionary.MergedDictionaries的写法,会导致明明开发的时候资源都设置成全局的了,运行的时候找不到资源。

这里把引用都放在了ResCommon.xaml文件中,Application引用该文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<Application
x:Class="SchoolClient.MyApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Wins/WelcomeWin.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/ResCommon.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

ResCommon.xaml

这个文件用来引用其他文件及设置默认的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:views="clr-namespace:Z.Views;assembly=Z">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/ResCommonBase.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResCommonConverter.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleWin.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleZRoundButton.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleButton.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleText.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleListBoxItem.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleScrollViewer.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleComboBox.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleTimePicker.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/ResStyleCheckBox.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style BasedOn="{StaticResource MyButton}" TargetType="Button" />
<Style BasedOn="{StaticResource ZRoundButtonStyle}" TargetType="views:ZRoundButton" />
<Style BasedOn="{StaticResource ForScrollbar}" TargetType="ScrollBar" />
<Style BasedOn="{StaticResource ListBoxItemContainerStyle1}" TargetType="ListBoxItem" />
<Style BasedOn="{StaticResource ForScrollviewer}" TargetType="ScrollViewer" />
</ResourceDictionary>

全局变量

定义

我们可以定义一些全局使用的颜色,字体等

ResCommonBase.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<!-- 定义主题色 -->
<Color x:Key="PrimaryColor">#339DFF</Color>
<!-- 定义前景背景 -->
<SolidColorBrush x:Key="PrimaryColorBrush" Color="{StaticResource PrimaryColor}" />
<!-- 定义字体大小 -->
<system:Double x:Key="FontSizeNomal">16</system:Double>
<system:Double x:Key="FontSizeTitle">18</system:Double>
<system:Double x:Key="FontSizeInfo">14</system:Double>
</ResourceDictionary>

字体

1
<FontFamily x:Key="UnidreamLed">pack://application:,,,/Font/#UniDreamLED</FontFamily>

使用

1
2
3
4
5
6
7
<TextBlock
Name="TimeTb"
VerticalAlignment="Center"
FontFamily="{StaticResource UnidreamLed}"
FontSize="16"
Foreground="#FF001E"
Text="00:00" />

使用

前景背景

1
2
3
4
5
6
<TextBlock
Margin="6,0,0,0"
VerticalAlignment="Center"
FontSize="14"
Foreground="{StaticResource PrimaryColorBrush}"
Text="还原" />

字体

1
<Setter Property="FontSize" Value="{StaticResource FontSizeNomal}" />

下面的写法是错误的,在开发时不报错运行时会报错。

1
<Setter Property="Background" Value="{StaticResource PrimaryColor}" />

全局转换器

ResCommonConverter.xaml

1
2
3
4
5
6
7
8
9
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Z.Converters;assembly=Z"
xmlns:zConverter="clr-namespace:ZConverter">
<converters:ZGenericTypeConverter x:Key="ZGenericTypeConverter" />
<zConverter:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />
<zConverter:TongjiBgConverter x:Key="BgConv" />
</ResourceDictionary>

组件样式

ResStyleWin.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ZWinStyle" TargetType="Window">
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome
CaptionHeight="0"
CornerRadius="0"
GlassFrameThickness="-1"
NonClientFrameEdges="None"
ResizeBorderThickness="0"
UseAeroCaptionButtons="False" />
</Setter.Value>
</Setter>

<Style.Triggers>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="BorderThickness" Value="6" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

ResStyleButton.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Z.Views;assembly=Z">
<Style
x:Key="BtnBgBlue"
BasedOn="{StaticResource ZRoundButtonStyle}"
TargetType="{x:Type views:ZRoundButton}">
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="BorderRadius" Value="8" />
<Setter Property="ClipToBounds" Value="True" />
<Setter Property="Background" Value="{StaticResource PrimaryColor}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Width" Value="138" />
<Setter Property="Height" Value="36" />
<Setter Property="FontSize" Value="{StaticResource FontSizeNomal}" />
</Style>
</ResourceDictionary>

概念

样式生效范围

根据生效范围从大到小依次为

  • Application.Resources(所有页面生效)
  • Window.Resources(当前页面生效)
  • 组件内设置(当前组件生效)

Resources

1
2
3
4
5
6
7
8
9
<Window.Resources>
<Style x:Key="BigFontButtonStyle">
<Setter Property="Control.FontSize" Value="18"/>
<Setter Property="Control.FontWeight" Value="Bold"/>
</Style>
</Window.Resources>
<Grid >
<Button Style="{StaticResource BigFontButtonStyle}" />
</Grid>

或者

1
2
3
4
5
6
<Window.Resources>
<Style x:Key="BigFontButtonStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Window.Resources>

组件内设置

1
2
3
4
5
6
7
<TextBlock Text="组件样式">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.FontSize" Value="36" />
</Style>
</TextBlock.Style>
</TextBlock>

全部生效和引用生效

全部生效

1
2
3
4
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="30" />
<Setter Property="FontWeight" Value="Bold" />
</Style>

注意

只要不设置x:Key,那么这个样式就会对所生效范围的所有组件生效,要只对某个生效就要设置x:Key

全部生效的尽量不要设置太基础的组件,比如如果设置了TextBlock,所有的Button的文字样式也会变化。

设置x:Key后必须引用才能生效

1
2
3
4
<Style x:Key="ZTextBlock" TargetType="TextBlock">
<Setter Property="FontSize" Value="30" />
<Setter Property="FontWeight" Value="Bold" />
</Style>

引用

1
2
3
4
<TextBlock
Margin="10"
Style="{StaticResource MTextBlock}"
Text="123" />

样式和模板

设置样式的时候我们可以设置样式和模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<Style
x:Key="SimpleButton"
BasedOn="{x:Null}"
TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource NormalBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="Grid">
<Border
x:Name="Border"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" />
<ContentPresenter
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

自定义组件

项目下添加Resources文件夹,添加以下文件

简单的按钮示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<Window.Resources>
<Style
x:Key="JianButton"
BasedOn="{x:Null}"
TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
Name="ZBorder"
Width="34"
Height="34"
BorderBrush="#2D8CF0"
BorderThickness="1"
CornerRadius="17">
<Image
Width="14"
Height="14"
Source="/Images/Login/jianhao.png" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ZBorder" Property="Background" Value="#E2F0FF" />
</Trigger>

<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ZBorder" Property="Background" Value="#bdddff" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>

使用

1
<Button Style="{StaticResource JianButton}" />

自定义TextBox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<Style x:Key="ZRoundTextBox"
TargetType="{x:Type TextBox}">

<Setter Property="BorderThickness"
Value="1">
</Setter>

<Setter Property="BorderBrush"
Value="#CED9F2">
</Setter>
<Setter Property="MaxLines"
Value="1">
</Setter>
<Setter Property="FontSize"
Value="16" />
<Setter Property="Foreground"
Value="#333333" />

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border CornerRadius="8"
Height="{TemplateBinding Height}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ScrollViewer x:Name="PART_ContentHost"
VerticalContentAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

输入的地方使用:

1
<ScrollViewer x:Name="PART_ContentHost" VerticalContentAlignment="Center" />

它负责显示 TextBox 的内容。这样可以确保在应用自定义样式后,仍然能够输入和显示文本值。

自定义ListBoxItem

/Resources/StyleListBoxItem.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ListBoxItemContainerStyle1" TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border
Name="Border"
Padding="0,0,0,0"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<ControlTemplate x:Key="ListBoxTemplate" TargetType="{x:Type ListBox}">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<WrapPanel
IsItemsHost="True"
Orientation="Horizontal"
ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</ControlTemplate>
</ResourceDictionary>

/Resources/OverwrideDefaultControlStyles.xaml

1
2
3
4
5
6
7
8
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/StyleListBoxItem.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style BasedOn="{StaticResource ListBoxItemContainerStyle1}" TargetType="ListBoxItem" />
</ResourceDictionary>

使用

1
2
3
4
5
6
7
8
9
<ListBox
x:Name="toolbar_list"
Margin="10,0,10,0"
Background="#f3f3f3"
BorderThickness="0"
ItemContainerStyle="{StaticResource ListBoxItemContainerStyle1}"
ItemTemplate="{StaticResource UserItemTemp}"
ItemsSource="{Binding userList}"
Template="{StaticResource ListBoxTemplate}" />

报错

System.Windows.Data Error: 4 : Cannot find source for binding with reference ‘RelativeSource FindAncestor, AncestorType=’System.Windows.Controls.ItemsControl’, AncestorLevel=’1’’. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is ‘ComboBoxItem’ (Name=’’); target property is ‘HorizontalContentAlignment’ (type ‘HorizontalAlignment’)

原因是ListBoxItem必须包含下面的两个属性

1
2
3
4
5
6
7

<Style
x:Key="ZItemContainerStyle"
TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top"/>
</Style>

and use it:

1
<ListView ItemContainerStyle="{StaticResource ZItemContainerStyle}"/>

内部裁剪的Border

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ZJClassTool.Views
{
public class ZClippingBorder : Border
{
private object _oldClip;

protected override void OnRender(DrawingContext dc)
{
OnApplyChildClip();
base.OnRender(dc);
}

public override UIElement Child
{
get => base.Child;
set
{
if (Child != value)
{
if (Child != null)
{
Child.SetValue(ClipProperty, _oldClip);
}

if (value != null)
{
_oldClip = value.ReadLocalValue(ClipProperty);
}
else
{
// If we dont set it to null we could leak a Geometry object
_oldClip = null;
}

base.Child = value;
}
}
}

protected virtual void OnApplyChildClip()
{
UIElement child = Child;
if (child != null)
{
double top = Math.Max(CornerRadius.TopLeft, CornerRadius.TopRight);
double bottom = Math.Max(CornerRadius.BottomLeft, CornerRadius.BottomRight);
double max = Math.Max(top, bottom);
Size size = RenderSize;
double width = size.Width - (BorderThickness.Left + BorderThickness.Right);
double height = size.Height - (BorderThickness.Top + BorderThickness.Bottom);
Geometry result = new RectangleGeometry(new Rect(0, 0, width, height), max, max);
double halfWidth = width / 2;
double halfHeight = height / 2;

if (CornerRadius.TopLeft == 0)
{
result = new CombinedGeometry(
GeometryCombineMode.Union,
result,
new RectangleGeometry(new Rect(0, 0, halfWidth, halfHeight))
);
}

if (CornerRadius.TopRight == 0)
{
result = new CombinedGeometry(GeometryCombineMode.Union, result, new RectangleGeometry
(new Rect(halfWidth, 0, halfWidth, halfHeight)));
}

if (CornerRadius.BottomLeft == 0)
{
result = new CombinedGeometry
(GeometryCombineMode.Union, result, new RectangleGeometry
(new Rect(0, halfHeight, halfWidth, halfHeight)));
}
if (CornerRadius.BottomRight == 0)
{
result = new CombinedGeometry
(
GeometryCombineMode.Union,
result,
new RectangleGeometry(new Rect(halfWidth, halfHeight, halfWidth, halfHeight))
);
}
child.Clip = result;
}
}
}
}

调用方式

引用

1
xmlns:util="clr-namespace:ZJClassTool.Utils"

使用

1
2
3
4
5
6
<Views:ZClippingBorder
Background="#fafafa"
BorderBrush="#409DFE"
BorderThickness="2"
CornerRadius="10">
</Views:ZClippingBorder>

自定义滚动条

/Resources/StyleScrolllview.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- ScrollViewer 滚动条 -->
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid>
<Rectangle
Fill="#90000000"
RadiusX="5"
RadiusY="5" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="HorizontalScrollBarPageButton" TargetType="{x:Type RepeatButton}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="VerticalScrollBarPageButton" TargetType="{x:Type RepeatButton}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="for_scrollbar" TargetType="{x:Type ScrollBar}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="Stylus.IsFlicksEnabled" Value="false" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Margin" Value="0,1,1,6" />
<Setter Property="Width" Value="10" />
<Setter Property="MinWidth" Value="10" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="Bg" SnapsToDevicePixels="true">
<Track
x:Name="PART_Track"
IsDirectionReversed="true"
IsEnabled="{TemplateBinding IsMouseOver}">
<Track.DecreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource VerticalScrollBarPageButton}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource VerticalScrollBarPageButton}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}" />
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Margin" Value="1,0,6,1" />
<Setter Property="Height" Value="10" />
<Setter Property="MinHeight" Value="10" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="Bg" SnapsToDevicePixels="true">
<Track x:Name="PART_Track" IsEnabled="{TemplateBinding IsMouseOver}">
<Track.DecreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageLeftCommand}" Style="{StaticResource HorizontalScrollBarPageButton}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageRightCommand}" Style="{StaticResource HorizontalScrollBarPageButton}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}" />
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>

<!-- ScrollViewer -->
<Style x:Key="for_scrollviewer" TargetType="{x:Type ScrollViewer}">
<Setter Property="BorderBrush" Value="LightGray" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<Grid Background="{TemplateBinding Background}">
<ScrollContentPresenter
Margin="{TemplateBinding Padding}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Cursor="{TemplateBinding Cursor}" />
<ScrollBar
x:Name="PART_VerticalScrollBar"
HorizontalAlignment="Right"
Maximum="{TemplateBinding ScrollableHeight}"
Orientation="Vertical"
Style="{StaticResource for_scrollbar}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{TemplateBinding VerticalOffset}" />
<ScrollBar
x:Name="PART_HorizontalScrollBar"
VerticalAlignment="Bottom"
Maximum="{TemplateBinding ScrollableWidth}"
Orientation="Horizontal"
Style="{StaticResource for_scrollbar}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Value="{TemplateBinding HorizontalOffset}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ScrollChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_VerticalScrollBar"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:1" />
<DoubleAnimation
BeginTime="0:0:1"
Storyboard.TargetName="PART_VerticalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:1" />
<DoubleAnimation
Storyboard.TargetName="PART_HorizontalScrollBar"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:1" />
<DoubleAnimation
BeginTime="0:0:1"
Storyboard.TargetName="PART_HorizontalScrollBar"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

全局样式设置

/Resources/OverwrideDefaultControlStyles.xaml

1
2
3
4
5
6
7
8
9
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/StyleScrolllview.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style BasedOn="{StaticResource for_scrollbar}" TargetType="ScrollBar" />
<Style BasedOn="{StaticResource for_scrollviewer}" TargetType="ScrollViewer" />
</ResourceDictionary>

自定义ComboBox

/Resources/StyleComboBox.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- ComBoBox项鼠标经过背景色 -->
<SolidColorBrush x:Key="ComboBoxMouseOverBackground" Color="#f0f0f0" />
<!-- Combox右侧下拉按钮 -->
<Style x:Key="ComboxStyleBtn" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- 下拉按钮内部背景色 -->
<Border
x:Name="Back"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1">
<!-- 下拉按钮内边框 -->
<Label
Name="PathFill"
Height="30"
HorizontalAlignment="Right"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Content="ˇ"
FontSize="30" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Back" Property="Background" Value="Transparent" />
<Setter TargetName="Back" Property="BorderBrush" Value="Transparent" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Combox -->
<Style x:Key="ComboBoxStyle" TargetType="ComboBox">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<!-- ComBoxItem -->
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="MinHeight" Value="32" />
<Setter Property="MinWidth" Value="60" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border
Name="_back"
Background="Transparent"
BorderBrush="#f3f3f3"
BorderThickness="0,0,0,0">
<ContentPresenter Margin="10,0,10,0" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="_back" Property="Background" Value="{StaticResource ComboBoxMouseOverBackground}" />
</Trigger>
<!-- 下拉框背景色 -->
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="_back" Property="Background" Value="{StaticResource ComboBoxMouseOverBackground}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<!-- 文字区域背景和边线样式 -->
<TextBox
Grid.Column="0"
Padding="10,0,0,0"
VerticalAlignment="Center"
Background="Transparent"
BorderBrush="#c0c0c0"
BorderThickness="0"
Foreground="Black"
IsReadOnly="{TemplateBinding IsReadOnly}"
Text="{TemplateBinding Text}" />
<Border
Grid.Column="0"
BorderBrush="#c0c0c0"
BorderThickness="0.6"
CornerRadius="1,0,0,1" />
<!-- 右侧下拉button设置 -->
<Border BorderThickness="0">
<ToggleButton
BorderBrush="Black"
BorderThickness="3"
ClickMode="Press"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ComboxStyleBtn}" />
</Border>
<!-- 弹出popup整体设置 -->
<Popup
x:Name="Popup"
AllowsTransparency="True"
Focusable="False"
IsOpen="{TemplateBinding IsDropDownOpen}"
Placement="Bottom"
PopupAnimation="Slide">
<Grid
x:Name="DropDown"
Width="{TemplateBinding ActualWidth}"
MaxHeight="200"
SnapsToDevicePixels="True">
<Border
x:Name="DropDownBorder"
BorderBrush="#e8e8e8"
BorderThickness="1,0,1,1" />
<ScrollViewer
Margin="1"
CanContentScroll="True"
HorizontalScrollBarVisibility="Disabled"
SnapsToDevicePixels="True"
VerticalScrollBarVisibility="Auto">
<!-- StackPanel 用于显示子级,方法是将 IsItemsHost 设置为 True -->
<StackPanel
Background="White"
IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
<ComboBox
x:Name="cmbClass"
Grid.Row="0"
Padding="10,0,10,0"
VerticalContentAlignment="Center"
BorderThickness="1"
Cursor="Hand"
DisplayMemberPath="Value"
FontSize="16"
ItemsSource="{Binding bookList}"
SelectedValuePath="Key"
SelectionChanged="cmbClass_SelectionChanged"
Style="{StaticResource ComboBoxStyle}" />

问题

样式不生效

我们知道设置启动页有两种方式

  • 添加窗口 生成操作 设置为 ApplicationDefinition
  • 添加cs代码文件,Main方法中运行窗口

但是用以下代码的方式创建窗口,设置的全局样式是不生效的,只有通过StartupUri="Wins/Welcome.xaml"设置启动页才能使全局样式生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public partial class MyApp : Application
{
public static LoadDialog myloading;

[STAThread]
private static void Main()
{
StopSameProcess();
new MyApp();
}

public MyApp()
{
Run(new LoginWindow());
}
}

但是通过StartupUri只能设置唯一的启动页,怎么办呢?

我们可以添加一个欢迎页面作为中间页面,这个页面再决定跳转到那个页面,这样设置的全局样式就生效了。