少有人走的路

勇哥的工业自动化技术网站

wpf(CommunityToolkit.Mv, Autofac 技术)下的菜单和工具栏的使用例子


接下来研究一下菜单和工具栏的使用。

FontAwesome.Sharp包是一个专门为 .NET 桌面应用(重点支持 WPF、WinForms、Avalonia)打造的开源图标库封装包,

核心是将 Font Awesome 图标字体 封装成了桌面应用可直接使用的控件,

让开发者无需手动处理字体文件、字符编码,就能快速在界面中嵌入美观的图标。


简单的说,就是用字体方式显示已经预定义好的图标(大约有几百个)。

还有一种SVG方式显示图标,这种方式相当于是通过图标数据画矢量图的图标画出来。在仅仅静态显示的时候,两种方式区别就比较小。

下一节研究一下SVG这种方式。



act102.gif

界面:


image.png


解决方案:

image.png



MainWindow.xaml

<Window x:Class="WpfApp1.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:WpfApp1"
        xmlns:conv="clr-namespace:WpfApp1.Converters"
        xmlns:fa="clr-namespace:FontAwesome.Sharp;assembly=FontAwesome.Sharp"
        mc:Ignorable="d"
        Title="WPF 菜单+工具栏示例(.NET 8 适配版)" Height="450" Width="800">
    <Window.Resources>
        <!-- 1. 注册字符串相等转换器 -->
        <conv:StringEqualsConverter x:Key="StringEqualsConverter"/>

        <!-- 2. 自定义工具栏样式 -->
        <Style x:Key="CustomToolBarStyle" TargetType="{x:Type ToolBar}">
            <Setter Property="OverridesDefaultStyle" Value="True"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToolBar}">
                        <Border Background="#F5F5F5" BorderThickness="0,1,0,0" BorderBrush="#E0E0E0">
                            <StackPanel Orientation="Horizontal" Margin="10,5,10,5" IsItemsHost="True"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <!-- 3. 工具栏按钮样式(用Converter实现选中逻辑) -->
        <Style x:Key="ToolBarButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Margin" Value="0,0,8,0"/>
            <Setter Property="Padding" Value="8,6"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="Foreground" Value="#333333"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="btnBorder" Background="{TemplateBinding Background}" CornerRadius="4">
                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
                                <!-- FontAwesome图标(字体方式) -->
                                <TextBlock x:Name="btnIcon" 
                                           FontFamily="pack://application:,,,/FontAwesome.Sharp;component/fonts/#Font Awesome 6 Free Solid"
                                           Text="{TemplateBinding Tag}"
                                           FontSize="24" Margin="0,0,4,0"/>
                                <TextBlock x:Name="btnText" Text="{TemplateBinding Content}" VerticalAlignment="Center"/>
                            </StackPanel>
                        </Border>
                        <ControlTemplate.Triggers>
                            <!-- 鼠标悬浮 -->
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="btnBorder" Property="Background" Value="#E9E9E9"/>
                            </Trigger>
                            <!-- 修复:用Converter判断选中状态(避开Condition的Value绑定限制) -->
                            <DataTrigger Binding="{Binding ActiveToolBarButton, Converter={StaticResource StringEqualsConverter}, ConverterParameter={TemplateBinding Content}}" Value="True">
                                <Setter TargetName="btnBorder" Property="Background" Value="#2ECC71"/>
                                <Setter TargetName="btnIcon" Property="Foreground" Value="White"/>
                                <Setter TargetName="btnText" Property="Foreground" Value="White"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <DockPanel>
        <!-- 顶部菜单 -->
        <Menu DockPanel.Dock="Top" Background="White" Foreground="#333333">
            <MenuItem Header="文件">
                <MenuItem Header="打开" Command="{Binding FileOpenCommand}" InputGestureText="Ctrl+O"/>
                <MenuItem Header="保存" Command="{Binding FileSaveCommand}" InputGestureText="Ctrl+S"/>
            </MenuItem>
        </Menu>

        <!-- 工具栏 -->
        <ToolBar DockPanel.Dock="Top" Style="{StaticResource CustomToolBarStyle}">
            <Button Content="配置" Tag="&#xf013;" Command="{Binding ToolBarConfigCommand}" Style="{StaticResource ToolBarButtonStyle}"/>
            <Button Content="流程" Tag="&#xf0e8;" Command="{Binding ToolBarFlowCommand}" Style="{StaticResource ToolBarButtonStyle}"/>
            <Button Content="生产" Tag="&#xf275;" Command="{Binding ToolBarProductionCommand}" Style="{StaticResource ToolBarButtonStyle}"/>

            <Separator Margin="8,0" Width="1" Background="#E0E0E0"/>

            <Button Content="保存" Tag="&#xf0c7;" Command="{Binding ToolBarSaveCommand}" Style="{StaticResource ToolBarButtonStyle}"/>
        </ToolBar>

        <!-- 内容区域 -->
        <Grid Background="White">
            <TextBlock Text="内容区域" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Grid>
    </DockPanel>
</Window>


App.xaml.cs

