基于.net8 的 WPF 中使用 MaterialDesignThemes 库及相关工具实践

在 WPF 应用开发领域,追求美观且现代化的用户界面是开发者们的共同目标,而 Material Design 风格凭借简洁、直观的视觉语言与流畅的交互体验,成为众多开发者的首选。借助 MaterialDesignThemes 库,我们能够轻松将这种优雅的设计风格融入 WPF 应用中。本文将基于.net8 框架,深度结合 MaterialDesignInXAML 3 和 Community Toolkit8.4,详细介绍如何在 WPF 项目中应用这些强大的库,并使用<WindowChrome.WindowChrome>实现高度自定义的标题栏。

项目创建与库安装

1. 创建.net8 WPF 项目

打开 Visual Studio 开发环境,在起始页点击 “创建新项目”。在项目模板搜索框中输入 “WPF”,选择 “WPF 应用程序(.NET)” 模板,点击 “下一步”。在配置新项目界面,填写项目名称(如 “WpfMaterialDesignDemo”)、选择合适的存储位置和解决方案名称,同时在 “框架” 下拉框中选择 “.NET 8.0”,最后点击 “创建”,即可完成基于.net8 的 WPF 项目搭建。

2. 安装 MaterialDesignThemes 3 和 CommunityToolkit.Wpf 8.4

项目创建完成后,在 “解决方案资源管理器” 中右键点击项目名称,选择 “管理 NuGet 程序包”。在 NuGet 包管理器界面,切换到 “浏览” 选项卡,分别搜索 “MaterialDesignThemes” 和 “CommunityToolkit.Wpf”。找到对应版本(MaterialDesignThemes 3 和 CommunityToolkit.Wpf 8.4)后,点击 “安装” 按钮。安装过程中,NuGet 会自动处理依赖关系,将所需的库文件添加到项目中,为后续开发做好准备。

引入资源

在 WPF 应用中,资源的引入是决定界面风格的关键步骤。在App.xaml文件中,<Application.Resources>标签是存放全局资源的核心区域,我们通过合并资源字典的方式引入 Material Design 相关资源。

<Application.Resources>

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<!-- 引入Material Design 3 Light主题 -->

<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Light.xaml" />

<!-- 引入默认设置 -->

<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />

<!-- 引入推荐主色(深紫色) -->

<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />

<!-- 引入推荐强调色(在3.0版本中Accent已变更为Secondary,这里使用酸橙色) -->

<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Secondary/MaterialDesignColor.Lime.xaml" />

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</Application.Resources>

具体来说,MaterialDesign3.Light.xaml定义了 Light 主题下的样式规则,包括按钮、文本框、菜单等控件的外观;MaterialDesignTheme.Defaults.xaml提供了默认的配置和基础样式,确保各组件的一致性;而主色和强调色资源字典则分别决定了应用的主要色调和突出元素的颜色。通过这种资源合并机制,整个应用将统一遵循 Material Design 规范,呈现出协调美观的视觉效果。开发者还可以根据实际需求,替换不同的主题和配色方案,如切换到 Dark 主题或选择其他颜色组合,只需修改资源字典的引用即可轻松实现。

使用 Community Toolkit 8.4 简化开发

Community Toolkit 8.4 是一款功能丰富的 WPF 开发辅助工具集,它包含了大量实用的组件和特性,能够显著提升开发效率。除了通过ObservableObject和ICommand实现简单的数据绑定和命令处理外,还有许多值得探索的应用场景。

1. 常用附加属性

例如,CommunityToolkit.Wpf提供的Visibility="{Binding IsSomeConditionMet, Converter={StaticResource BoolToVisibilityConverter}}"附加属性,可方便地根据视图模型中的布尔属性控制控件的可见性。假设在一个订单管理界面中,存在 “取消订单” 按钮,只有当订单处于可取消状态(通过视图模型的IsOrderCancelable属性表示)时,该按钮才应显示,使用此附加属性就能轻松实现这一逻辑,避免在代码背后文件中编写繁琐的可见性控制代码。

2. 数据转换器

数据转换器也是其强大功能之一。除了基本的数据类型转换,还可以实现复杂的业务逻辑转换。比如,在显示日期时,我们可能需要将数据库中的日期格式(如 “yyyy-MM-dd HH:mm:ss”)转换为用户友好的格式(如 “yyyy 年 MM 月 dd 日”)。通过自定义实现IValueConverter接口的转换器类,并在 XAML 中进行注册和绑定,即可轻松完成这一转换过程,使数据展示更加符合用户习惯。

3. 示例扩展

回到显示当前时间的示例,我们可以进一步扩展其功能。在视图模型中添加一个IsAutoUpdate属性,用于控制时间是否自动更新。同时,添加一个切换自动更新状态的命令ToggleAutoUpdateCommand:

using CommunityToolkit.Mvvm.ComponentModel;

using CommunityToolkit.Mvvm.Input;

using System;

using System.Collections.ObjectModel;

using System.Windows.Input;

namespace WpfMaterialDesignDemo.ViewModels

