我们在WPF应用端的界面中,使用lepoco/wpfui 来做主要的入口框架,这个项目它的菜单内置了不少图标,我们需要在动态菜单的配置中,使用它作为图标的展示处理,本篇随笔介绍如何基于图标枚举集合进行图标的展示和选择处理。并扩展到Font-Awesome-WPF的处理进行展示和选择。

1、lepoco/wpfui 项目的图标库

lepoco/wpfui 项目的图标库来源于Fluent System Icons,项目地址是:https://github.com/microsoft/fluentui-system-icons

这些图标映射到枚举对象 SymbolRegular 和 SymbolFilled,一个是常规的,一个是填充的图标,如下枚举对象所示。

图标主要通过前面的名称来区分展示的,图标列表主要展示效果如下所示。

我们可以通过代码把这些枚举内容全部加载到列表中进行使用。

var iconList = EnumHelper.GetMemberKeyValue<SymbolRegular>();
foreach (var icon in iconList)
{
this.AllItems.Add(new CListItem(icon.Key, icon.Value.ToString()));
}

我们为了处理这些图标内容,需要按照MVVM的设计模式,设计相关的视图模型和视图界面,由于图标比较多,测试一次性展示的时候太过耗时,因此把它们分页处理,实际运行的界面效果如下所示。

1)图标列表选择界面

2)图标选择后的展示界面

我们一般在动态菜单设置页面中用到图标的选择处理,如下界面所示。

为了有效的对图标进行分页展示,视图模型需要包含一些分页所需的对象信息,如下代码所示。

    /// <summary>
