wpf集成MaterialDesignThemes

介绍

MaterialDesignThemes 是一个基于 WPF(Windows Presentation Foundation) 的开源 UI 框架,用于实现现代化的 Material Design 风格界面。它提供了丰富的控件、样式、主题和动画,帮助开发者快速构建美观、一致的桌面应用程序。

官网:https://materialdesigninxaml.net/

核心功能与特性

  1. 预定义控件库
    包含 Material Design 规范中的常用控件,例如:
  • 按钮(ButtonFlatButtonRaisedButton
  • 输入框(TextBoxPasswordBoxTextBoxHelper
  • 导航控件(NavigationViewTabControl
  • 布局控件(CardGridStackPanel
  • 通知控件(SnackbarDialog
  • 数据控件(DataGridListView
  • 图标支持(集成 Material Design Icons 字体库,可通过 PackIcon 控件调用)。
  1. 主题与样式系统
  • 内置主题:支持浅色(Light)、深色(Dark)和高对比度(High Contrast)主题,可通过修改资源字典快速切换。
<!-- 浅色主题 -->
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Light.xaml" />
<!-- 深色主题 -->
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Dark.xaml" />
  • 自定义主题:可通过修改颜色值(如主色 PrimaryColor、强调色 AccentColor)来自定义主题风格。
  1. 动画与过渡效果
    提供多种 Material Design 风格的动画,例如:
  • 按钮点击涟漪效果(Ripple Effect)
  • 控件淡入淡出、展开收缩过渡(如 TransitioningContent 控件的 OpeningEffect ClosingEffect)。注意****:在较新版本中,部分过渡效果可能调整了名称或参数,建议参考 官方文档 确认用法。
  1. 行为与附加属性
  • 内置 MaterialDesignXamlBehaviors 库,支持手势操作、数据绑定增强等功能。
  • 附加属性简化控件配置,例如:
<Button md:ButtonAssist.Pressable="True" /> <!-- 启用按钮按压反馈 -->
<TextBox md:TextBoxHelper.PasswordChar="•" /> <!-- 密码框掩码字符 -->

下载资源包

全局配置应用资源

打开App.xaml文件,添加Material Design的资源字典,通过全局资源字典将Material Design风格应用到整个应用程序。

  1. 先在头部引入Material Design的命名空间
 xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
  1. 在Application.Resources中配置资源字典
 <Application.Resources>
     <!-- Material Design 基础资源 -->
     <ResourceDictionary>
         <ResourceDictionary.MergedDictionaries>
             <materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
             <!-- 必需的 Material Design 资源 -->
             <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
         </ResourceDictionary.MergedDictionaries>
     </ResourceDictionary>
 </Application.Resources>

注意:在老版本Material Design中ResourceDictionary的Source用下面这个也是可以的。但是新版本不能用,否则会报错:找不到资源“themes/materialdesigntheme.defaults.xaml****”

 <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
  • BaseTheme:基础主题(DarkLight),控制整体明暗风格(背景、字体颜色等)。
  • PrimaryColor:主色(如界面主色调、按钮默认颜色)。
  • SecondaryColor:辅助色(如按钮悬停 / 选中颜色、强调色)。

局部配置应用资源

如果不想全局应用Material Design库,想要混合使用原生控件和Material Design控件,可以在需要的窗口中配置Material Design资源字典。只需要把对应的配置放在Window.Resources标签里即可。

 xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
            <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

常用主题资源路径

  1. 基础默认值(必须引用)
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
  1. 应用的主题样式:浅色主题还是深色主题
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Dark.xaml" />

原生控件自动应用Material Design样式

由于MaterialDesign3.Defaults.xaml已被加载,所有原生的WPF控件都会自动应用Material Design样式。

 <Grid>
     <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
         <Button Content="Button" Width="100" Height="30"/>
     </StackPanel>
 </Grid>

附加属性

  1. 浮动标签:类似于提示文本,未输入时:提示文本显示在输入框内部,作为占位符。输入内容时:提示文本平滑上浮到输入框顶部,并缩小字体,成为一个标签。输入框有内容但失去焦点时:提示文本保持在顶部。
 xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
<TextBox Margin="20,0" Width="200" md:HintAssist.Hint="请输入账号"/>

未输入时:

输入内容时:

常用的HintAssist属性

属性名 作用
HintAssist.Hint 设置提示文本内容。
HintAssist.Foreground 设置提示文本的颜色(默认使用主题色)。
HintAssist.IsFloating 控制是否启用浮动效果(默认为True)。
HintAssist.HintOpacity 设置提示文本的透明度(输入时会自动调整)。
HintAssist.UseFloatingHintWhenNotEmpty 当输入框有内容但失去焦点时,是否保持浮动标签效果(默认为 True)。
  1. 清除按钮md:TextFieldAssist.HasClearButton="True" 是 MaterialDesignInXamlToolkit 中用于为文本框(TextBox 或 TextField)添加清除按钮(X 按钮)的附加属性。当用户输入内容时,清除按钮会自动显示,点击可快速清空文本框内容。
 xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
<TextBox Width="250" VerticalAlignment="Center" md:TextFieldAssist.HasClearButton="True" md:HintAssist.Hint="请输入账号"/>

输入内容后,右侧会出现清除图标,点击可清除TextBox内容。

  1. 常用TextFieldAssist属性
附加属性 说明
md:TextFieldAssist.HasClearButton 是否显示清除按钮(布尔值,默认False
md:TextFieldAssist.ClearButtonVisibility 控制清除按钮的显示时机,
可选值:
Visible(始终显示) WhenFocused(获得焦点时显示) WhenFocusedOrNotEmpty(焦点或非空时显示,默认)
md:TextFieldAssist.ClearButtonCommand 自定义清除按钮的命令(默认使用内部清空命令)
md:TextFieldAssist.ClearButtonContent 自定义清除按钮的内容(如改用图标

消息通知Snackbar

  1. 作用:在界面底部显示轻量级的临时消息通知,具体如下:
  • 临时通知:在不干扰用户主操作的前提下,显示简短的消息(如操作结果、系统提示)。
  • 交互选项:可包含一个可选的操作按钮(如 “撤销”)。
  • 自动消失:默认几秒后自动消失,无需用户手动关闭。
  1. 核心属性:MessageQueue
  • 类型:IMessageQueue
  • 作用:Snackbar 的消息队列,用于管理和显示消息。
  • {md:MessageQueue} 是一个 标记扩展,自动创建并绑定一个全局单例消息队列。
  1. 示例代码:
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
<Grid>
    <md:Snackbar x:Name="MySnackBar" MessageQueue="{md:MessageQueue}" />
</Grid>
 MySnackBar.MessageQueue.Enqueue("HelloWorld");

  1. 常用配置选项:
属性 作用
MessageQueue 绑定消息队列。
VerticalAlignment 位置(通常为Bottom)。
IsActive 控制 Snackbar 是否显示(可绑定布尔值)。
Duration 消息显示时间(默认TimeSpan.FromSeconds(2.75))。
ActionContent 操作按钮文本(如 “撤销”)。
ActionCommand 操作按钮的命令。

页面切换动画Transitioner

  1. md:Transitioner 是 Material Design In XAML Toolkit 提供的一个容器控件,用于实现 页面切换动画。它类似于原生 WPF 的 TabControl,但专注于提供流畅的过渡效果。
  2. 主要功能如下:
  • 多页面容器:可以包含多个 md:TransitionerSlide 子元素,每个 Slide 代表一个 "页面"。
  • 切换动画:在页面切换时自动应用动画效果(如淡入淡出、滑动等)。
  • 索引控制:通过 SelectedIndex 属性指定当前显示的页面。
  1. 常用属性:
属性名 作用
SelectedIndex 当前选中的页面索引(从 0 开始)。可绑定到 ViewModel 中的属性。
Transition 指定切换动画类型(默认MaterialDesignTransitionEffect)。
ItemsSource 绑定数据源,用于动态生成页面(类似ItemsControl)。
ItemTemplate 定义数据项的显示模板。
  1. 示例代码

通过点击按钮,切换不同的页面。

View

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
        xmlns:vm="clr-namespace:WpfApp2.ViewModels"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- 导航按钮 -->
        <StackPanel Orientation="Horizontal" Margin="10" Grid.Row="0">
            <Button Content="页面1" 
                       Command="{Binding}"
                       CommandParameter="0"
                       Style="{StaticResource MaterialDesignRaisedButton}"
                       Margin="5"/>
            <Button Content="页面2" 
                       Command="{Binding}"
                       CommandParameter="1"
                       Style="{StaticResource MaterialDesignRaisedButton}"
                       Margin="5"/>
        </StackPanel>

        <md:Transitioner SelectedIndex="{Binding SelectIndex,FallbackValue=0}"  Grid.Row="1">
            <md:TransitionerSlide>
                <Grid Background="LightBlue">
                    <TextBlock Text="页面一" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
                </Grid>
            </md:TransitionerSlide>

            <md:TransitionerSlide>
                <Grid Background="LightGreen">
                    <TextBlock Text="页面二" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
                </Grid>
            </md:TransitionerSlide>
        </md:Transitioner>
    </Grid>
</Window>

ViewModel

 public class MainViewModel : INotifyPropertyChanged, ICommand
 {
     private int _selectIndex;
     public int SelectIndex
     {
         get => _selectIndex;
         set => SetProperty(ref _selectIndex, value);
     }

     // 实现 ICommand 接口
     public event EventHandler CanExecuteChanged;
     public bool CanExecute(object parameter) => true;

     public void Execute(object parameter)
     {
         if (parameter is string indexStr && int.TryParse(indexStr, out int pageIndex))
         {
             SelectIndex = pageIndex;
         }
     }

     // 实现 INotifyPropertyChanged 接口
     public event PropertyChangedEventHandler PropertyChanged;

     protected virtual void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
     {
         if (EqualityComparer<T>.Default.Equals(field, value)) return;

         field = value;
         OnPropertyChanged(propertyName);
     }

     protected virtual void OnPropertyChanged(string propertyName)
         => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 }

效果:默认显示页面一,点击页面2按钮切换到页面二,再次点击页面一按钮切换到页面一。默认动画效果为淡入淡出。

全局弹窗&抽屉DialogHost

  1. 介绍:md:DialogHost 是 MaterialDesignInXamlToolkit 库中最重要的控件之一,用于在 WPF 应用中实现 全局弹窗(Dialog)抽屉(Drawer) 功能。它提供了统一的模态 / 非模态对话框管理、动画效果和键盘导航,是 Material Design 交互规范的核心组件。
  2. md:DrawerHost 实现侧边抽屉菜单(左 / 右侧),支持动画滑入滑出。
  • 关键属性
    • IsLeftDrawerOpen/IsRightDrawerOpen:控制抽屉的打开状态(绑定布尔值)。
    • LeftDrawerContent/RightDrawerContent:定义抽屉内容(通常为 StackPanel ListBox)。
    • DrawerWidth:设置抽屉宽度(默认 240)。
    • DrawerBackground:设置抽屉背景色。
  1. 主要功能
  • 全局弹窗管理
    • 显示模态或非模态对话框,覆盖整个应用界面。
    • 自动处理弹窗的层级关系,确保弹窗显示在所有内容之上。
  • 抽屉导航
    • 支持左侧或右侧抽屉菜单,通过动画滑入滑出。
    • 与 DrawerHost 结合使用,实现响应式布局。
  • 交互规范
    • 内置键盘导航(如 Esc 键关闭弹窗 / 抽屉)。
    • 支持触摸操作和鼠标交互。
  1. 示例代码

View

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
        xmlns:vm ="clr-namespace:WpfApp2.ViewModels"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainViewModel/>
        <!-- 绑定 ViewModel -->
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <!-- 左侧按钮区固定宽度 -->
            <ColumnDefinition Width="*"/>
            <!-- 右侧内容区 -->
        </Grid.ColumnDefinitions>

        <!-- 左侧区域:按钮栏 -->
        <StackPanel Grid.Column="0" Background="#F5F5F5" Margin="20">
            <!-- 设置按钮宽高,确保可点击 -->
            <Button 
                Content="打开抽屉" 
                Command="{Binding OpenDrawerCommand}" 
                Width="120" Height="40" 
                Margin="10"/>

            <Button 
                Content="关闭抽屉" 
                Command="{Binding CloseDrawerCommand}" 
                Width="120" Height="40" 
                Margin="10"/>
        </StackPanel>

        <!-- 右侧区域:抽屉宿主 -->
        <md:DrawerHost Grid.Column="1" IsRightDrawerOpen="{Binding IsDrawerOpen}">
            <!-- 右侧抽屉内容 -->
            <md:DrawerHost.RightDrawerContent>
                <DockPanel Width="300" LastChildFill="False">
                    <TextBlock Padding="20,10"  DockPanel.Dock="Top" FontSize="20" FontWeight="Bold" Text="{Binding LoginTitle}" />
                    <TextBox Margin="20,0" md:HintAssist.Hint="请输入账号" DockPanel.Dock="Top" Text="{Binding Account}" />
                    <TextBox MinHeight="100" Margin="20" md:HintAssist.Hint="请输入密码" DockPanel.Dock="Top" Text="{Binding Password}" />
                    <Button Margin="20,0" Command="{Binding ExecuteCommand}" CommandParameter="SaveData" Content="保存" DockPanel.Dock="Top" />
                </DockPanel>
            </md:DrawerHost.RightDrawerContent>
        </md:DrawerHost>
    </Grid>
</Window>

ViewModel

public class MainViewModel : INotifyPropertyChanged
{
    // 实现INotifyPropertyChanged接口
    public event PropertyChangedEventHandler? PropertyChanged;

    // 通知属性变更的方法
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 抽屉状态属性
    private bool _isDrawerOpen;
    public bool IsDrawerOpen
    {
        get => _isDrawerOpen;
        set
        {
            if (_isDrawerOpen != value)
            {
                _isDrawerOpen = value;
                OnPropertyChanged();
            }
        }
    }

    // 登录标题属性
    private string _loginTitle = "用户登录";
    public string LoginTitle
    {
        get => _loginTitle;
        set
        {
            if (_loginTitle != value)
            {
                _loginTitle = value;
                OnPropertyChanged();
            }
        }
    }

    // 账号属性
    private string _account = "";
    public string Account
    {
        get => _account;
        set
        {
            if (_account != value)
            {
                _account = value;
                OnPropertyChanged();
            }
        }
    }

    // 密码属性
    private string _password = "";
    public string Password
    {
        get => _password;
        set
        {
            if (_password != value)
            {
                _password = value;
                OnPropertyChanged();
            }
        }
    }

    // 打开抽屉命令
    private ICommand? _openDrawerCommand;
    public ICommand OpenDrawerCommand
    {
        get
        {
            return _openDrawerCommand ??= new RelayCommand(
                execute: _ => { IsDrawerOpen = true; },
                canExecute: _ => true
            );
        }
    }

    // 关闭抽屉命令
    private ICommand? _closeDrawerCommand;
    public ICommand CloseDrawerCommand
    {
        get
        {
            return _closeDrawerCommand ??= new RelayCommand(
                execute: _ => { IsDrawerOpen = false; },
                canExecute: _ => true
            );
        }
    }

    // 执行命令
    private ICommand? _executeCommand;
    public ICommand ExecuteCommand
    {
        get
        {
            return _executeCommand ??= new RelayCommand(
                execute: parameter => Execute(parameter as string),
                canExecute: _ => true
            );
        }
    }

    // 命令执行方法
    private void Execute(string? command)
    {
        switch (command)
        {
            case "SaveData":
                SaveData();
                break;
            default:
                Console.WriteLine($"未知命令: {command}");
                break;
        }
    }

    // 保存数据方法
    private void SaveData()
    {
        Console.WriteLine($"保存数据 - 账号: {Account}, 密码: {Password}");

        // 这里可以添加实际的数据保存逻辑
        // 例如调用服务层保存到数据库

        // 保存成功后关闭抽屉
        IsDrawerOpen = false;
    }
}

// 简单的RelayCommand实现
public class RelayCommand : ICommand
{
    private readonly Action<object?> _execute;
    private readonly Func<object?, bool>? _canExecute;

    public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public bool CanExecute(object? parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object? parameter)
    {
        _execute(parameter);
    }
}

  1. 常用属性
属性名 类型 作用说明
DialogContent DataTemplate 定义弹窗的内容模板
DialogOpened RoutedEventHandler 弹窗打开时触发的事件
DialogClosing RoutedEventHandler 弹窗关闭时触发的事件(可取消关闭)
DialogClosed RoutedEventHandler 弹窗关闭后的事件
IsDrawerOpen bool 控制抽屉的打开状态(配合DrawerHost使用)
KeyTipTarget UIElement 设置键盘快捷键提示的目标元素
  1. 内置命令
命令名 作用说明
OpenDialogCommand 打开弹窗(参数为弹窗内容或数据上下文)
CloseDialogCommand 关闭当前弹窗(可传递返回值)
ToggleDrawerCommand 切换抽屉的打开 / 关闭状态
OpenDrawerCommand 打开抽屉
CloseDrawerCommand 关闭抽屉

创建弹出式菜单或浮动内容PopupBox

  1. 介绍:PopupBox 是一个容器控件,类似于原生 WPF 的 Popup,md:PopupBox 是 MaterialDesignInXamlToolkit 中的一个控件,用于创建弹出式菜单或浮动内容。
  2. 特点:
  • 内置 Material Design 样式和动画。
  • 支持相对定位(上、下、左、右)。
  • 可自定义触发方式(点击、悬停等)。
  • 自动处理焦点和关闭逻辑。
  1. 关键属性

定位属性

属性 说明
HorizontalAlignment 控制弹出框的水平对齐方式(Left/Center/Right/Stretch
VerticalAlignment 控制弹出框的垂直对齐方式
Placement 指定弹出方向(Bottom/Top/Left/Right/Mouse等)
PlacementTarget 指定弹出框相对于哪个元素定位(默认为自身)

显示控制

属性 说明
IsOpen 控制弹出框是否打开(可绑定到 ViewModel)
Trigger 指定触发方式(Click/Hover/Focus/Explicit
StaysOpen 是否保持打开状态(默认为 false,点击外部关闭)

层级与样式

属性 说明
Panel.ZIndex 设置控件的 Z 轴层级(值越大越靠前,可解决被遮挡问题)
PopupStyle 自定义弹出框的样式
Child 弹出框内的内容
  1. 示例代码:
 xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"

删除

  <md:PopupBox HorizontalAlignment="Center"   Cursor="">
      <Button Content="删除" />
  </md:PopupBox>

内容过渡动画TransitioningContent

  1. 介绍:TransitioningContent 是 MaterialDesignThemes 中用于实现 内容过渡动画 的容器控件。它可以包裹任意内容(如文本、按钮、面板等),并在内容显示 / 隐藏时自动应用指定的过渡效果,提升界面交互的流畅性和视觉体验。
  2. 核心属性
  • Content:需要显示的内容(可绑定数据或直接设置)。
  • OpeningEffect:内容显示时的入场动画效果。
  • ClosingEffect:内容隐藏时的出场动画效果。(新版本不支持此属性)
  1. 示例代码
 xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
 <md:TransitioningContent OpeningEffect="{md:TransitionEffect Kind=ExpandIn, Duration=0:0:0.5}">
     <TextBlock Text="这是会展开显示的内容" FontSize="16"/>
 </md:TransitioningContent>

或者

 <md:TransitioningContent Content="这是会展开显示的内容" FontSize="16" OpeningEffect="{md:TransitionEffect Kind=ExpandIn, Duration=0:0:0.5}">
 </md:TransitioningContent>

TransitionEffect

Kind属性

效果类型 说明
None 无动画(直接显示)
ExpandIn 展开(宽度/高度从 0 → 正常尺寸)
FadeIn 淡入(透明度从 0 → 1)
SlideInFromLeft 从左侧滑入
SlideInFromTop 从顶部滑入
SlideInFromRight 从右侧滑入
SlideInFromBottom 从底部滑入

Duration:格式时:分:秒0:0:0.5 表示 500ms)。

posted @ 2025-05-16 18:23  相遇就是有缘  阅读(141)  评论(0)    收藏  举报