在 Blend 中对 WPF 应用进行皮肤化






4.87/5 (34投票s)
了解如何在 Expression Blend 中为 WPF 应用程序换肤。
引言
在本教程中,我将向您展示如何在 Expression Blend 中为 WPF 应用程序换肤。您将创建的应用程序将具有非常简单的 UI,但您将学到的概念将使您能够为具有更广泛用户界面的 WPF 应用程序换肤。
背景
换肤是指向应用程序添加功能的过程,该功能使用户能够在运行时更改其外观和感觉。为 WPF 应用程序换肤需要考虑某些因素,您将在过程中熟悉这些因素。
设计
开始换肤
- 启动 Expression Blend 并创建一个名为 Skinning 的新 WPF 项目。确保在 Language(语言)组合框中将 Language(语言)设置为 Visual Basic。
- 在 Objects and Timeline(对象和时间轴)中选择
Window
元素,然后在 Properties(属性)面板的 Layout(布局)部分,将宽度设置为 400,高度设置为 300。 - 从 Toolbox(工具箱)中选择 Rectangle(矩形)工具,并在窗口上绘制一个矩形。将矩形的 Vertical Alignment(垂直对齐)设置为 top(顶部)。
- 将矩形重命名为 Header,并为其设置蓝色渐变 Fill(填充)。将其 Stroke(笔触)设置为 No brush(无画笔)。
- 选择
Window
元素,并在 Properties(属性)面板的 Brushes(画笔)部分,将其 Background(背景)设置为渐变画笔。 - 选择 Button(按钮)工具,并在
Window
元素中绘制一个按钮。
接下来,我们将通过将当前蓝色外观更改为红色来为应用程序换肤。换肤涉及使用 ResourceDictionary
,其中包含您希望为应用程序提供的不同外观和感觉。ResourceDictionary
本质上是资源的集合。资源可以是对象的所有属性,或者像按钮这样的控件,可以是样式或模板。要使属性在添加新的 ResourceDictionary
时发生更改,必须为其分配 DynamicResource
。DynamicResource
在运行时,当您从一个 ResourceDictionary
切换到另一个时会发生更改。
如果您查看应用程序的 XAML 代码,您会注意到应用程序中元素的任何属性都没有分配 DynamicResource
。随着我们继续,这种情况将会改变。
ResourceDictionaries 和 Resources(资源字典和资源)
在接下来的步骤中,我们将创建 ResourceDictionary
和资源。
- 单击 Resources(资源)选项卡以打开 Resources(资源)面板,然后单击 Create new resource dictionary(创建新资源字典)按钮。
- 在 New Item(新项)对话框中,将新的
ResourceDictionary
的名称更改为 BlueSkin.xaml。单击 OK(确定)。BlueSkin.xaml 已添加到 Resources(资源)面板的列表中。 - 在 Objects and Timeline(对象和时间轴)中选择
Window
元素,并打开 Properties(属性)面板。 - 单击 Brushes(画笔)部分
Background
属性旁边的“Advanced options”(高级选项)按钮。从上下文菜单中选择 Convert to New Resource(转换为新资源)。这将打开 Create Brush Resource(创建画笔资源)对话框。 - 在 Create Brush Resource(创建画笔资源)对话框中,将名称更改为
WindowBackground
,并选择 Resource Dictionary(资源字典)选项按钮。确保在组合框中将 BlueSkin.xaml 作为目标。单击 OK(确定)。如果您现在切换到 XAML 视图,您会注意到Window
元素的Background
属性已分配了一个名为WindowBackground
的DynamicResource
。 - 选择我们添加的矩形。在 Properties(属性)面板中,单击
Fill
属性的 Advanced options(高级选项)按钮,然后从上下文菜单中选择 Convert to New Resource(转换为新资源)。 - 将新资源命名为 ‘
HeaderFill
’,并选择 ‘Resource dictionary’(资源字典)选项按钮。单击 OK(确定)。 - 选择 Resources(资源)选项卡以打开 Resources(资源)面板。创建一个新的 Resource Dictionary(资源字典)并将其命名为 RedSkin.xaml。
- 选择
Window
元素,并将其Background
的渐变画笔更改为红色渐变。 - 单击
Background
属性的“Advanced options”(高级选项)按钮,然后从上下文菜单中选择 Convert to new Resource(转换为新资源)。 - 在 Create Brush Resource(创建画笔资源)对话框中,将名称更改为
WindowBackground
,并选择 Resource dictionary(资源字典)选项按钮。从组合框中选择 RedSkin.xaml,然后单击 OK(确定)。忽略警告。如果您现在查看 Resources(资源)面板并展开 BlueSkin 和 RedSkin.xaml,您会注意到两个资源字典都具有同名的资源。 - 在 Objects and Timeline(对象和时间轴)中选择
Header
,并为其Fill
设置红色渐变。 - 单击
Fill
属性的“Advanced options”(高级选项)按钮,然后从上下文菜单中选择 Convert to New Resource(转换为新资源)。 - 在 ‘Create Brush Resource’(创建画笔资源)对话框中,将名称更改为
HeaderFill
,并选择 Resource dictionary(资源字典)选项按钮。从组合框中选择 RedSkin.xaml,然后单击 OK(确定)。
由于 BlueSkin
中的 WindowBackground
是 DynamicResource
,因此当切换资源字典时,它将与 RedSkin
中的 WindowBackground
交换。如果名称不同,背景颜色将不会改变,因为您的应用程序不知道应从新的资源字典中应用哪个资源。
现在我们已经完成了资源字典的创建,让我们添加必要的代码以允许我们在运行时切换到红色主题。
编码
- 选择添加到应用程序中的按钮,然后在 Properties(属性)面板中,单击 Events(事件)按钮。
- 在 Click(单击)事件文本框中,键入 ‘Btn1_Click’,然后按 Enter。Blend 的代码编辑器将打开,供您添加按钮单击事件所需的代码。
- 在
Btn1_Click
事件中,键入以下代码: - 选择 Resource(资源)面板选项卡,然后展开 App.xaml。
- 由于我们的应用程序目前链接到 RedSkin.xaml,因此我们将删除此链接。右键单击 ‘Linked To: RedSkin.xaml’,然后从上下文菜单中选择 Delete(删除)。在弹出的对话框中单击 Yes(是)。
- 运行应用程序。单击按钮。请注意,界面已更改为红色外观。
- 关闭应用程序。
Dim RedSkin as New ResourceDictionary
RedSkin.Source = New Uri("RedSkin.xaml", UriKind.Relative)
Me.Resources.MergedDictionaries.Clear()
Me.Resources.MergedDictionaries.Add(RedSkin)
上述代码创建了一个名为 RedSkin
的新资源字典,并将其源属性设置为我们之前创建的资源字典 RedSkin.xaml。然后,我们清空 MergedDictionaries
集合,并添加一个新的资源字典。MergedDictionaries
是 ResourceDictionary
对象集合。如果您的应用程序需要维护与现有资源字典的链接,请避免清空 MergedDictionaries
集合,而是使用 Add
方法,键入以下代码:
Me.Resources.MergedDictionaries(0) = RedSkin
此代码将资源字典放置在 MergedDictionaries
集合的第一个位置。
到目前为止,我们已经成功更改了应用程序中部分元素的显示效果。即使切换主题后,按钮对象的外观仍然保持不变。更改控件的外观与更改矩形或椭圆形等形状对象的外观略有不同。要更改控件对象的外观,我们必须使用样式或模板。
样式允许我们更改控件的外观,但会限制我们,因为我们无法直接访问构成控件的元素。另一方面,模板允许我们直接操作构成控件的元素。在模板中,您可以添加、删除或修改构成控件整体结构的元素。因此,在为控件换肤时,您可以选择使用样式或模板。
控件换肤
在接下来的步骤中,我们将使用模板为按钮控件换肤。
- 选择按钮对象。右键单击按钮,然后选择 Edit Template > Create Empty(编辑模板 > 创建空白)。
- 在 ‘Create ControlTemplate Resource’(创建 ControlTemplate 资源)对话框中,保留名称不变,然后选择 Resource dictionary(资源字典)选项按钮。BlueSkin.xaml 应该是组合框中的唯一值,因为我们删除了与 RedSkin 的链接。单击 OK(确定)。
- 放大空的 Control Template(控件模板)。
- 在空的网格中绘制一个矩形,然后右键单击它并选择 Auto Size > Fill(自动调整大小 > 填充)。矩形应该填充整个网格。
- 将矩形重命名为
BtnBackground
,并为其设置圆角。 - 在 Properties(属性)面板中,确保 Properties(属性)按钮处于活动状态,而不是 Events(事件)按钮。将
StrokeThickness
(笔触粗细)更改为2
,并将笔触颜色更改为白色实心画笔。 - 选择 Fill(填充),并将其更改为蓝色渐变画笔。
- 选择工具箱中的 Assets(资源)按钮,然后在 Assets(资源)窗口的 Controls(控件)部分选择
ContentPresenter
。ContentPresenter
将添加到工具箱。 - 双击工具箱中的
ContentPresenter
工具将其添加到控件的网格中。将其 Horizontal and Vertical alignments(水平和垂直对齐)设置为 center(居中)。 - 再绘制一个矩形。将其笔触设置为 No Brush(无画笔),然后调整其外观,使其看起来像下图所示。如果对齐线阻碍了您的操作,请关闭捕捉。
- 将两个渐变停止点都更改为白色,并调整最后一个渐变停止点的 alpha 属性为零。
- 在 Objects and Timeline(对象和时间轴)面板中,单击 Up One Level(上一级)按钮以返回到 Window(窗口)。
- 在选中按钮对象的情况下,将其
Content
(内容)属性更改为 ‘Red’,然后按 Enter。然后将其Foreground
(前景)属性更改为 White(白色)。保存项目。 - 在 Resources(资源)面板中,右键单击 App.xaml,然后选择 Link to ResourceDictionary > RedSkin.xaml(链接到 ResourceDictionary > RedSkin.xaml)。链接到 RedSkin 会使其在 ‘Create ControlTemplate Resource’(创建 ControlTemplate 资源)对话框的 Resource dictionary(资源字典)组合框中可见。
- 选择按钮。右键单击按钮,然后选择 Edit Template > Edit a Copy(编辑模板 > 编辑副本)。
- 在 ‘Create ControlTemplate Resource’(创建 ControlTemplate 资源)对话框中,将名称更改为
ButtonControlTemplate1
,然后选择 Resource dictionary(资源字典)选项按钮,并从组合框中选择 RedSkin.xaml。单击 OK(确定)。 - 在按钮控件模板中,将
BtnBackground
的 Fill(填充)更改为红色渐变。 - 返回主窗口。在 Resources(资源)面板的 App.xaml 部分删除到 RedSkin.xaml 的链接。在弹出的对话框中单击 Yes(是)。
- 运行项目并单击按钮。现在请注意,应用程序中的所有元素都已换肤。一点动画效果可以让我们的按钮在鼠标悬停事件中看起来更像是在点亮,但这可以在您空闲时完成。
我们无法在两个主题之间切换,这有点无趣,所以让我们启用它。
- 如果尚未停止应用程序,请停止它。
- 选择按钮对象,然后将一个新的按钮复制并粘贴到您的
Window
中。 - 将其拖动并放置在第一个按钮下方。
- 将新按钮的
Content
(内容)属性更改为 ‘Blue’。 - 选择 Properties(属性)面板的 Events(事件)按钮,然后在 Click(单击)事件文本框中键入 ‘
Btn2_Click
’。按 Enter 打开代码编辑器窗口。 - 在
Btn2_Click
事件中,键入以下代码: - 运行项目。请注意,单击这两个按钮允许您在两个主题之间切换。
Dim BlueSkin as New ResourceDictionary
BlueSkin.Source = New Uri("BlueSkin.xaml", UriKind.Relative)
Me.Resources.MergedDictionaries.Clear()
Me.Resources.MergedDictionaries.Add(BlueSkin)
保存状态
应用程序当前以默认主题 BlueSkin
打开,当您更改主题并关闭应用程序时。为了使应用程序能够以选定的主题打开,我们将使用应用程序设置。
- 在 Project(项目)面板中右键单击项目,然后从上下文菜单中选择 Edit in Visual Studio(在 Visual Studio 中编辑)。
- Visual Studio 打开后,在 Solution Explorer(解决方案资源管理器)中右键单击项目,然后从上下文菜单中选择 Properties(属性)。这将打开 Properties(属性)窗口。
- 选择 Settings(设置)选项卡。创建一个名为
SkinPath
的新设置,类型为String
,范围为 User(用户)。将 Value(值)部分留空。 - 双击 MainWindow.xaml.vb 进行编辑。
- 从 Class Name(类名)组合框中选择 ‘
MainWindow
Events’,从 Method Name(方法名)组合框中选择Loaded
。 - 在
MainWindow_Loaded
事件中,键入以下代码: - 在
Btn1_Click
事件中,添加以下代码: - 在
Btn2_Click
事件中,添加以下代码: - 运行应用程序。
- 将主题更改为红色并关闭应用程序。
- 再次运行应用程序。请注意,它现在以我们最后应用的皮肤打开。
'Check whether SkinPath has a value and create
'a resource dictionary…
If My.Settings.SkinPath <> String.Empty Then
Dim newDictionary As New ResourceDictionary()
Dim path As String
path = My.Settings.SkinPath
newDictionary.Source = New Uri(path, UriKind.Relative)
Me.Resources.MergedDictionaries.Clear()
Me.Resources.MergedDictionaries.Add(newDictionary)
End If
My.Settings.SkinPath = "\RedSkin.xaml"
My.Settings.Save()
My.Settings.SkinPath = "\BlueSkin.xaml"
My.Settings.Save()
在 MainWindow_Loaded
事件中,我们检查设置 SkinPath
是否包含值,并创建一个资源字典,其源属性分配了一个 URI 对象,该对象以设置作为资源字典的位置。在按钮单击事件中,我们将设置 SkinPath
赋为相关资源字典的位置。然后,我们通过调用 Save
方法来持久化设置。
UserControls 换肤
在换肤 UserControls 时,您可以采取几种方法。
- 在创建 UserControl 时,您可以公开构成控件的元素的属性,以便一旦将 UserControl 放置在窗口上,就可以将该属性转换为资源。为此,请使用依赖属性。
- 在创建 UserControl 时,从构成控件的元素的属性创建资源,即,当 UserControl 文件在 Blend 中打开时,选择要换肤的元素,并从感兴趣的属性创建资源。
结论
我希望本教程非常有帮助,但我无法在不考虑开发人员是否也应充当设计者的争论的情况下结束。我的想法是,既然 Blend 提供了一个戴上两顶帽子的机会,为什么不这样做呢?我个人的看法是,存在一位开发人员,他也可以成为一名优秀的设计师,反之亦然。
外部链接
历史
- 2010 年 5 月 4 日:初次发布。
- 2011 年 1 月 23 日:更新了文章文本。