/// SymbolRegular 的图标查询视图模型
/// </summary>
public partial class SymbolRegularListViewModel : BaseViewModel
{
/// <summary>
/// 选择的图表项目
/// </summary>
[ObservableProperty]
private CListItem selectedItem = new(); /// <summary>
/// 符合条件的图标列表
/// </summary>
[ObservableProperty]
private List<CListItem> iconItems = new(); /// <summary>
/// 所有图标
/// </summary>
[ObservableProperty]
private List<CListItem> allItems = new(); /// <summary>
/// 分页对象
/// </summary>
[ObservableProperty]
private PagingData pagerInfo = new PagingData() { CurrentPageIndex = 1, PageSize =60 };

由于图标不用访问数据库,因此在枚举读取初始化后存储所有的图标集合,并以集合为基础进行图标名称的检索及排序处理。

在查询处理的时候,我们需要把分页的页码和页面大小等信息转换为记录数的跳转及获取变量,如下所示。

/// <summary>
/// 转换下分页信息,为查询对象的属性
/// </summary>
protected virtual void ConvertPagingInfo()
{
//根据传入的分页信息构建查询记录数和位置
this.SkipCount = (this.PagerInfo.CurrentPageIndex - 1) * this.PagerInfo.PageSize;
this.MaxResultCount = this.PagerInfo.PageSize;
}

查询处理的代码如下所示(在视图模型上处理代码)

/// <summary>
/// 触发查询处理命令
/// </summary>
/// <returns></returns>
[RelayCommand]
public virtual void Search()
{
//切换第一页
this.PagerInfo.CurrentPageIndex = 1;
//查询更新
GetData();
} /// <summary>
/// 根据分页和查询条件查询,请求数据
/// </summary>
/// <returns></returns>
public virtual void GetData()
{
//转换下分页信息
ConvertPagingInfo();
if (this.Filter.IsNullOrEmpty())
{
this.IconItems = AllItems.Skip(this.SkipCount).Take(this.MaxResultCount).ToList();
this.PagerInfo.RecordCount = this.AllItems.Count;
}
else
{
this.IconItems = AllItems.Where(s => s.Text.Contains(this.Filter, StringComparison.OrdinalIgnoreCase)).Skip(this.SkipCount).Take(this.MaxResultCount).ToList();
this.PagerInfo.RecordCount = AllItems.Where(s => s.Text.Contains(this.Filter, StringComparison.OrdinalIgnoreCase)).Count();
}
}

选择图标的列表展示界面,遵循MVVM的视图界面代码规范,标准化处理即可,对查询搜索框的处理响应进行查询处理。

/// <summary>
/// SymbolRegularSelectPage.xaml 交互逻辑
/// </summary>
public partial class SymbolRegularSelectPage : INavigableView<SymbolRegularListViewModel>
{
/// <summary>
/// 视图模型对象
/// </summary>
public SymbolRegularListViewModel ViewModel { get; } /// <summary>
/// 构造函数
/// </summary>
/// <param name="viewModel">视图模型对象</param>
public SymbolRegularSelectPage(SymbolRegularListViewModel viewModel)
{
ViewModel = viewModel;
DataContext = this;
InitializeComponent();
} /// <summary>
/// 过滤查询事件
/// </summary>
private void SearchBar_OnSearchStarted(object sender, HandyControl.Data.FunctionEventArgs<string> e)
{
this.ViewModel.Search();
}

2、对图标使用ItemsControl控件进行控制输出

图标是一个集合对象,因此我们如果需要按照我们格式进行展示,可以使用ItemsControl来进行处理。

ItemsControl可以通过控制 ItemsControl.ItemsPanel 的ItemsPanelTemplate模板进行控制布局面板,可以通过控制 ItemsControl.Template 的ControlTemplate来控制输出格式的总模板,可以通过控制 ItemsControl.ItemTemplate的DataTemplate来控制输出每个项目的模板,这个控件ItemsControl提供了很灵活的控制模板处理。如下XAML界面代码所示。

<ItemsControl
x:Name="chkIcons"
Height="580"
HorizontalContentAlignment="Left"
ItemsSource="{Binding ViewModel.IconItems}"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="true"
VirtualizingStackPanel.VirtualizationMode="Standard">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
HorizontalAlignment="Left"
VerticalAlignment="Top"
Columns="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Width="80"
Height="80"
Margin="8,8"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Click="Button_Click"
FontSize="32"
MouseDoubleClick="Button_MouseDoubleClick"
Tag="{Binding}"
ToolTip="{Binding Text, Mode=OneTime}"
ToolTipService.InitialShowDelay="240">
<ui:SymbolIcon
FontSize="48"
Foreground="CornflowerBlue"
Symbol="{Binding Text}"
Tag="{Binding}"
ToolTip="{Binding Text}" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer
Width="Auto"
CanContentScroll="True"
VerticalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

展示界面效果如下所示

当然这里面还有分页的界面代码,使用的是HandyControl的分页控件处理。

<hc:Pagination
DataCountPerPage="{Binding ViewModel.PagerInfo.PageSize}"
IsJumpEnabled="True"
MaxPageCount="{Binding ViewModel.PagerInfo.MaxPageCount}"
MaxPageInterval="5"
PageIndex="{Binding ViewModel.PagerInfo.CurrentPageIndex, UpdateSourceTrigger=PropertyChanged}">
<hc:Interaction.Triggers>
<hc:EventTrigger EventName="PageUpdated">
<hc:EventToCommand Command="{Binding ViewModel.PageUpdatedCommand}" PassEventArgsToCommand="True" />
</hc:EventTrigger>
</hc:Interaction.Triggers>
</hc:Pagination>

而对每项的选择,我们单击选中,双击选中并返回处理,代码逻辑如下所示。

private void Button_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
if (button != null)
{
if (button.Tag is CListItem item)
{
this.ViewModel.SelectedItem = item;
}
}
} private void Button_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var button = (Button)sender;
if (button != null)
{
if (button.Tag is CListItem item)
{
this.ViewModel.SelectedItem = item;
this.DialogResult = true;
}
}
}

而选择的信息展示,我们可以通过一个面板来组合展示相关图标名称和图标效果即可。

<WrapPanel
Grid.Column="0"
Margin="10,0"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="当前选择:" />
<ui:SymbolIcon
FontSize="32"
Foreground="CornflowerBlue"
Symbol="{Binding ViewModel.SelectedItem.Text}"
ToolTip="{Binding ViewModel.SelectedItem.Text}" />
<TextBlock
Margin="10,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="Blue"
Text="{Binding ViewModel.SelectedItem.Text}" />
</WrapPanel>

确认选择后返回的内容展示,我们也是使用类似的方式处理界面的

红色框的界面XAML代码如下所示。

