UWP 绑定 vs. XBind






4.43/5 (4投票s)
Xaml 绑定 vs. XBind
引言
不过,任何美好的事物都有其代价,例如性能下降和调试困难。
在 UWP 中,微软发布了 X:Bind——一种与旧 Binding 并行的全新 Binding 概念,旨在提高性能、改善调试并简化操作。
然而,从 Binding 到 x:Bind,发生了许多变化。
在这篇文章中,我将从语法、概念、性能、调试、功能等方面对这两种强大的技术进行对比,并提供我所能提供的一切。
幕后
{x:Bind} 仅仅是一个标记扩展,无法以编程方式创建或操作此类绑定。有关标记扩展的更多信息…
换句话说,xBind 是新一代的标记扩展,它不是一个可用的类。
此外,请注意,与 WPF 相比,标记扩展发生了变化,当时它们实际上是一个类,Binding 和 StaticResources 等都继承自 MarkupExtension 类型。
因此,Binding 本身实际上是 Windows.UI.Xaml.Data 命名空间中的一种新类型。这在某种程度上保留了 WPF 绑定的概念。
xBind & Binding 默认行为
xBind 不使用 DataContext.Properties,而是使用 c# 部分类中的字段/属性/函数。
(对于 WPF 程序员来说,这就像设置:DataContext=this;)
Binding 默认使用 Source 属性,而 Source 属性默认依赖于 DataContext 属性,而 DataContext 属性默认是 Null,这意味着不设置 Source 而使用 Binding 将无效。
Binding 不直接支持函数,只支持属性。
对于曾经处理过 WPF 的人来说,Commands(命令)是很熟悉的,但使用 xBind,我们有一些可选的事件绑定解决方案。我们可以直接绑定点击事件(或其他路由事件)到函数,但有一些规则:如果你使用 Click 事件绑定函数,该函数必须不带参数,或者与发送者/参数的原始 Click 路由事件处理函数类似。更重要的是,xBind 允许将标准的 UI 字段(文本、颜色等)绑定到函数,并且这里甚至可以带有参数的函数!甚至可以将两个函数一起绑定(使用 BindBack 关键字)。
x:Bind 和 INotifyPropertyChanged
使用 Binding 的程序员对这个接口很熟悉,它在 xBind 中仍然扮演着角色。然而,xBind 引入了一种名为 **Bindings.Update** 的新技术。在旧的(WPF 风格)绑定中,当模型中的数据发生变化,我们想刷新视图时,必须触发 PropertyChanged 事件(xaml 绑定在后台监听它)。
这很好也很简单,但它会导致额外的类和非常长的属性模式。现在,模型/ViewModel 类可以只是一个类,你不需要改变模型类中的任何东西,甚至不需要考虑绑定支持。唯一需要的是两个词:`Bindings.Update`(但仍然需要找到触发调用此方法的时机)。
x:Bind vs Binding 在 DataTemplates 中的应用
Binding 可以用于各种 DataTemplates。由于 Binding 是在运行时使用反射编译的,它们对于程序员的错误非常灵活且宽容。我们可以将 Binding 用于少数几个层级类型等等。但 xBind 有限制。
x:DataType 指令是必需的,而且它在本地 xaml 文件资源中非常方便,而在全局资源文件中则相当困难。
由于 x:bind 实际上使用代码隐藏项来评估数据,而 DataTemplates 本质上是用于呈现尚未实际存在的内容的模板,它只是一个如何呈现数据项的模式。所以,x:bind 再次查找代码隐藏,而 DataTemplate 是一个在运行时出现的虚拟 xaml。
但真正的问题是是否有必要使用它。是的,如果你想节省 Commands,或者绑定函数,或者喜欢预编译的语法验证(xBind 会检查你的属性类型等的语法),但除了其他优点之外,xBind 还提供了更好的性能(稍后讨论)。那么,在 DataTemplates 中也值得使用吗?
所以,我将比较三件事——加载时间、CPU、内存。
强调一下——我无论如何都使用 xBind 作为集合的数据源。
<GridView ItemsSource="{x:Bind Persons}" />
<DataTemplate x:Key="localPersonTemplatexBind" x:DataType="local:Person">
<StackPanel Background="Red">
<TextBlock Text="{x:Bind Id}" />
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</DataTemplate>
或者,
<DataTemplate x:Key="localPersonTemplateBinding" x:DataType="local:Person">
<StackPanel Background="Azure">
<TextBlock Text="{Binding Id}" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
所以,对于 Binding DataTemplate
加载时间:4.00 – 4.6 秒。
CPU 空闲时:0。
CPU 极端情况:约 20。
RAM 空闲时:85。
RAM 极端情况:85。
所以,对于 xBind DataTemplate
加载时间:3.9 – 4.3 秒。
CPU 空闲时:0。
CPU 极端情况:约 20。
RAM 空闲时:78。
RAM 极端情况:78。
所以,毫不意外但又令人惊讶…… x:Bind 影响了加载时间,以及大约 10% 的内存占用。(* 例如,基于单个机器的测试)。
x:Bind vs Binding 在性能方面
在之前有趣的 DataTemplates 深入研究之后,让我们再次看看性能。即使我们专注于 UWP,我也不能错过与 WPF 进行比较的机会。
此应用程序的基准测试比较是在发布模式下,包含 2000 个列表项(带有简单模板)。
Wpf 启动时间:3.6-3.7。
Wpf 空闲时 - CPU:0,RAM:93。
Wpf 鼠标悬停时 - CPU:15-20,RAM:94。
Wpf 极端情况:CPU 20-30,RAM 130。
Uwp Binding 启动时间:1.2-2。
Uwp Binding 空闲时 - CPU:0,RAM:32-36。
Uwp Binding 鼠标悬停时 - CPU:4.5,RAM:32-36。
Uwp Binding 极端情况:CPU 20-35,RAM 48。
Uwp x:Bind 启动时间:0.5-1.0。
Uwp x:Bind 空闲时 - CPU:0,RAM 20。
Uwp x:Bind 鼠标悬停时 - CPU:4-5,RAM:21。
Uwp x:Bind 极端情况:CPU 10-20,RAM 38。
所以,WUP(这里应为 UWP)比 WPF 好,而 xBind 比 Binding 好。
怎么做?为什么?
首先,通用应用程序更好,因为它们与绑定无关,这与新的应用程序架构和操作系统本身有关。
至于 Binding 本身,显然 Binding 使用反射,而 X:Bind 只是执行生成的、经过良好编译的代码,并且是类型安全的,所以它更快。
此外,xBind 迫使你编写更好、更谨慎的代码。
老实说,我不喜欢这种方法,但我喜欢结果。例如:
不再像 Binding 那样在 Visual-Tree 上进行相对遍历。
不再有默认的 Bind 模式(方向和更新),必须显式指定 Binding 模式,等等…
你可以在这个 帖子 中下载一个完整的性能演示应用程序。
x:Bind 与方法 & 事件
回到开头,Xbind 允许我们将事件直接绑定到函数,这很棒,它只是降低了 Command 模式的优先级。Binding 当然做不到。绑定到方法没有限制,你可以绑定带参数的、返回值等,当然还有默认的 EventHandler 签名,带或不带默认参数。
例如
<VariableSizedWrapGrid>
<Button Content="click to void method" Click="{x:Bind Foo, Mode=OneWay}" />
<Button Content="double method with args" Width="{x:Bind Foo2(100),Mode=OneWay}"/>
<Button Content="sting method" ToolTipService.ToolTip="{x:Bind Foo3(),Mode=OneWay}" />
<Button Content="Event pointer enterd method" PointerEntered="{x:Bind Foo4}" />
</VariableSizedWrapGrid>
//void no arguments function Bind to click event above
private void Foo()
{
}
//function that returns and receive input Bind to width property above
private double Foo2(int val)
{
return val+100;
}
//string function Bind to tooltip property above
private string Foo3()
{
return "Hello";
}
//void function Bind to PointerEntered event above
// the signiture must be as original eventhandler format
private void Foo4(object sender, PointerRoutedEventArgs e)
{
}
x:Bind 的调试 & 验证
调试,
对于使用标记语言的前端开发人员来说,调试 UI 层可能是一项艰巨的任务。例如,如果值没有正确到达视图,我们就会使用外部工具或编写调试组件。xBind 使这一切变得更容易,标记绑定会生成一个读取文件,我们可以通过它进行调试。
xBind & MVVM 架构
多年前,当 MVC/MVVM 变得流行起来,让程序员兴奋不已时,我们将一切都分成小的单元,这些小的单元共同协调应用程序的构建块。它开始于小型视图、ViewModel(无论视图优先还是 ViewModel 优先)、服务、DataTemplate 也被划分到独立的区域,样式和模板甚至可以存储在不同的程序集中等等。
现在,一切都稍有改变,但总的来说都一样。
XBind(如果你选择使用它)喜欢将所有东西放在近距离,例如将 DataTemplates 放在同一个文件,将 Xbind 属性放在部分 *.cs 文件中。老实说,我喜欢它,如果你熟悉 Angular,你可以把它看作一个 Angular 组件。
MVVM 视图优先:没有什么变化,但我们使用部分 *.cs 文件(代码隐藏)中的 *ViewModel 实例,而不是 DataContext,并将 xaml 字段绑定到 VM.Properties 等。
MVVM 视图优先:更具挑战性,但你可以这样做,如下面的示例所示。
View xaml 文件
<Grid Background="LightBlue">
<ContentControl Content="{x:Bind VM}" ContentTemplate="{StaticResource itemServieTemplate}"/>
</Grid>
View.cs 文件
// the xaml logic (mainPage.xaml.cs)
public sealed partial class MainPage : Page
{
//viewModel property
public ItemsServiceViewModel VM
{
get; set;
}
//ctor
public MainPage()
{
VM = new ItemsServiceViewModel();
this.InitializeComponent();
}
}
App.xaml - DataTemplates
<ResourceDictionary>
<!--data-template for ItemviewModel class as shown later-->
<DataTemplate x:Key="z" x:DataType="local:ItemViewModel">
<StackPanel>
<TextBlock Text="fds" />
<TextBlock Text="{x:Bind Title}"/>
<TextBlock Text="{x:Bind ItemCategory}"/>
</StackPanel>
</DataTemplate>
<!--data-template for ItemsServiceViewModel class as shown later-->
<DataTemplate x:Key="itemServieTemplate" x:DataType="local:ItemsServiceViewModel">
<Grid Background="LightGreen" BorderBrush="Black" BorderThickness="3">
<ListBox ItemsSource="{x:Bind Items,Mode=OneWay}" ItemTemplate="{StaticResource z}" />
<Button VerticalAlignment="Bottom" Content="Add Item Test" Click="{x:Bind AddItem}"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
ViewModels
//Represets Item-view-model for Item model class
public class ItemViewModel : ViewModelBase
{
//ctor
public ItemViewModel(Item _model)
{
Model = _model;
}
//model property
private Item model;
public Item Model
{
get { return model; }
set
{
model = value;
//refresh UI binded fields
Notify();
}
}
// title proprty from model
public string Title { get { return model.Title; } }
// category proprty from model
public Category ItemCategory { get { return model.ItemCategory; } }
//Represets ItemService-view-model for main view,
// this is VIP class at that demo for the view
public class ItemsServiceViewModel : ViewModelBase
{
//collection of Items
public ObservableCollection<ItemViewModel> Items
{
get; set;
}
//add item to collection
public void AddItem()
{
//add mock item
ItemViewModel newItem = new ItemViewModel(new Item("^^", Category.Food));
Items.AddItem(newItem.Model);
}
}
模型
//every item class has category
public enum Category
{
Food = 1,
Music = 2
}
//item model
public class Item
{
//ctor
public Item( string titile , Category cat)
{
Title = titile;
ItemCategory = cat;
}
//tite property
public string Title { get; set; }
//category property
public Category ItemCategory { get; set; }
}