当前位置: 首页 > news >正文

Avalonia 学习笔记07. Control Themes(控件主题)

在本章节中,我们的目标是创建一个可复用的、带图标的按钮控件,以简化我们在视图(View)中编写的XAML代码。当前,每创建一个带图标的按钮,都需要在 <Button> 内部嵌套一个 <StackPanel> 和两个 <Label>,这非常繁琐。

我们将创建一个名为 IconButton 的新控件,它天生就包含一个图标区域和一个内容区域,使得我们可以像这样使用它:<IconButton IconText="..." Content="..."/>。我们将通过控件主题(ControlTheme)来实现这个功能,这是一种深度自定义控件外观的强大方式。

7.1 Controls\IconButton.axaml

首先,我们在项目中新建一个名为 Controls 的文件夹。然后右键点击该文件夹,选择“添加” -> “新建项”,在 Avalonia 分类中选择 Templated Control(模板化控件),并将其命名为 IconButton.axaml

创建完成后,我们会得到一个 .axaml 文件和一个对应的 .axaml.cs 后台代码文件。

我们的目标是让 IconButton 拥有标准按钮的所有外观和行为,并在此基础上增加一个图标。最简单的方式就是复制 Avalonia Fluent 主题中 Button 的默认模板,然后进行修改。

以下代码就是从 Avalonia 源码中复制并修改而来的 Button 的 ControlTheme

这个初始的模板可以在这个链接获取:https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Themes.Fluent/Controls/Button.xaml

关键修改点解释:

  1. <ControlTheme x:Key="{x:Type IconButton}" TargetType="IconButton">

    • 用途:这行代码声明了我们正在定义一个控件主题。TargetType="IconButton" 指定了这个主题是为我们自己的 IconButton 控件设计的。它会成为 IconButton 的默认外观。
  2. <ContentPresenter.ContentTemplate>

    • 用途:我们不再让 ContentPresenter 简单地显示内容,而是为它提供了一个 DataTemplate(数据模板)。这个模板定义了内容的具体结构:一个水平排列的 StackPanel,里面包含一个用于显示图标的 Label 和一个用于显示主要内容的 ContentControl
  3. 图标绑定:Content="{Binding $parent[IconButton].IconText}"

    • 用途:这是 DataTemplate 内部的绑定语法。$parent[IconButton] 的意思是“从当前位置向上查找,找到第一个名为 IconButton 的父控件”,然后 .IconText 表示绑定到该控件的 IconText 属性。这样,我们在XAML中设置的 IconText 就能正确地显示为图标了。
    • 注意Label 的 Classes="icon" 是为了能让 AppDefaultStyles.axaml 中定义的图标字体样式应用到这个 Label 上。
  4. 内容和数据上下文绑定

    • 用途<ContentControl DataContext="{...}" Content="{...}" /> 这一部分是为了修复一个在视频后面会遇到的 DataContext(数据上下文)问题。当我们在 IconButton 内部放置一个需要绑定到视图模型(ViewModel)的控件时(比如侧边栏可以折叠的文字),这个设置可以确保数据上下文被正确地传递下去,让绑定能够正常工作。
