WPF 与Xamarin.Forms 应用程序生命周期
Xamarin.Forms 从之前的基于 XAML 的框架借鉴了大量设计理念,尤其是 WPF。 然而,在其他方面,它有明显的偏离,这对试图迁移过去的人来说可能是一个棘手的问题。 本文档试图指出这些问题的其中一部分,并在可能的情况下提供将 WPF 知识桥接到 Xamarin.Forms 的指导。
应用生命周期
WPF 和 Xamarin.Forms 的应用程序生命周期类似。 两者都从外部(平台)代码开始,并通过方法调用启动 UI。 区别在于,Xamarin.Forms 始终在特定于平台的程序集中启动,然后初始化并创建应用的 UI。
WPF
Main method > App > MainWindow
注意
默认情况下,Main
方法在代码中自动生成且不可见。
Xamarin.Forms
- iOS –
Main method > AppDelegate > App > ContentPage
- Android –
MainActivity > App > ContentPage
- UWP –
Main method > App(UWP) > MainPage(UWP) > App > ContentPage
应用程序类
WPF 和 Xamarin.Forms 都有一个 Application
类,该类创建为单一实例。 在大多数情况下,应用将派生自此类以提供自定义应用程序,尽管 WPF 中并不严格要求这样做。 这两者都公开 Application.Current
属性以查找创建的单一实例。
全局属性 + 持久性
WPF 和 Xamarin.Forms 都有一个 Application.Properties
字典,可在其中存储全局应用级对象,以便在应用程序中的任意位置访问它们。 主要区别在于,Xamarin.Forms 将在应用暂停时保留集合中存储的任何基元类型,并在重新启动时重新加载它们。 WPF 不会自动支持该行为 - 相反,大多数开发人员依赖于独立存储或使用内置的 Settings
支持。
定义页面和可视化树
WPF 将 Window
用作任何顶级视觉元素的根元素。 这会在 Windows 世界中定义用于显示信息的 HWND。 可以像在 WPF 中一样同时创建和显示任意数量的窗口。
在 Xamarin.Forms 中,顶级视觉对象始终由平台定义,例如在 iOS 上,它是 UIWindow
。 Xamarin.Forms 使用 Page
类将其内容呈现到这些本机平台表示形式中。 Xamarin.Forms 中的每个 Page
都表示应用程序中唯一的“页面”,一次只显示一个。
WPF Window
和 Xamarin.Forms Page
都包含一个用于影响显示标题的 Title
属性,并且都有一个 Icon
属性来显示页面的特定图标(请注意,标题和图标并不总是在 Xamarin.Forms 中可见)。 此外,还可以更改两者的常见视觉属性,例如背景色或图像。
从技术上看,可以呈现到两个单独的平台视图(例如,定义两个 UIWindow
对象,并将第二个呈现到外部显示器或 AirPlay),它需要特定于平台的代码才能执行此操作,这不是 Xamarin.Forms 本身直接支持的功能。
视图
这两个框架的视觉层次结构相似。 由于 WPF 对 WYSIWYG 文档的支持,WPF 要更深一些。
WPF
DependencyObject - base class for all bindable things
Visual - rendering mechanics
UIElement - common events + interactions
FrameworkElement - adds layout
Shape - 2D graphics
Control - interactive controls
Xamarin.Forms
BindableObject - base class for all bindable things
Element - basic parent/child support + resources + effects
VisualElement - adds visual rendering properties (color, fonts, transforms, etc.)
View - layout + gesture support
视图生命周期
Xamarin.Forms 主要面向移动方案。 因此,当用户与应用程序交互时,应用程序会激活、暂停和重新激活。 这类似于在 WPF 应用程序中单击 Window
以外的区域,并且有一组方法和相应的事件可以替代或挂钩以监视此行为。
目的 | WPF 方法 | Xamarin.Forms 方法 |
---|---|---|
初始激活 | ctor + Window.OnLoaded | ctor + Page.OnStart |
显示 | Window.IsVisibleChanged | Page.Appearing |
已隐藏 | Window.IsVisibleChanged | Page.Disappearing |
暂停/丢失焦点 | Window.OnDeactivated | Page.OnSleep |
激活/获取焦点 | Window.OnActivated | Page.OnResume |
已解决 | Window.OnClosing + Window.OnClosed | 不适用 |
两者都支持隐藏/显示子控件,在 WPF 中,它是一个三态属性 IsVisible
(可见、隐藏和折叠)。 在 Xamarin.Forms 中,它只通过 IsVisible
属性可见或隐藏。
Layout
页面布局发生在 WPF 中发生的同一 2 次传递(度量/排列)中。 可以通过替代 Xamarin.Forms Page
类中的以下方法来钩入页面布局:
方法 | 目的 |
---|---|
OnChildMeasureInvalidated | 子级的首选大小已更改。 |
OnSizeAllocated | 已为页面分配宽度/高度。 |
LayoutChanged 事件 | 页面的布局/大小已更改。 |
没有今天调用的全局布局事件,也没有像在 WPF 中找到的全局 CompositionTarget.Rendering
事件。
常见布局属性
WPF 和 Xamarin.Forms 都支持 Margin
来控制元素周围的间距,并支持 Padding
来控制元素内的间距。 此外,大多数 Xamarin.Forms 布局视图都具有控制间距的属性(例如行或列)。
此外,大多数元素都具有属性来影响它们在父容器中的定位方式:
WPF | Xamarin.Forms | 目的 |
---|---|---|
水平对齐 | HorizontalOptions | Left/Center/Right/Stretch 选项 |
VerticalAlignment | VerticalOptions | Top/Center/Bottom/Stretch 选项 |
注意
这些属性的实际解释取决于父容器。
布局视图
WPF 和 Xamarin.Forms 都使用布局控件来定位子元素。 在大多数情况下,它们在功能方面彼此非常接近。
WPF | Xamarin.Forms | 布局样式 |
---|---|---|
StackPanel | StackLayout | 从左到右,或从上到下无限堆叠 |
Grid | 网格 | 表格格式(行和列) |
DockPanel | 不适用 | 停靠到窗口边缘 |
画布 | AbsoluteLayout | 像素/坐标定位 |
WrapPanel | 不适用 | 包装堆栈 |
不适用 | RelativeLayout | 基于规则的相对定位 |
注意
Xamarin.Forms 不支持 GridSplitter
。
这两个平台都使用附加属性来微调子级。
渲染
WPF 和 Xamarin.Forms 的呈现机制截然不同。 在 WPF 中,你创建的控件将内容直接呈现到屏幕上的像素。 WPF 有两个对象图(树)来表示这一点 - 逻辑树表示代码或 XAML 中定义的控件,可视化树表示由视觉元素(通过虚拟绘图方法)直接执行的,或通过 XAML 定义的 ControlTemplate
(可替换或自定义)执行的屏幕上发生的实际呈现。 通常,可视化树更为复杂,因为它包括控件周围的边框、隐式内容的标签等。WPF 包含一组 API(LogicalTreeHelper
和 VisualTreeHelper
)来检查这两个对象图。
在 Xamarin.Forms 中,你在 Page
中定义的控件实际上只是简单的数据对象。 它们类似于逻辑树表示形式,但绝不会自行呈现内容。 相反,它们是影响元素呈现的数据模型。 实际的呈现由一组单独的视觉呈现器完成,这些呈现器映射到每个控件类型。 这些呈现器通过特定于平台的 Xamarin.Forms 程序集在每个特定于平台的项目中注册。 可在此处查看列表。 除了替换或扩展呈现器之外,Xamarin.Forms 还支持效果,它可用于在每个平台上影响本机呈现。
逻辑/可视化树
没有公开的 API 可在 Xamarin.Forms 中遍历逻辑树,但你可以使用反射来获取相同的信息。 例如,下面是一个方法,它使用反射来枚举逻辑子级。
图形
Xamarin.Forms 包含用于绘制基元的图形系统,称为 Shapes。 有关 Shapes 的详细信息,请参阅 Xamarin.Forms Shapes。 此外,还可以包含第三方库(如 SkiaSharp),以获取跨平台 2D 绘图。
资源
WPF 和 Xamarin.Forms 都有资源和资源字典的概念。 可以将任何对象类型放入具有键的某个 ResourceDictionary
,然后使用 {StaticResource}
查找其中不会更改的项,或者使用 {DynamicResource}
查找在运行时可在字典中更改的项。 用法和机制是相同的,但有一个区别:Xamarin.Forms 要求你定义 ResourceDictionary
以分配给 Resources
属性,而 WPF 会预先创建一个并为你分配它。
例如,请参阅以下定义:
WPF
<Window.Resources>
<Color x:Key="redColor">#ff0000</Color>
...
</Window.Resources>
Xamarin.Forms
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="redColor">#ff0000</Color>
...
</ResourceDictionary>
</ContentPage.Resources>
如果未定义 ResourceDictionary
,则会生成运行时错误。
样式
Xamarin.Forms 也完全支持样式,它可用于为构成 UI 的 Xamarin.Forms 元素设置主题。 它们支持触发器(属性、事件和数据)、通过 BasedOn
继承以及值的资源查找。 样式通过 Style
属性显式应用于元素,或者不提供资源键而隐式应用(就像 WPF 一样)。
设备样式
WPF 有一组预定义的属性(作为静态值存储在一组静态类上,例如 SystemColors
),这些属性以值和资源键的形式指示系统颜色、字体和指标。 Xamarin.Forms 也是类似,但它定义了一组设备样式来表示相同的内容。 这些样式由框架提供,并基于运行时环境(例如辅助功能)设置值。
WPF
<Label Text="Title" Foreground="{DynamicResource {x:Static SystemColors.DesktopBrushKey}}" />
Xamarin.Forms
<Label Text="Title" Style="{DynamicResource TitleStyle}" />