using Autofac;
using System.Configuration;
using System.Data;
using System.Windows;
using WpfApp1.Bootstrap;
using WpfApp1.ViewModels;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        // 全局Autofac容器
        public static IContainer? Container { get; private set; }

        protected override void OnStartup(StartupEventArgs e)
        {
            // 1. 初始化Autofac容器
            Container = AutofacConfig.BuildContainer();
            if (Container == null)
            {
                MessageBox.Show("Autofac 容器初始化失败", "错误");
                Shutdown();
                return;
            }

            // 2. 从容器中解析 MainWindowViewModel(依赖注入获取实例)
            var mainViewModel = Container.Resolve<MainWindowViewModel>();

            // 3. 实例化主窗口并绑定 DataContext
            var mainWindow = new MainWindow();
            mainWindow.DataContext = mainViewModel;
            mainWindow.Show();

            // 禁用默认启动逻辑(避免重复加载窗口)
            base.OnStartup(e);
        }
    }

}


App.xaml

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1">
    <Application.Resources>
         
    </Application.Resources>
</Application>


MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1.ViewModels
{
    /// <summary>
    /// 主窗口ViewModel(继承ViewModelBase获得INotifyPropertyChanged实现)
    /// </summary>
    public partial class MainWindowViewModel : ObservableObject
    {
        // 用于绑定工具栏按钮的选中状态(示例:默认"配置"按钮选中)
        private string _activeToolBarButton = "配置";

        public string ActiveToolBarButton
        {
            get => _activeToolBarButton;
            set => SetProperty(ref _activeToolBarButton, value);
        }

        #region 菜单命令
        /// <summary>
        /// 「文件-打开」命令(RelayCommand 简化命令创建,无需手动实现ICommand)
        /// </summary>
        [RelayCommand]
        private void FileOpen()
        {
            MessageBox.Show("执行「文件-打开」操作", "提示");
        }

        /// <summary>
        /// 「文件-保存」命令
        /// </summary>
        [RelayCommand]
        private void FileSave()
        {
            MessageBox.Show("执行「文件-保存」操作", "提示");
        }
        #endregion

        #region 工具栏命令
        /// <summary>
        /// 「配置」按钮命令
        /// </summary>
        [RelayCommand]
        private void ToolBarConfig()
        {
            ActiveToolBarButton = "配置";
            MessageBox.Show("切换到「配置」功能页面", "提示");
        }

        /// <summary>
        /// 「流程」按钮命令
        /// </summary>
        [RelayCommand]
        private void ToolBarFlow()
        {
            ActiveToolBarButton = "流程";
            MessageBox.Show("切换到「流程」功能页面", "提示");
        }

        /// <summary>
        /// 「生产」按钮命令
        /// </summary>
        [RelayCommand]
        private void ToolBarProduction()
        {
            ActiveToolBarButton = "生产";
            MessageBox.Show("切换到「生产」功能页面", "提示");
        }

        /// <summary>
        /// 「保存」按钮命令
        /// </summary>
        [RelayCommand]
        private void ToolBarSave()
        {
            MessageBox.Show("执行工具栏「快速保存」操作", "提示");
        }
        #endregion
    }
}


StringEqualsConverter.cs

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace WpfApp1.Converters
{
    /// <summary>
    /// 字符串相等判断转换器
    /// </summary>
    public class StringEqualsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // 比较ViewModel的ActiveToolBarButton(value)和按钮的Content(parameter)是否相等
            return value?.ToString() == parameter?.ToString();
        }

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



AutofacConfig.cs

using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp1.ViewModels;

namespace WpfApp1.Bootstrap
{
    /// <summary>
    /// Autofac 依赖注入配置
    /// </summary>
    public static class AutofacConfig
    {
        /// <summary>
        /// 构建容器并返回
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // 注册 ViewModel(单例模式,适合窗口ViewModel;后续业务服务可注册为瞬时/作用域)
            builder.RegisterType<MainWindowViewModel>().SingleInstance();

            // 后续扩展:注册业务服务(例如:builder.RegisterType<OrderService>().As<IOrderService>().InstancePerDependency();)

            // 构建并返回容器
            return builder.Build();
        }
    }
}


FontAwesome.Sharp画图标的过程如下:


  • WPF 渲染 TextBlock 时,先解析 FontFamily,通过 Pack URI 找到 FontAwesome.Sharp.dll 中的 fonts 目录。

  • 在该目录下,找到字体家族名称为 Font Awesome 6 Free Solid.ttf 字体文件,加载该字体。

  • 解析 Text="{TemplateBinding Tag}"Tag 属性中填写的 &#xf013; 是 Font Awesome 图标的「Unicode 十六进制编码实体」:

    • &#x:表示这是十六进制的 Unicode 编码。

    • f013:是「设置」图标的专属 Unicode 编码(每个 Font Awesome 图标都有唯一的编码)。

    • ;:编码实体的结束符。

  • WPF 用加载好的 Font Awesome 字体,将 &#xf013; 这个 Unicode 字符渲染成对应的图标,

  • 而不是普通文本,最终就显示出了我们看到的图标样式。









发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«    2026年1月    »
1234
567891011
12131415161718
19202122232425
262728293031
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接

Powered By Z-BlogPHP 1.7.3

Copyright www.skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864