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
关键修改点解释:
- 
<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 accessible over 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>
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号