比较事件驱动型 UI 和数据绑定型 UI
- 6 分钟
事件驱动型用户界面 (UI) 是围绕控件公开的事件设计的。 这些事件可以与触发事件时调用的事件处理程序代码相关联。 例如,假设你有一个按钮,单击时,会执行长时间运行的操作。 分配给 Clicked 事件的事件处理程序可以启动该操作,然后将按钮的 IsEnabled 属性设置为 false,以防止在操作运行时再次单击该按钮。
数据绑定 UI 使用数据绑定来呈现数据并与数据交互。 控件的属性绑定到数据对象的属性,并且这些绑定可以检测属性的更改。 使用上一示例时,考虑执行长时间运行操作的按钮。 将 IsEnabled 属性绑定到数据对象的 IsBusy 属性,而不是在代码隐藏中禁用按钮。 每当数据对象变为“忙碌”时,按钮的启用状态将自动更改为匹配。
使用事件和代码隐藏的优缺点
使用控件的带代码隐藏的事件处理程序是为 UI 设计应用逻辑的一种快速便捷的方法。 可以使用代码来调用服务以获取数据、对该数据执行操作以及与页面上的控件交互。 该代码用于让 UI 和数据保持同步。
考虑气象服务应用的示例。 以下 XAML 片段包含一个简单的 UI 按钮,用户选择该按钮即可获取最新数据并使用湿度来更新 UI。
<VerticalStackLayout Margin="10">
<HorizontalStackLayout Spacing="20">
<Label Text="Postal Code:" VerticalOptions="Center" />
<Entry x:Name="PostalCode" WidthRequest="100" />
<Button x:Name="RefreshWeatherButton" Text="Refresh" WidthRequest="200" Clicked="RefreshWeatherButton_Clicked" />
</HorizontalStackLayout>
<Label x:Name="Humidity" Text="Humidity: ?" />
</VerticalStackLayout>
此示例中有三个命名控件:
- 名为 PostalCode 的
Entry控件。 - 名为 RefreshWeatherButton 的
Button控件。 - 名为“湿度”的
Label控件。
RefreshWeatherButton 有一个为 Clicked 事件声明的事件处理程序。 单击该按钮时,事件处理程序将使用输入控件中 PostalCode 输入的数据查询天气服务以获取最新天气预报,并将标签的文本设置为 Humidity 当前湿度。
private void RefreshWeatherButton_Clicked(object sender, EventArgs e)
{
WeatherService.Location = PostalCode.Text;
WeatherService.Refresh();
Humidity.Text = $"Humidity: {WeatherService.Humidity}";
}
在这一事件处理程序中,三个控件通过代码隐藏彼此紧密耦合并与数据紧密耦合。
这种设计非常适合小型 UI,但一旦 UI 变得复杂,维护紧密耦合的代码隐藏可能就会变得很麻烦。 如果删除或更改控件,则必须清理使用这些 UI 控件的所有代码,其中可能包括事件处理程序。 如果决定重新设计 UI,则还需要重构大量代码。 当支持数据结构发生变化时,必须深入研究每个 UI 的代码才能保持同步。
数据绑定很有用
可以在 XAML 或代码中实现数据绑定,但它们在 XAML 中更为常见,它们有助于减少代码隐藏文件大小。 将事件处理程序中的过程代码替换为声明性代码或标记时,将简化和阐明应用。 由于绑定不需要代码隐藏,因此你可以轻松地创建、更改或重新设计 UI,使之与你所希望的数据呈现方式相适应。
让我们采用上一部分的示例,但将其更新为使用数据绑定:
<VerticalStackLayout Margin="10">
<HorizontalStackLayout Spacing="20">
<Label Text="Postal Code:" VerticalOptions="Center" />
<Entry Text="{Binding Location, Mode=OneWayToSource}" WidthRequest="100" />
<Button Text="Refresh" Command="{Binding RefreshWeather}" WidthRequest="200" />
</HorizontalStackLayout>
<Label Text="{Binding Humidity}" />
</VerticalStackLayout>
可以发现进行了数据绑定的属性,它们使用 XAML 扩展语法 {Binding ...} 作为属性的值。 别担心具体细节:在本模块的后面部分,我们将介绍这一点。
相同的三个控件在 XAML 中声明,但其中没有一个控件被命名,因为不需要名称:
Entry控件:此控件的Text属性关联到名为Location的属性。Button控件:按钮Command的属性绑定到名为RefreshWeather的属性。Command是按钮上的一个属性,会在按下按钮时调用代码。 它是数据绑定中使用的Clicked事件的替代项。Label控制:这个Text属性绑定到名为Humidity的属性。
在这个简单的 UI 中,所有代码隐藏都被消除了。 尽管有可能,但移除所有代码隐藏并不是数据绑定的重点。 代码隐藏仍然占有一席之地。 你实施多少数据绑定取决于你自己。
现在,UI 与数据对象松散耦合。 为什么是松散耦合而不是紧密耦合? 原因在于绑定的评估方式。 每个控件都有一个 BindingContext 属性。 如果未设置上下文,则使用父控件的上下文,依此类推,直到评估 XAML 的根。 评估绑定时,将检查上下文的对象实例是否具有所需的属性,例如标签控件的 Text 绑定(绑定到上下文的 Humidity 属性)。 如果上下文中不存在 Humidity,则什么也不会发生。
由于 UI 松散耦合,因此你可以重新设计 UI,而无需担心代码中断。 但是,可能会破坏功能。 例如,你可以删除该按钮,应用仍然可以编译并运行,但你没有办法刷新天气。 另一方面,你可以将 Entry 和 Button 控件替换为单个 SearchBar 控件。 该控件允许你输入文本并调用命令。
<SearchBar Text="{Binding Location, Mode=OneWayToSource}" SearchCommand="{Binding RefreshWeather}" />
如你所见,在 UI 设计中使用数据绑定有助于发展和更改 UI,无需做太多工作。 它会自动使 UI 与数据保持同步,并且应用逻辑与 UI 分离。