<!-- 备注:这是 IconButton 的视觉模板定义文件。 -->
<!-- 它决定了 IconButton 在界面上看起来是什么样子。 -->
<ResourceDictionary xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"x:ClassModifier="internal"><!-- Design.PreviewWith 用于在设计器中预览控件效果,不影响程序运行 --><Design.PreviewWith><Border Padding="20"><StackPanel Spacing="20"><IconButton IconText="&#xe7f2;" Content="Click Me!" /><IconButton IconText="&#xe3ee;" Classes="accent" Content="Click Me!" /><Button Content="Click Me!" /><Button Classes="accent" Content="Click Me!" /></StackPanel></Border></Design.PreviewWith><!-- 这是 IconButton 控件的主题定义 --><ControlTheme x:Key="{x:Type IconButton}" TargetType="IconButton"><!-- 下面是按钮在各种状态下的默认样式设置(背景、前景、边框等) --><Setter Property="Background" Value="{DynamicResource ButtonBackground}" /><Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" /><Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" /><Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" /><Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" /><Setter Property="Padding" Value="{DynamicResource ButtonPadding}" /><Setter Property="HorizontalAlignment" Value="Left" /><Setter Property="VerticalAlignment" Value="Center" /><Setter Property="RenderTransform" Value="none" /><Setter Property="Transitions"><Transitions><TransformOperationsTransition Property="RenderTransform" Duration="0:0:.075" /></Transitions></Setter><!-- Template 定义了控件的内部结构 (ControlTemplate) --><Setter Property="Template"><ControlTemplate><ContentPresenter x:Name="PART_ContentPresenter"Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"CornerRadius="{TemplateBinding CornerRadius}"Padding="{TemplateBinding Padding}"RecognizesAccessKey="True"HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"><!-- 这里是我们自定义的内容模板,用于显示图标和文字 --><ContentPresenter.ContentTemplate><DataTemplate DataType="x:Object"><StackPanel Orientation="Horizontal" Spacing="8"><!-- 这个 Label 用于显示图标,它的 Content 绑定到 IconButton 的 IconText 属性 --><Label Classes="icon" Content="{Binding $parent[IconButton].IconText}"></Label><!-- 这个 ContentControl 用于显示 IconButton 的主要内容,并修复了 DataContext 的问题 --><ContentControl DataContext="{Binding $parent[IconButton].DataContext}" Content="{Binding $parent[IconButton].Content}" /></StackPanel></DataTemplate></ContentPresenter.ContentTemplate></ContentPresenter></ControlTemplate></Setter><!-- 下面是按钮在不同交互状态(如鼠标悬浮、按下)下的样式变化 --><Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" /><Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" /><Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" /></Style><Style Selector="^:pressed"><Setter Property="RenderTransform" Value="scale(0.98)" /></Style><Style Selector="^:pressed  /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" /><Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" /><Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" /></Style><Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" /><Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" /><Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" /></Style><!-- 这是对拥有 "accent" 样式的按钮的特殊处理 --><Style Selector="^.accent"><Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource AccentButtonBackground}" /><Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrush}" /><Setter Property="Foreground" Value="{DynamicResource AccentButtonForeground}" /></Style><Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPointerOver}" /><Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPointerOver}" /><Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundPointerOver}" /></Style><Style Selector="^:pressed  /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPressed}" /><Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPressed}" /><Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" /></Style><Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter"><Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundDisabled}" /><Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushDisabled}" /><Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundDisabled}" /></Style></Style></ControlTheme>
</ResourceDictionary>

 

7.2 Controls\IconButton.axaml.cs

这是 IconButton 的后台代码文件。

关键修改点解释:

  1. public class IconButton : Button

    • 用途:我们将基类从默认的 TemplatedControl 修改为了 Button。这是至关重要的一步。通过继承 Button,我们的 IconButton 自动获得了按钮的所有核心功能,例如 Click 事件、Command 绑定、可点击性等。它现在“是”一个按钮了。
  2. IconTextProperty

    • 用途:我们在这里定义了一个新的 StyledProperty(样式化属性),名为 IconTextStyledProperty 是 Avalonia 中的一种特殊属性,它支持数据绑定、样式设置和在模板中使用。这使得我们可以在 XAML 文件中像这样 <IconButton IconText="..."/> 来给它赋值。
// 备注:这是 IconButton 的后台逻辑代码文件。
using Avalonia;
using Avalonia.Controls;namespace AvaloniaApplication2.Controls;// 关键:让 IconButton 继承自 Button,这样它就拥有了按钮的所有功能
public class IconButton : Button
{// 定义一个名为 IconText 的新属性,这样我们就可以在 XAML 中设置它// StyledProperty 是一种支持数据绑定和样式的特殊属性public static readonly StyledProperty<string> IconTextProperty = AvaloniaProperty.Register<IconButton, string>(nameof(IconText));// 这是 IconText 属性的常规 C# 包装器public string IconText{get => GetValue(IconTextProperty);set => SetValue(IconTextProperty, value);}
}

 

7.3 Styles\AppDefaultStyles.axaml

