从 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)才能使用它。
布局
以前,可以通过获取布局根并在布局管理器上调用方法来实现完整的布局过程:
((ILayoutRoot)control).LayoutManager.ExecuteLayout();
LayoutManager 不再从 ILayoutRoot 暴露出来,而是与 WPF/UWP 一样,在任何控件上调用 UpdateLayout 方法:
control.UpdateLayout();
在 0.10.x 版本中,使用 ILayoutable 来获取先前的测量约束和排列边界。由于 ILayoutable 不再可用,现在可以从 LayoutInformation 中获取它们:
Size? LayoutInformation.GetPreviousMeasureConstraint(Layoutable control)Rect? LayoutInformation.GetPreviousArrangeBounds(Layoutable control)
焦点
焦点管理器不再通过 FocusManager.Instance 访问,而是移至 TopLevel:
var focusManager = FocusManager.Instance;
替换为:
var focusManager = TopLevel.GetTopLevel(control).FocusManager;
此外,IFocusManager API 已更改。
- 要获取当前聚焦的元素,请使用
IFocusManager.GetFocusedElement() - 要聚焦一个控件,请使用
control.Focus()
目前没有监听 IFocusManager 的焦点更改的事件。要监听焦点更改,请添加一个监听器到 InputElement.GotFocusEvent:
InputElement.GotFocusEvent.AddClassHandler<InputElement>((element, args) => { });
键盘设备也是如此,不再可访问。请使用与焦点相关的相同 API 进行替换。
更多信息请参见#11407。
视觉树
在 0.10.x 版本中,使用 IVisual 暴露控件的视觉父级和视觉子级。由于 IVisual 不再可用,现在将其作为扩展方法暴露在 Avalonia.VisualTree 命名空间中:
using Avalonia.VisualTree;
var visualParent = control.GetVisualParent();
var visualChildren = control.GetVisualChildren();
渲染
某些控件的 Render 方法现在已被标记为 sealed。这是因为计划让这些控件使用组合原语而不是通过 DrawingContext 进行渲染。
如果您的控件的 Render 方法被重载但现在被标记为 sealed,请考虑使用一个基类,例如不使用 Border 而是使用 Decorator。请注意,您现在需要自行绘制背景/边框。
更多信息请参见#10299。
定位器
AvaloniaLocator 不再可用。现在大多数通过定位器可用的服务都有替代方法:
AssetLoader现在是一个静态类,具有所有旧方法。IPlatformSettings已移到TopLevel.PlatformSettings和Application.PlatformSettings。请注意,始终应优先使用特定顶级(窗口)的设置,而不是全局设置。IClipboard已移到TopLevel.Clipboard。请注意,Application.Clipboard也已被移除。PlatformHotkeyConfiguration已移到PlatformSettings.HotkeyConfiguration。
一些应用程序将 AvaloniaLocator 用作通用服务定位器。这从未是 AvaloniaLocator 的预期用法,这些应用程序应该转向专为此目的设计的服务定位器或依赖注入容器,例如 Splat 或 Microsoft.Extensions.DependencyInjection。
杂项/高级场景
IRenderer/DeferredRenderer/ImmediateRenderer现在已移除。出于性能原因,不再允许提供自己的渲染器,现在所有内容都使用新的组合渲染器。Renderer.Diagnostics现在是RendererDiagnosticsICustomDrawOperation.Render现在使用ImmediateDrawingContext而不是DrawingContext- 在直接返回
Task的方法中,在对Dispatcher.UIThread.InvokeAsync进行调用时,请在调用结尾添加.GetTask()。 IRenderRoot.RenderScaling已移至TopLevel.RenderScalingLightweightObservableBase和SingleSubscriberObservableBase现在已变为内部类。这些实用程序类设计用于 Avalonia 中的特定目的,并不打算由客户端使用,因为它们不能处理某些边缘情况。使用System.Reactive提供的机制来创建可观察对象,例如Observable.Create- 在绑定到方法时,方法必须没有参数或仅有一个对象参数。
OpenFileDialog和SaveFileDialog现在已移除。对于文件系统存储服务,请在TopLevel使用IStorageProvider。