65.9K
CodeProject 正在变化。 阅读更多。
Home

UWP 绑定 vs. XBind

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (4投票s)

2017 年 12 月 12 日

CPOL

8分钟阅读

viewsIcon

28955

Xaml 绑定 vs. XBind

引言

WPF 中最令人兴奋的可能就是 Binding(绑定)的概念了,它现在在许多其他客户端框架(如 Angular??)中已成为一种标准。

不过,任何美好的事物都有其代价,例如性能下降和调试困难。

在 UWP 中,微软发布了 X:Bind——一种与旧 Binding 并行的全新 Binding 概念,旨在提高性能、改善调试并简化操作。

然而,从 Binding 到 x:Bind,发生了许多变化。

在这篇文章中,我将从语法、概念、性能、调试、功能等方面对这两种强大的技术进行对比,并提供我所能提供的一切。

 

幕后

对我来说,首先冒出来的问题是:“x:Bind 到底是什么?”
我的意思是,Binding 是一个 .NET 类,你可以重写它、继承它等等。但(根据 MSDN)

 {x:Bind} 仅仅是一个标记扩展,无法以编程方式创建或操作此类绑定。有关标记扩展的更多信息…

换句话说,xBind 是新一代的标记扩展,它不是一个可用的类。

此外,请注意,与 WPF 相比,标记扩展发生了变化,当时它们实际上是一个类,Binding 和 StaticResources 等都继承自 MarkupExtension 类型。

因此,Binding 本身实际上是 Windows.UI.Xaml.Data 命名空间中的一种新类型。这在某种程度上保留了 WPF 绑定的概念。

xBind & Binding 默认行为

xBind 可以绑定到私有字段!而不仅仅是 Binding 所能绑定的公共属性。xBind 的默认模式是“一次性”(OneTime)(您可能希望在 xaml 代码中覆盖它)。而 Binding 是双向的(TwoWay)。此外,新的“BindBack”功能允许 UI 字段同时绑定到模型中的两个属性。

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 的内部实现。
 
<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 的调试 & 验证

验证 - xBing 使用预编译的内部验证,因此可以避免一些运行时故障,也可能导致更好、更高效的代码。例如,在端点之一缺少转换器的情况下使用双向绑定将导致编译时错误,并可避免运行时错误。

调试,

对于使用标记语言的前端开发人员来说,调试 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; }
}

 

© . All rights reserved.