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

循序渐进,介绍,基于,communitytoolkit,mvvm,handycontrol,wpf,应用,开发,图标,列表,展示,选择,处理 · 浏览次数 : 23

小编点评

``` {Binding ViewModel.Item.Icon} ```

正文

我们在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) -- 图标列表展示和选择处理相似的内容:

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(8) -- 使用Converter类实现内容的转义处理

在我们WPF应用端的时候,和WInform开发或者Vue前端开发一样,有时候也需要对内容进行转义处理,如把一些0,1数值转换为具体含义的文本信息,或者把一些布尔变量转换为是否等,都是常见的转换处理,本篇随笔介绍在WPF应用端对内容使用Converter类实现内容的转义处理的操作。

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

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

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(1)

在我们的SqlSugar的开发框架中,整合了Winform端、Vue3+ElementPlus的前端、以及基于UniApp+Vue+ThorUI的移动前端几个前端处理,基本上覆盖了我们日常的应用模式了,本篇随笔进一步介绍前端应用的领域,研究集成WPF的应用端,循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(2)

在前面随笔《循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(1)》中介绍了Mvvm 的开发,以及一些界面效果,本篇随笔继续深入探讨基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发,介绍如何整合SqlSugar框架的基础接口,通过基类继承的方式,简化实际项目的开发代码处理。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件

在我们创建界面元素的时候,不管在Vue3+ElementPlus的前端上,还是Winform桌面端上,都是会利用自定义用户控件来快速重用一些自定义的界面内容,对自定义用户控件的封装处理,也是我们开发WPF应用需要熟悉的一环。本篇随笔继续深入介绍介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发,主要针对自定义用户控件的封装和使用做一些介绍。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(4) -- 实现DataGrid数据的导入和导出操作

在我们设计软件的很多地方,都看到需要对表格数据进行导入和导出的操作,主要是方便客户进行快速的数据处理和分享的功能,本篇随笔介绍基于WPF实现DataGrid数据的导入和导出操作。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用

在我们展示一些参考信息的时候,有所会用树形列表来展示结构信息,如对于有父子关系的多层级部门机构,以及一些常用如字典大类节点,也都可以利用树形列表的方式进行展示,本篇随笔介绍基于WPF的方式,使用TreeView来洗实现结构信息的展示,以及对它的菜单进行的设置、过滤查询等功能的实现逻辑。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(6) -- 窗口控件脏数据状态IsDirty的跟踪处理

在我们窗口新增、编辑状态下的时候,我们往往会根据是否修改过的痕迹-也就是脏数据状态进行跟踪,如果用户发生了数据修改,我们在用户退出窗口的时候,提供用户是否丢弃修改还是继续编辑,这样在一些重要录入时的时候,可以避免用户不小心关掉窗口,导致窗口的数据要重新录入的尴尬场景。本篇随笔介绍基于WPF开发中,窗...

基于SqlSugar的开发框架循序渐进介绍(29)-- 快速构建系统参数管理界面-Vue3+ElementPlus

在随笔《基于SqlSugar的开发框架循序渐进介绍(28)-- 快速构建系统参数管理界面》中介绍了基于SqlSugar开发框架,构建系统参数管理的后端API部分,以及WInform界面部分内容,本篇随笔介绍基于Vue3+ElementPlus的前端界面开发过程。

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

在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间,这样验证短信验证码的时候,就会自动判断是否过期了。本篇随笔结合CSRedis的使用,介绍如何实现缓存的初始化及使用的处理。