关键修改点解释:

  1. :is(Button) 选择器
    • 用途:我们将之前所有针对 Button 的样式选择器,例如 Style Selector="Button",都修改为了 Style Selector=":is(Button)"
    • 原因:is() 是一个伪类选择器,它会匹配所有符合括号内条件的控件。:is(Button) 不仅会匹配标准的 <Button>,还会匹配任何继承自 Button 的控件。因为我们的 IconButton 继承自 Button,所以这个修改可以确保我们为普通按钮编写的通用样式(如圆角、字体大小、颜色等)也能自动应用到 IconButton 上,保持了应用的视觉统一性。
<Styles xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Design.PreviewWith><Border Padding="20" Background="{DynamicResource PrimaryBackgroundGradient}" Width="200"><!-- 此处预览代码未更改 --><StackPanel Spacing="12"><Image Source="{SvgImage /Assets/Images/logo.svg}" Width="200"></Image><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content="&#xE2C2;"></Label><Label Classes="akko" Content="Home"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content="&#xE346;"></Label><Label Classes="akko" Content="Process"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content="&#xE7F2;"></Label><Label Classes="akko" Content="Actions"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content="&#xE3EE;"></Label><Label Classes="akko" Content="Macros"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content="&#xEB7A;"></Label><Label Classes="akko" Content="Reporter"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content="&#xE03A;"></Label><Label Classes="akko" Content="History"></Label></StackPanel></Button><Button><Label Classes="icon-only" Content="&#xE272;"></Label></Button><Button Classes="transparent" Grid.Row="1"><Label Classes="icon-only" Content="&#xE272;"></Label></Button></StackPanel></Border></Design.PreviewWith><!-- 未更改的样式 --><Style Selector="Window"><!-- <Setter Property="FontFamily" Value="{DynamicResource AkkoPro}"></Setter> --></Style><Style Selector="Border"><Setter Property="Transitions"><Transitions><DoubleTransition Property="Width" Duration="0:0:1"></DoubleTransition></Transitions></Setter></Style><Style Selector="Label.icon, Label.icon-only"><Setter Property="FontFamily" Value="{DynamicResource Phosphor-Fill}"></Setter><Setter Property="Margin" Value="0 2 5 0"></Setter><Setter Property="FontSize" Value="19"></Setter></Style><Style Selector="Label.icon-only"><Setter Property="Margin" Value="0"></Setter></Style><Style Selector="Button, Label.akko"><Setter Property="FontFamily" Value="{DynamicResource AkkoPro}"></Setter></Style><!-- 关键修改:将 "Button" 选择器改为 ":is(Button)",以使其能应用到 IconButton --><Style Selector=":is(Button)"><Setter Property="FontSize" Value="20"></Setter><Setter Property="CornerRadius" Value="10"></Setter><Setter Property="Foreground" Value="{DynamicResource PrimaryForeground}"></Setter><Setter Property="Background" Value="{DynamicResource PrimaryBackground}"></Setter></Style><Style Selector=":is(Button) /template/ ContentPresenter"><Setter Property="RenderTransform" Value="scale(1)"></Setter><Setter Property="Transitions"><Transitions><BrushTransition Property="Foreground" Duration="0:0:0.1"></BrushTransition><BrushTransition Property="Background" Duration="0:0:0.1"></BrushTransition><TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.1"></TransformOperationsTransition></Transitions></Setter></Style><Style Selector=":is(Button).transparent:pointerover Label"><Setter Property="RenderTransform" Value="scale(1.2)"></Setter></Style><Style Selector=":is(Button):pointerover /template/ ContentPresenter"><Setter Property="Foreground" Value="{DynamicResource PrimaryHoverForeground}"></Setter><Setter Property="Background" Value="{DynamicResource PrimaryHoverBackground}"></Setter></Style><Style Selector=":is(Button).active /template/ ContentPresenter"><Setter Property="Background" Value="{DynamicResource PrimaryActiveBackground}"></Setter></Style><Style Selector=":is(Button).transparent"><Setter Property="Background" Value="Transparent"></Setter></Style><Style Selector=":is(Button).transparent Label.icon-only"><Setter Property="FontFamily" Value="{DynamicResource Phosphor}"></Setter></Style><Style Selector=":is(Button).transparent:pointerover /template/ ContentPresenter"><Setter Property="Background" Value="Transparent"></Setter></Style>
</Styles>

 

