从 0.10 升级
Avalonia 11 版本引入了许多与 0.10 版本不兼容的变化。以下指南涵盖了最常见的更改,并提供了解决方法。
更新项目
- 将 Avalonia 包更新为 11.x 版本。
- Avalonia.Desktop 包不再包含主题,因此您需要添加以下任一包引用:
Avalonia.Themes.FluentAvalonia.Themes.Simple
- 移除对
XamlNameReferenceGenerator包的引用,Avalonia 现在默认包含这个生成器。 - 如有需要,将
<LangVersion>更新至至少 9,以便使用仅限初始化属性 (init-only properties)。 - 如果需要与 0.10 版本相同的字体,还需包括
Avalonia.Fonts.Inter包,并在应用程序构建器中添加.WithInterFont()。在 11.0 版本中,默认情况下不包含任何自定义字体。
主题处理
在 0.10 版本中,主题直接在Application.axaml文件的Application.Styles标签内指定。以下是示例:
<Application.Styles>
<FluentTheme Mode="Light"/>
</Application.Styles>
在这个示例中,FluentTheme标签的Mode属性用于指定主题模式,可以是 "Light" 或 "Dark"。
引入了一个新属性RequestedThemeVariant,用于Application标签,该属性用于设置应用程序的主题,如果指定了,则会覆盖系统当前的主题。如果要遵循系统当前的主题,可以将其设置为 "Default"。其他可用选项为 "Dark" 和 "Light"。
以下示例展示了如何使用该属性:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ILoveAvaloniaUI.App"
xmlns:local="using:ILoveAvaloniaUI"
RequestedThemeVariant="Default">
FluentTheme标签不再需要Mode属性,可以将其留空:
<Application.Styles>
<FluentTheme />
</Application.Styles>
主题字典和主题变体
根据 PR #8166,现在方法 Styles.TryGetResource 需要一个可为空的 ThemeVariant 参数。这允许用户指定 Light、Dark 和 Default。
使用 ThemeVariant.Default 作为键将特定的主题字典标记为一种回退(fallback),以防在其他主题字典中找不到主题变体或资源键。
除了内置的 Light、Dark 和 Default 值外,任何对象值都可以用作键(因为它包装在 ThemeVariant(object key) 结构中)。如果开发人员希想要在 XAML 代码中定义多个自定义主题作为静态属性并从中引用它们,则可以在此处使用 {x:Static} 标记扩展。
// 以前
bool TryGetResource(object key, out object? value)
// Avalonia v11
bool TryGetResource(object key, ThemeVariant? theme, out object? value)
System.Reactive/Observables
Avalonia 不再依赖System.Reactive。如果您使用了响应式特性,请将System.Reactive包添加到您的项目中。
如果您不需要System.Reactive的全部功能,只是想对IObservable<T>进行简单的订阅,可以使用 Avalonia 提供的实用类AnonymousObserver<T>,例如:
observable.Subscribe(new AnonymousObserver<string>(() => { /* 当可观察对象发生更改时执行的代码 */ }));
如果需要订阅属性或事件更改,可以使用 AddClassHandler 而不是 observables。
更新接口
在 Avalonia 11 中移除了许多接口。您可以通过全局查找/替换将每个接口替换为其具体类型:
IAvaloniaObject->AvaloniaObjectIBitmap->BitmapIContentPresenter->ContentPresenterIControl->ControlIInteractive->InteractiveIItemsPresenter->ItemsPresenterILayoutable->LayoutableIPanel->PanelIStyledElement->StyledElementITemplatedControl->TemplatedControlIVisual->Visual
如果您有自己的接口派生自上述接口之一,您需要移除接口继承,并在使用点上显式地将其转换为具体类。
推荐的可选择更改:
IStyleable接口现在已被弃用。在 Avalonia 0.10.x 中,要覆盖控件的样式键,您需要实现IStyleable并为StyleKey添加显式接口实现:
class MyButton : Button, IStyleable
{
Type IStyleable.StyleKey => typeof(Button);
}
在 Avalonia 11 中,IStyleable引用将显示弃用警告。改为使用以下代码:
class MyButton : Button
{
protected override Type StyleKeyOverride => typeof(Button);
}
更多信息请参见#11380。
视图
以.axaml/.axaml.cs(或.xaml/.xaml.cs)形式的视图现在具有自动生成的 C# 代码。要实现此目的,请执行以下操作:
- 将 .cs 文件中的类设置为
partial。 - 移除
private void InitializeComponent()方法。 - 在构造函数中不要移除对
InitializeComponent()的调用,因为该方法现在是生成的方法,仍然需要调用。 - 从构造函数中移除
this.AttachDevTools()调用——InitializeComponent现在有一个参数,该参数控制是否在调试模式下附加 DevTools,默认值为true。
以前,要查找在 XAML 文件中声明的具有名称的控件,需要调用 this.FindControl<T>(string name) 或 this.GetControl<T>(string name)。现在不再需要——在 XAML 文件中带有 Name 或 x:Name 属性的控件将自动在类中生成一个字段,用于访问命名的控件( 与 WPF/UWP 等类似)。
请注意,此源生成器仅适用于 C#。对于 F#,没有进行任何更改。
ItemsControl
ItemsControl 和派生类(例如 ListBox 和 ComboBox)现在都有 Items 属性和 ItemsSource 属性,与 WPF/UWP 类似。
Items 是一个只读集合属性,预先填充数据,而 ItemsSource 是读写版本,其默认值为 null。
将所有绑定到 Items 的绑定更改为绑定到 ItemsSource:
<ListBox Items="{Binding Items}">
替换为:
<ListBox ItemsSource="{Binding Items}">
此外:
ListBox.VirtualizationMode已移除,虚拟化模式通过更改ItemsPanel实现:- 禁用虚拟化,请使用
StackPanel。 - 启用虚拟化,请使用
VirtualizingStackPanel。
- 禁用虚拟化,请使用
Carousel.IsVirtualizing已移除,现在只有Carousel的虚拟化模式。- 项容器查找已移至
ItemsControl,类似 UWP(旧方法保留在 ItemContainerGenerator 上,并标记为 [Obsolete]):ItemsControl.ContainerFromIndex(object item)。ItemsControl.IndexFromContainer(Control container)。
ItemsPresenter上的Items和ItemTemplate属性已移除。在控件模板中对这些属性的模板绑定可以直接移除。
Classes
StyledElement.Classes 现在是一个只读属性。在对象初始化器中使用时,以前的代码如下:
var c = new Control
{
Classes = new Classes("foo", "bar"),
};
现在可以更改为:
var c = new Control
{
Classes = { "foo", "bar" },
};
在对象初始化器之外操作 Classes 集合时,请使用标准的 IList<string> 方法。
更多信息请参见#11013。
Windows
TopLEvel.PlatformImpl API 不再适用于 Window 等控件。相关方法已移至 TopLevel、WindowBase 或 Window 自身:
window.PlatformImpl.Handle变为window.TryGetPlatformHandle()window.PlatformImpl.BeginMove(e)变为window.BeginMove()window.PlatformImpl.Resized变为window.Resized
AssetLoader
IAssetLoader 接口不再可用。请使用静态类 AssetLoader:
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
var bitmap = new Bitmap(assets.Open(new Uri(uri)));
替换为:
var bitmap = new Bitmap(AssetLoader.Open(new Uri(uri)));
OnPropertyChanged
虚拟的 AvaloniaObject.OnPropertyChanged 方法现在是非泛型的。将:
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
替换为:
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
还有一种方法可以从 AvaloniaPropertyChangedEventArgs 中获取旧值和新值,而无需装箱:
- 将
change.NewValue.GetValueOrDefault<T>()替换为change.GetNewValue<bool>() - 将
change.OldValue.GetValueOrDefault<T>()替换为change.GetOldValue<bool>() - 您还可以使用
change.GetOldAndNewValue<T>()来获取这两个值
更多信息请参见#7980。
事件
以下事件已更名:
PointerEnter->PointerEnteredPointerLeave->PointerExitedContextMenuContextMenuClosing->ClosingContextMenuOpening->Opening
MenuBaseMenuClosed->ClosedMenuOpened->Opened
RoutedEventArgs.Source 的类型从 IInteractive 更改为 object: :需要将其转换为具体类型(如 Control)才能使用它。