<!--  组合多个控件显示  -->
<hc:ElementGroup
Width="350"
Height="32"
Margin="5"
Layout="Stack"
Orientation="Horizontal">
<TextBox
x:Name="txtIcon"
Width="230"
hc:TitleElement.Title="图标"
hc:TitleElement.TitlePlacement="Left"
IsReadOnly="True"
Text="{Binding ViewModel.Item.Icon, UpdateSourceTrigger=PropertyChanged}" />
<Border Padding="1,0" Style="{StaticResource BorderRegion}">
<ui:SymbolIcon
Width="50"
FontSize="32"
Symbol="{Binding ViewModel.Item.Icon, UpdateSourceTrigger=PropertyChanged}" />
</Border>
<Button
Command="{Binding SelectIconCommand}"
Content="选择图标"
Style="{StaticResource ButtonPrimary}" />
</hc:ElementGroup>

后面就是存储处理,按照窗口界面弹出,并存储对象属性即可。

3、扩展到Font-Awesome-WPF的处理进行展示和选择

在WPF中使用Font-Awesome-WPF 图标组件的很多,它的项目地址:https://github.com/charri/Font-Awesome-WPF/blob/master/README-WPF.md

我们也可以用类似的方式来整合这个图标组件到项目中进行使用。

首先在项目的Nugget上添加安装FontAwesome.WPF组件。

或者通过命令进行安装

PM> Install-Package FontAwesome.WPF

在使用的XAML中添加对应的命名空间。

xmlns:fa="http://schemas.fontawesome.io/icons/"

这个图标的组件使用比较简单如下代码所示。

<fa:FontAwesome Icon="Flag" />

和前面的图标组件处理类似,同样需要处理图标枚举到具体图标列表展示的处理过程,图标选择界面运行效果如下所示,由于图标不是很多,所以一次性加载了。

我们先创建MVVM的视图模型对象,如下所示代码。

/// <summary>
/// FontAwesome.WPF的图标查询视图模型
/// </summary>
public partial class FontAwesomeListViewModel : BaseViewModel
{
[ObservableProperty]
private CListItem selectedItem = new(); /// <summary>
/// 符合条件的图标列表
/// </summary>
[ObservableProperty]
private List<CListItem> iconItems = new(); /// <summary>
/// 所有图标
/// </summary>
[ObservableProperty]
private List<CListItem> allItems = new(); /// <summary>
/// 构造函数
/// </summary>
public FontAwesomeListViewModel()
{
this.Title = "FontAwesome.WPF的图标"; //FontAwesomeIcon.Book = 0xf02d
var iconList = EnumHelper.GetMemberKeyValue<FontAwesomeIcon>();
foreach (var icon in iconList)
{
this.AllItems.Add(new CListItem(icon.Key, icon.Value.ToString()));
} ResetData();
}

ItemsControl的对象展示类似,如下所示。

<ItemsControl
x:Name="chkIcons"
Height="900"
HorizontalContentAlignment="Left"
ItemsSource="{Binding ViewModel.IconItems}"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="true"
VirtualizingStackPanel.VirtualizationMode="Standard">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
HorizontalAlignment="Left"
VerticalAlignment="Top"
Columns="9" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Width="80"
Height="80"
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Click="Button_Click"
FontSize="32"
MouseDoubleClick="Button_MouseDoubleClick"
Tag="{Binding}"
ToolTip="{Binding Text, Mode=OneTime}"
ToolTipService.InitialShowDelay="240">
<fa:ImageAwesome
Width="32"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="CornflowerBlue"
Icon="{Binding Text}"
Tag="{Binding}" />

</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer
Width="Auto"
CanContentScroll="True"
VerticalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

其实处理的逻辑类似了。在调用的父页面中展示也是使用相应的图标代码即可。

<fa:FontAwesome
Width="50"
FontSize="32"
Icon="{Binding ViewModel.Item.Icon, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding ViewModel.Item.Icon}" />

这样通过动态配置的菜单,我们就可以让它在系统运行的时候动态加载对应的菜单图标了。

菜单模块配置界面列表效果。

系统运行,动态从后端获取菜单及图标展示如下所示。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列表展示和选择处理的更多相关文章

  1. 基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发

    我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码 ...