{

public partial class MainViewModel : ObservableObject

{

[ObservableProperty]

private string currentTime = DateTime.Now.ToString("HH:mm:ss");

[ObservableProperty]

private bool isAutoUpdate = true;

[RelayCommand]

private void UpdateTime()

{

CurrentTime = DateTime.Now.ToString("HH:mm:ss");

}

[RelayCommand]

private void ToggleAutoUpdate()

{

IsAutoUpdate =!IsAutoUpdate;

if (IsAutoUpdate)

{

// 启动定时器自动更新时间

System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();

timer.Interval = TimeSpan.FromSeconds(1);

timer.Tick += (sender, e) => UpdateTime();

timer.Start();

}

}

}

}

在视图(MainWindow.xaml)中,新增一个按钮用于切换自动更新状态:


<Window x:Class="WpfMaterialDesignDemo.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:WpfMaterialDesignDemo"

xmlns:viewModels="clr-namespace:WpfMaterialDesignDemo.ViewModels"

mc:Ignorable="d"

Title="Material Design WPF App" Height="450" Width="800">

<Window.DataContext>

<viewModels:MainViewModel/>

</Window.DataContext>

<Grid>

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

<TextBlock Text="{Binding CurrentTime}" FontSize="24" Margin="10"/>

<Button Content="Update Time" Command="{Binding UpdateTimeCommand}" Margin="10"/>

<Button Content="Toggle Auto Update" Command="{Binding ToggleAutoUpdateCommand}" Margin="10"/>

</StackPanel>

</Grid>

</Window>

通过这样的扩展,我们充分展示了 Community Toolkit 8.4 在复杂业务逻辑处理和交互功能实现上的便捷性。

自定义标题栏

使用<WindowChrome.WindowChrome>可以彻底改变 WPF 窗口标题栏的默认样式,打造个性化的界面风格。在MainWindow.xaml中,我们首先通过<Window.WindowChrome>标签配置标题栏的基本属性:

<Window x:Class="WpfMaterialDesignDemo.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:WpfMaterialDesignDemo"

xmlns:viewModels="clr-namespace:WpfMaterialDesignDemo.ViewModels"

mc:Ignorable="d"

Title="Material Design WPF App" Height="450" Width="800">

<Window.WindowChrome>

<WindowChrome

CaptionHeight="40"

GlassFrameThickness="1"

NonClientFrameEdges="Left,Bottom,Right"

ResizeBorderThickness="5"

UseAeroCaptionButtons="False" />

</Window.WindowChrome>

<!-- 定义标题区域与客户区域 -->

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="40"/> <!-- 标题栏高度 -->

<RowDefinition Height="*"/> <!-- 客户区域,占据剩余空间 -->

</Grid.RowDefinitions>

<!-- 标题栏内容 -->

<Border Grid.Row="0" Background="LightGray">

<TextBlock Text="自定义标题" HorizontalAlignment="Center" VerticalAlignment="Center"/>

</Border>

<!-- 客户区域内容 -->

<Grid Grid.Row="1">

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

<TextBlock Text="{Binding CurrentTime}" FontSize="24" Margin="10"/>

<Button Content="Update Time" Command="{Binding UpdateTimeCommand}" Margin="10"/>

</StackPanel>

</Grid>

</Grid>

</Window>

其中,CaptionHeight属性决定了标题栏的垂直高度,设置为 40 能为标题和按钮等元素提供合适的显示空间;GlassFrameThickness用于控制玻璃边框的厚度,值为 1 时会呈现出微妙的玻璃质感效果;NonClientFrameEdges明确了非客户端框架的边缘范围,这里设置为左侧、底部和右侧,使窗口看起来更具现代感;ResizeBorderThickness指定了窗口可调整边框的厚度,方便用户通过鼠标拖拽调整窗口大小;UseAeroCaptionButtons="False"则禁用了 Windows 默认的 Aero 风格标题栏按钮,为我们自定义按钮留出空间。

1. 优化标题栏布局

为了使标题栏更加美观和实用,我们可以进一步优化其布局。添加窗口标题显示和控制按钮,使用Grid和StackPanel进行布局管理:

<Window x:Class="WpfMaterialDesignDemo.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:WpfMaterialDesignDemo"

xmlns:viewModels="clr-namespace:WpfMaterialDesignDemo.ViewModels"

xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"

mc:Ignorable="d"

Title="Material Design WPF App" Height="450" Width="800">

<Window.WindowChrome>

<WindowChrome

CaptionHeight="40"

GlassFrameThickness="1"

NonClientFrameEdges="Left,Bottom,Right"

ResizeBorderThickness="5"

UseAeroCaptionButtons="False" />

</Window.WindowChrome>

<!-- 定义标题区域与客户区域 -->

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="40"/> <!-- 标题栏高度 -->

<RowDefinition Height="*"/> <!-- 客户区域,占据剩余空间 -->

</Grid.RowDefinitions>

<!-- 标题栏内容 -->

<Border Grid.Row="0" Background="{DynamicResource PrimaryColor}" Height="40">

<Grid>

<!-- 窗口标题 -->

<TextBlock Text="{Binding Title}"