7.4 App.axaml

关键修改点解释:

  1. <MergeResourceInclude Source="/Controls/IconButton.axaml"/>
    • 用途:这行代码的作用是将我们刚刚创建的 IconButton.axaml 文件(它是一个资源字典)合并到应用的主资源中。
    • 原因:如果不进行这一步,整个应用程序将不知道 IconButton 的样式定义在哪里,导致 IconButton 无法被正确渲染,会显示为一个没有样式的空白控件。这一步使得 IconButton 的主题在整个应用范围内都是可用的。
<Application xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"x:Class="AvaloniaApplication2.App"xmlns:local="clr-namespace:AvaloniaApplication2"RequestedThemeVariant="Default"><!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --><Application.DataTemplates><local:ViewLocator></local:ViewLocator></Application.DataTemplates><Application.Styles><FluentTheme /><StyleInclude Source="Styles/AppDefaultStyles.axaml"></StyleInclude></Application.Styles><!-- 在这里添加了对 IconButton 样式文件的引用 --><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><!-- 这行代码将 IconButton.axaml 中定义的样式合并到整个应用中 --><MergeResourceInclude Source="/Controls/IconButton.axaml"></MergeResourceInclude></ResourceDictionary.MergedDictionaries></ResourceDictionary><!-- 此处其他资源未更改 --><SolidColorBrush x:Key="PrimaryForeground">#CFCFCF</SolidColorBrush><SolidColorBrush x:Key="PrimaryBackground">#14172D</SolidColorBrush><LinearGradientBrush x:Key="PrimaryBackgroundGradient" StartPoint="0%, 0%" EndPoint="100%, 0%"><GradientStop Offset="0" Color="#111214"></GradientStop><GradientStop Offset="1" Color="#151E3E"></GradientStop></LinearGradientBrush><SolidColorBrush x:Key="PrimaryHoverBackground">#333B5A</SolidColorBrush><SolidColorBrush x:Key="PrimaryActiveBackground">#6633dd</SolidColorBrush><SolidColorBrush x:Key="PrimaryHoverForeground">White</SolidColorBrush><FontFamily x:Key="AkkoPro">/Assets/Fonts/AkkoPro-Regular.ttf#Akko Pro</FontFamily><FontFamily x:Key="AkkoProBold">/Assets/Fonts/AkkoPro-Bold.ttf#Akko Pro</FontFamily><FontFamily x:Key="Phosphor">/Assets/Fonts/Phosphor.ttf#Phosphor</FontFamily><FontFamily x:Key="Phosphor-Fill">/Assets/Fonts/Phosphor-Fill.ttf#Phosphor</FontFamily></Application.Resources>
</Application>

 

7.5 App.axaml.cs

关键修改点解释:

  1. [assembly: XmlnsDefinition(...)]
    • 用途:这是一个程序集级别的特性(Attribute),它将一个 C# 命名空间(AvaloniaApplication2.Controls)映射到一个 XML 命名空间(https://github.com/avaloniaui)。
    • 原因:这是一个非常有用的“语法糖”。添加它之后,我们就可以在 XAML 中直接使用 <IconButton />,而不需要先在文件顶部定义一个像 xmlns:c="clr-namespace:AvaloniaApplication2.Controls" 这样的前缀,然后再使用 <c:IconButton />。它让我们的自定义控件使用起来和 Avalonia 的内置控件一样方便、自然。
