在本章节中,我们的目标是创建一个可复用的、带图标的按钮控件,以简化我们在视图(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
关键修改点解释:
-
<ControlTheme x:Key="{x:Type IconButton}" TargetType="IconButton">
- 用途:这行代码声明了我们正在定义一个控件主题。
TargetType="IconButton"
指定了这个主题是为我们自己的IconButton
控件设计的。它会成为IconButton
的默认外观。
- 用途:这行代码声明了我们正在定义一个控件主题。
-
<ContentPresenter.ContentTemplate>
- 用途:我们不再让
ContentPresenter
简单地显示内容,而是为它提供了一个DataTemplate
(数据模板)。这个模板定义了内容的具体结构:一个水平排列的StackPanel
,里面包含一个用于显示图标的Label
和一个用于显示主要内容的ContentControl
。
- 用途:我们不再让
-
图标绑定:
Content="{Binding $parent[IconButton].IconText}"
- 用途:这是
DataTemplate
内部的绑定语法。$parent[IconButton]
的意思是“从当前位置向上查找,找到第一个名为IconButton
的父控件”,然后.IconText
表示绑定到该控件的IconText
属性。这样,我们在XAML中设置的IconText
就能正确地显示为图标了。 - 注意:
Label
的Classes="icon"
是为了能让AppDefaultStyles.axaml
中定义的图标字体样式应用到这个Label
上。
- 用途:这是
-
内容和数据上下文绑定
- 用途:
<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="" Content="Click Me!" /><IconButton IconText="" 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
的后台代码文件。
关键修改点解释:
-
public class IconButton : Button
- 用途:我们将基类从默认的
TemplatedControl
修改为了Button
。这是至关重要的一步。通过继承Button
,我们的IconButton
自动获得了按钮的所有核心功能,例如Click
事件、Command
绑定、可点击性等。它现在“是”一个按钮了。
- 用途:我们将基类从默认的
-
IconTextProperty
- 用途:我们在这里定义了一个新的
StyledProperty
(样式化属性),名为IconText
。StyledProperty
是 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
关键修改点解释:
: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=""></Label><Label Classes="akko" Content="Home"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content=""></Label><Label Classes="akko" Content="Process"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content=""></Label><Label Classes="akko" Content="Actions"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content=""></Label><Label Classes="akko" Content="Macros"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content=""></Label><Label Classes="akko" Content="Reporter"></Label></StackPanel></Button><Button HorizontalAlignment="Stretch"><StackPanel Orientation="Horizontal"><Label Classes="icon" Content=""></Label><Label Classes="akko" Content="History"></Label></StackPanel></Button><Button><Label Classes="icon-only" Content=""></Label></Button><Button Classes="transparent" Grid.Row="1"><Label Classes="icon-only" Content=""></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
关键修改点解释:
<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
关键修改点解释:
[assembly: XmlnsDefinition(...)]
- 用途:这是一个程序集级别的特性(Attribute),它将一个 C# 命名空间(
AvaloniaApplication2.Controls
)映射到一个 XML 命名空间(https://github.com/avaloniaui
)。 - 原因:这是一个非常有用的“语法糖”。添加它之后,我们就可以在 XAML 中直接使用
<IconButton />
,而不需要先在文件顶部定义一个像xmlns:c="clr-namespace:AvaloniaApplication2.Controls"
这样的前缀,然后再使用<c:IconButton />
。它让我们的自定义控件使用起来和 Avalonia 的内置控件一样方便、自然。
- 用途:这是一个程序集级别的特性(Attribute),它将一个 C# 命名空间(
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="" HorizontalAlignment="Stretch" Classes.active="{Binding HomePageIsActive}" Command="{Binding GoToHomeCommand}"><Label Classes="akko" Content="Home" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="" HorizontalAlignment="Stretch" Classes.active="{Binding ProcessPageIsActive}" Command="{Binding GoToProcessCommand}"><Label Classes="akko" Content="Process" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="" HorizontalAlignment="Stretch" Classes.active="{Binding ActionsPageIsActive}" Command="{Binding GoToActionsCommand}"><Label Classes="akko" Content="Actions" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="" HorizontalAlignment="Stretch" Classes.active="{Binding MacrosPageIsActive}" Command="{Binding GoToMacrosCommand}"><Label Classes="akko" Content="Macros" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="" HorizontalAlignment="Stretch" Classes.active="{Binding ReporterPageIsActive}" Command="{Binding GoToReporterCommand}"><Label Classes="akko" Content="Reporter" IsVisible="{Binding SideMenuExpanded}"></Label></IconButton><IconButton IconText="" 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=""></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="" 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="" Content="Login" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><IconButton IconText="" 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="" Content="Clear Cache" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><IconButton IconText="" Content="Export Cache" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton><IconButton IconText="" Content="Import Cache" Grid.Column="1" HorizontalAlignment="Stretch"></IconButton></StackPanel></StackPanel></StackPanel></Grid> </UserControl>