HorizontalAlignment="Left"

VerticalAlignment="Center"

Margin="10,0,0,0"

FontSize="14"

Foreground="{DynamicResource PrimaryForegroundColor}"/>

<!-- 窗口控制按钮 -->

<StackPanel Orientation="Horizontal"

HorizontalAlignment="Right"

VerticalAlignment="Center">

<!-- 最小化按钮 -->

<Button Command="{Binding MinimizeCommand}"

Style="{StaticResource MaterialDesignToolButton}"

ToolTip="最小化"

Width="45" Height="40">

<materialDesign:PackIcon Kind="WindowMinimize"

Foreground="{DynamicResource PrimaryForegroundColor}"/>

</Button>

<!-- 最大化/还原按钮 -->

<Button Command="{Binding MaximizeCommand}"

Style="{StaticResource MaterialDesignToolButton}"

ToolTip="{Binding IsWindowMaximized, Converter={StaticResource BoolToToolTipConverter}}"

Width="45" Height="40">

<materialDesign:PackIcon Kind="{Binding IsWindowMaximized, Converter={StaticResource BoolToIconKindConverter}}"

Foreground="{DynamicResource PrimaryForegroundColor}"/>

</Button>

<!-- 关闭按钮 -->

<Button Command="{Binding CloseCommand}"

Style="{StaticResource MaterialDesignToolButton}"

ToolTip="关闭"

Width="45" Height="40">

<materialDesign:PackIcon Kind="WindowClose"

Foreground="{DynamicResource PrimaryForegroundColor}"/>

</Button>

</StackPanel>

</Grid>

</Border>

<!-- 客户区域内容 -->

<Grid Grid.Row="1">

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

<TextBlock Text="{Binding CurrentTime}" FontSize="24" Margin="10"/>

<Button Content="Update Time" Command="{Binding UpdateTimeCommand}" Margin="10"/>

</StackPanel>

</Grid>

</Grid>

</Window>

在上述代码中,窗口标题通过数据绑定显示视图模型中的Title属性;三个控制按钮(最小化、最大化 / 还原、关闭)使用了 Material Design 内置的PackIcon图标,并绑定到视图模型中的对应命令。同时,按钮样式采用了MaterialDesignToolButton,确保与整体 Material Design 风格一致。

2. 视图模型实现

为了使按钮功能正常工作,我们需要在视图模型中定义相应的命令和属性:

using CommunityToolkit.Mvvm.ComponentModel;

using CommunityToolkit.Mvvm.Input;

using System;

using System.Collections.ObjectModel;

using System.Windows;

using System.Windows.Input;

namespace WpfMaterialDesignDemo.ViewModels

{

public partial class MainViewModel : ObservableObject

{

[ObservableProperty]

private string currentTime = DateTime.Now.ToString("HH:mm:ss");

[ObservableProperty]

private bool isWindowMaximized;

// 窗口标题

public string Title { get; set; } = "Material Design WPF App";

// 最小化命令

public ICommand MinimizeCommand { get; }

// 最大化/还原命令

public ICommand MaximizeCommand { get; }

// 关闭命令

public ICommand CloseCommand { get; }

public MainViewModel()

{

// 初始化命令

MinimizeCommand = new RelayCommand(MinimizeWindow);

MaximizeCommand = new RelayCommand(ToggleMaximizeWindow);

CloseCommand = new RelayCommand(CloseWindow);

}

private void MinimizeWindow()

{

Application.Current.MainWindow.WindowState = WindowState.Minimized;

}

private void ToggleMaximizeWindow()

{

Application.Current.MainWindow.WindowState =

Application.Current.MainWindow.WindowState == WindowState.Maximized

? WindowState.Normal

: WindowState.Maximized;

IsWindowMaximized = Application.Current.MainWindow.WindowState == WindowState.Maximized;

}

private void CloseWindow()

{

Application.Current.MainWindow.Close();

}

[RelayCommand]

private void UpdateTime()

{

CurrentTime = DateTime.Now.ToString("HH:mm:ss");

}

}

}

此外,还需要创建两个转换器类,用于处理最大化 / 还原按钮的图标和工具提示切换:

using System;
using System.Globalization;
using System.Windows.Data;
using MaterialDesignThemes.Wpf;

namespace WpfMaterialDesignDemo.Converters
{
    // 布尔值到图标类型的转换器
    public class BoolToIconKindConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool isMaximized && isMaximized)
                return PackIconKind.WindowRestore;
            return PackIconKind.WindowMaximize;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    
    // 布尔值到工具提示的转换器
    public class BoolToToolTipConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool isMaximized && isMaximized)
                return "还原";
            return "最大化";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

App.xaml中注册这些转换器:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <!-- 已有资源保持不变 -->
        </ResourceDictionary.MergedDictionaries>
        
        <!-- 注册转换器 -->
        <converters:BoolToIconKindConverter x:Key="BoolToIconKindConverter"/>
        <converters:BoolToToolTipConverter x:Key="BoolToToolTipConverter"/>
    </ResourceDictionary>
</Application.Resources>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值