using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Metadata; // 需要引入这个命名空间
using AvaloniaApplication2.Data;
using AvaloniaApplication2.Factories;
using AvaloniaApplication2.ViewModels;
using Microsoft.Extensions.DependencyInjection;// 关键:添加这个程序集特性,让我们可以不带前缀地在 XAML 中使用 IconButton
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "AvaloniaApplication2.Controls")]namespace AvaloniaApplication2;public partial class App : Application
{// 此文件中的其他代码在本节教程中未发生变更public override void Initialize(){AvaloniaXamlLoader.Load(this);}public override void OnFrameworkInitializationCompleted(){var collection = new ServiceCollection();collection.AddSingleton<MainViewModel>();collection.AddTransient<ActionsPageViewModel>();collection.AddTransient<HomePageViewModel>();collection.AddTransient<MacrosPageViewModel>();collection.AddTransient<ProcessPageViewModel>();collection.AddTransient<ReporterPageViewModel>();collection.AddTransient<HistoryPageViewModel>();collection.AddTransient<SettingsPageViewModel>();collection.AddSingleton<Func<ApplicationPageNames, PageViewModel>>(x => name => name switch{ApplicationPageNames.Home => x.GetRequiredService<HomePageViewModel>(),ApplicationPageNames.Process => x.GetRequiredService<ProcessPageViewModel>(),ApplicationPageNames.Macros => x.GetRequiredService<MacrosPageViewModel>(),ApplicationPageNames.Actions => x.GetRequiredService<ActionsPageViewModel>(),ApplicationPageNames.Reporter => x.GetRequiredService<ReporterPageViewModel>(),ApplicationPageNames.History => x.GetRequiredService<HistoryPageViewModel>(),ApplicationPageNames.Settings => x.GetRequiredService<SettingsPageViewModel>(),_ => throw new InvalidOperationException()});collection.AddSingleton<PageFactory>();var services = collection.BuildServiceProvider();if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop){desktop.MainWindow = new MainView{DataContext = services.GetService<MainViewModel>()};}base.OnFrameworkInitializationCompleted();}
}

 

7.6 Views\MainView.axaml

关键修改点解释:

  • 替换为 <IconButton>:这里是应用我们新控件最直观的地方。之前所有左侧菜单的按钮都是由 <Button><StackPanel><Label/><Label/></StackPanel></Button> 这样的复杂结构组成的。
  • 简化XAML:现在,我们用一行简洁的 <IconButton> 就完成了同样的功能。图标通过 IconText 属性设置,而需要折叠的文字 Label 则直接作为 IconButton 的内容(Content)传入。这使得 XAML 代码大大减少,并且更易于阅读和维护。
<Window xmlns="https://github.com/avaloniaui"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"mc:Ignorable="d" d:DesignWidth="1400" d:DesignHeight="800"Width="1400" Height="800"MinWidth="1400" MinHeight="800 "x:Class="AvaloniaApplication2.MainView"xmlns:vm="clr-namespace:AvaloniaApplication2.ViewModels"xmlns:view="clr-namespace:AvaloniaApplication2.Views"x:DataType="vm:MainViewModel"Title="AvaloniaApplication2"><Design.DataContext><vm:MainViewModel></vm:MainViewModel></Design.DataContext><Grid Background="{DynamicResource PrimaryBackground}" ColumnDefinitions="Auto, *"><ContentControl Grid.Column="1" Content="{Binding CurrentPage}" /><Border Padding="20" Background="{DynamicResource PrimaryBackgroundGradient}"><Grid RowDefinitions="*, Auto"><StackPanel Spacing="12"><Image PointerPressed="InputElement_OnPointerPressed" Source="{SvgImage /Assets/Images/logo.svg}" Width="220" IsVisible="{Binding SideMenuExpanded}"></Image><Image PointerPressed="InputElement_OnPointerPressed" Source="{SvgImage /Assets/Images/icon.svg}" Width="22" IsVisible="{Binding !SideMenuExpanded}"></Image><!-- 关键修改:将原来的复杂 Button 结构替换为简洁的 IconButton --><IconButton IconText="&#xE2C2;" HorizontalAlignment="Stretch" Classes.active="{Binding HomePageIsActive}" Command="{Binding GoToHomeCommand}"><Label Classes="akko" Content="Home" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="&#xE346;" HorizontalAlignment="Stretch" Classes.active="{Binding ProcessPageIsActive}" Command="{Binding GoToProcessCommand}"><Label Classes="akko" Content="Process" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="&#xE7F2;" HorizontalAlignment="Stretch" Classes.active="{Binding ActionsPageIsActive}" Command="{Binding GoToActionsCommand}"><Label Classes="akko" Content="Actions" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="&#xE3EE;" HorizontalAlignment="Stretch" Classes.active="{Binding MacrosPageIsActive}" Command="{Binding GoToMacrosCommand}"><Label Classes="akko" Content="Macros" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="&#xEB7A;" HorizontalAlignment="Stretch" Classes.active="{Binding ReporterPageIsActive}" Command="{Binding GoToReporterCommand}"><Label Classes="akko" Content="Reporter" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="&#xE03A;" HorizontalAlignment="Stretch" Classes.active="{Binding HistoryPageIsActive}" Command="{Binding GoToHistoryCommand}"><Label Classes="akko" Content="History" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton></StackPanel><!-- 这个设置按钮未在本节修改,但因为它也是Button,所以会受到 :is(Button) 样式的影响 --><Button Classes="transparent" Grid.Row="1" Classes.active="{Binding SettingsPageIsActive}" Command="{Binding GoToSettingsCommand}"><Label Classes="icon-only" Content="&#xE272;"></Label></Button></Grid></Border></Grid></Window>

 