  2. 推荐一个基于Vue2.0的的一款移动端开发的UI框架,特别好用。。。

    一丶YDUI 一只注重审美,且性能高效的移动端&微信UI. 下面为地址自己研究去吧! 我的项目正在用,以前用的Mint-ui但是现在感觉还是这个好一点,官方给出的解释很清楚,很实用. 官方地址 ...

  3. 基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理

    我们在设计数据库表的时候,往往为了方便,主键ID一般采用字符串类型或者GUID类型,这样对于数据库表记录的迁移非常方便,而且有时候可以在处理关联记录的时候,提前对应的ID值.但有时候进行数据记录插入的 ...

  4. 基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转

    在前面随笔,我们介绍过这个基于SqlSugar的开发框架,我们区分Interface.Modal.Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface ...

  5. 基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口

    在基于SqlSugar的开发框架中,我们设计了一些系统服务层的基类,在基类中会有很多涉及到相关的数据处理操作的,如果需要跟踪具体是那个用户进行操作的,那么就需要获得当前用户的身份信息,包括在Web A ...

  6. 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录

    在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...

  7. 基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理

    在早期的随笔就介绍过,把常规页面的内容拆分为几个不同的组件,如普通的页面,包括列表查询.详细资料查看.新增资料.编辑资料.导入资料等页面场景,这些内容相对比较独立,而有一定的代码量,本篇随笔介绍基于V ...

  8. 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用

    在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...

  9. 基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用

    刚完成一些前端项目的开发,腾出精力来总结一些前端开发的技术点,以及继续完善基于SqlSugar的开发框架循序渐进介绍的系列文章,本篇随笔主要介绍一下基于Vue3+TypeScript的全局对象的注入和 ...

  10. 基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理

    在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间 ...

随机推荐

  1. 聊聊Flink CDC必知必会

    CDC是(Change Data Capture变更数据获取)的简称. 核心思想是,监测并捕获数据库的变动(包括数据 或 数据表的插入INSERT.更新UPDATE.删除DELETE等),将这些变更按 ...

  2. 自己动手实现rpc框架(一) 实现点对点的rpc通信

    自己动手实现rpc框架(一) 实现点对点的rpc通信 1. 什么是rpc? RPC是远过程调用(Remote Procedure Call)的缩写形式,其区别于一个程序内部基本的过程调用(或者叫函数/ ...

  3. 【websocket】小白快速上手flask-socketio

    大家好,我是一个初级的Python开发工程师.本文是结合官方教程和代码案例,简单说下我对flask-socketio的使用理解. 一.websocket简介 websocket 说白一点就是,建立客户 ...

  4. 【技术积累】JavaScript中的基础语法【三】

    JavaScript的条件结构 JavaScript中的条件结构主要包括if语句.if-else语句.if-else if语句和switch语句.这些条件结构用于根据不同的条件执行不同的代码块. if ...

  5. MyBatis使用注解开发(及Sqlsession连接器的本质)

    使用注解开发 底层实现机制是反射和,动态代码.反射可以获得这个类的方法属性还可以创建对象,执行方法. 面向接口编程 之前学过,面向对象编程,也学习过接口.但是真正的开发中,很多时候我们会选择面向接口编 ...

  6. Python sorted() 函数和sort()函数对比分析

    Python sorted() 函数 一.概述 sorted()函数是对所有可迭代的对象进行排序操作. sort与sorted的区别: sort是应用在list上的方法,sorted可以对所有可迭代的 ...

  7. redis 中的 set

    set是String中的无序集合  底层是 是 value为null 的hash表  时间复杂化是o(1): sadd  k1 v1 v2 v3  set中添加数据 smembers k1 取出set ...

  8. return true 与 return false的妙用——jQuery

    var arr = [1, 3, 5,7,9]; jQuery.each(arr, function(key, value){ if(key === 2){ return true; } consol ...

  9. 什么是PMP?

    PMP(Project Management Professional)中文名称叫项目管理专业人士资格认证.它是由美国项目管理协会(PMI)发起的,严格评估项目管理人员知识技能是否具有高品质的资格认证 ...

  10. 王道oj/problem23

    网址:oj.lgwenda.problem/23 代码: #define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include<stri ...