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

  • iOSMain method > AppDelegate > App > ContentPage
  • AndroidMainActivity > App > ContentPage
  • UWPMain 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(LogicalTreeHelperVisualTreeHelper)来检查这两个对象图。

在 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}" />