7.7 Views\SettingsPageView.axaml

与 MainView 类似,SettingsPageView 中的所有带图标的按钮也被替换成了我们新的 IconButton

关键修改点解释:

  • 代码简化:这里的修改效果更加明显。对于那些内容只是简单文本的按钮,例如“Release License”,之前的XAML结构被极大地简化了。
  • 统一接口:现在,无论是只需要文本的按钮,还是需要图标+文本的按钮,我们都使用同一个 IconButton 控件,通过 IconText 和 Content 属性来控制其显示,使得API非常统一和清晰。
<UserControl xmlns="https://github.com/avaloniaui"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:vm="clr-namespace:AvaloniaApplication2.ViewModels"mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="900"Background="{DynamicResource PrimaryBackground}"Foreground="{DynamicResource PrimaryForeground}"x:DataType="vm:SettingsPageViewModel"x:Class="AvaloniaApplication2.Views.SettingsPageView"><Design.DataContext><vm:SettingsPageViewModel></vm:SettingsPageViewModel></Design.DataContext><Grid ColumnDefinitions="*, *" RowDefinitions="Auto, *"><!-- Header 未更改 --><Grid Name="HeaderGrid" Grid.ColumnSpan="2"><Image Source="{SvgImage /Assets/Images/background-settings.svg}" Height="160" Stretch="UniformToFill"></Image><StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"><Label HorizontalAlignment="Center" Content="Settings"></Label><Label HorizontalAlignment="Center" Content="Version 3.0.0.2 Beta"></Label><Label HorizontalAlignment="Center" Content="Compiled Jul 07 2025"></Label></StackPanel></Grid><!-- Left side content --><StackPanel Grid.Column="0" Grid.Row="1" Spacing="10" Margin="15"><!-- General --><StackPanel><Label Content="General"></Label><Grid ColumnDefinitions="*, Auto" RowDefinitions="Auto, Auto, Auto"><!-- Release license --><TextBlock TextWrapping="Wrap" Text="Remove license of BatchProcess from this machine and release the license back to the server ready to be transferred to another machine."></TextBlock><!-- 关键修改:替换为 IconButton --><IconButton IconText="&#xE2FE;" Content="Release License" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><!-- Skip Files --><TextBlock Grid.Row="1" Grid.Column="0" TextWrapping="Wrap" Text="Skip files if only Open, Save (Optional) and Close are Valid actions."></TextBlock><CheckBox Grid.Row="1" Grid.Column="1"></CheckBox><!-- Duplicate Entries --><TextBlock Grid.Row="2" Grid.Column="0" TextWrapping="Wrap" Text="Allow duplicate entries of the same file in project list"></TextBlock><CheckBox Grid.Row="2" Grid.Column="1"></CheckBox></Grid></StackPanel><!-- Location --><StackPanel><Label Content="Location"></Label><Grid ColumnDefinitions="*, Auto"><StackPanel><TextBlock TextWrapping="Wrap" Text="Add or remove the locations to search for Reporter Templates, Macros, Actions and other custom files or templates."></TextBlock><TextBlock TextWrapping="Wrap" Text="All sub-directories will be searched automatically"></TextBlock></StackPanel><Button Grid.Column="1" Content="+ Folder" HorizontalAlignment="Stretch"></Button></Grid><ListBox ItemsSource="{Binding LocationPaths}"></ListBox></StackPanel></StackPanel><!-- Right Side Content --><StackPanel Grid.Column="1" Grid.Row="1" Spacing="10" Margin="15"><!-- SolidWorks Host (未更改) --><StackPanel><Label Content="SolidWorks Host"></Label><TextBlock TextWrapping="Wrap">BatchProcess can work locally on the current machine, or on any machine accessibleover the network or even internet.<LineBreak /><LineBreak />Enter the machines IP address, network name or localhost for this machine.</TextBlock><ComboBox></ComboBox><Label Content="Connection established"></Label></StackPanel><!-- PDM Enterprise --><StackPanel Spacing="15"><Label Content="PDM Enterprise"></Label><TextBlock TextWrapping="Wrap" Text="If you are using PDM Enterprise enter the credentials below and test login. BatchProcess can then automatically handle checking in and out files from PDM Enterprise."></TextBlock><Grid ColumnDefinitions="*, *, *"><ComboBox HorizontalAlignment="Stretch"></ComboBox><TextBox Grid.Column="1"></TextBox><TextBox Grid.Column="2"></TextBox></Grid><StackPanel Orientation="Horizontal"><!-- 关键修改:替换为 IconButton --><IconButton IconText="&#xE23E;" Content="Login" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><IconButton IconText="&#xE094;" Content="Refresh Vault" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton></StackPanel><Label Content="Connection Established"></Label></StackPanel><!-- Setting Cache --><StackPanel Spacing="15"><Label Content="Setting Cache"></Label><TextBlock TextWrapping="Wrap">Various settings are stored locally including Processes, Actions, Macros, Reports and History. <LineBreak /><LineBreak />If you are experiencing issues you can try clearing the cache (this won't remove the license).</TextBlock><StackPanel Orientation="Horizontal"><!-- 关键修改:替换为 IconButton --><IconButton IconText="&#xEC54;" Content="Clear Cache" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><IconButton IconText="&#xE5DE;" Content="Export Cache" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><IconButton IconText="&#xE20C;" Content="Import Cache" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton></StackPanel></StackPanel></StackPanel></Grid>
</UserControl>

 

http://www.hskmm.com/?act=detail&tid=14982

相关文章:

  • matter 协议的架构;
  • matter 协议解析;
  • 9月23日
  • Nordic 的支持对Matter 协议的支持;
  • nRF54LM20A USB
  • nRF54LM20A GRTC
  • 2025年10款最佳生产力提效chrome插件推荐,亲测有用
  • Avalonia 学习笔记06. Page Layout(页面布局)
  • 发表第一篇文章,谈谈对软件工程的理解
  • nRF54LM20A 芯片分析;
  • 第二天
  • 内部类
  • NRF54L15 两者结合的jlink保护机制(硬件+软件)
  • 软件测试员的核心技能:一文掌握等价类划分与边界值分析
  • 《CBI 技术有聊》对话 OpenCSG:智能体落地困境与企业转型的必然路径
  • 个人对软件工程的理解
  • 9/23
  • NUMERICAL RESULT (2025/09/23)
  • 数组入门:从零基础到排序算法 - 教程
  • 用C/C++重构PowerShell:全面绕过安全机制的技术解析
  • Optuna v4.5新特性深度解析:GPSampler实现约束多目标优化
  • 题解:P4769 [NOI2018] 冒泡排序
  • 2025/9/23
  • Tita:更频繁的绩效考核周期的好处
  • 详细介绍:【Linux】Linux文件系统详解:从磁盘到文件的奥秘
  • Which side of a 2d curve is a point on
  • 20250923
  • CCPC秦皇岛 2023 M Inverted
  • 大三上第一篇日志
  • 0923模拟赛总结