上周打爐石打得太晚……忘記更新了,本周補上。本篇我們講一下{x:Bind}擴展標記。{x:Bind}擴展標記也是Windows 10 Uinversal 新增的內容,按官方的說法是 {Binding} 的備用選項。雖然 {x:Bind} 缺少 {Binding} 中的一些功能,但它運行時所花費的時間和使用的內存量均比 {Binding} 要少,且支持更好的調試。
首先我們來看一下{x:Bind}的基本用法:
<Page x:Class="XBindTest.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XBindTest" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock Text="{x:Bind HelloWorld}"></TextBlock> </Grid> </Page>
public sealed partial class MainPage : Page { public string HelloWorld { get; set; } public MainPage() { this.InitializeComponent(); this.HelloWorld = "Hello World"; } }
和原先使用的Binding最大的不同,就是不需要設置DataContext,而是從Page(或者UserControl)來尋找屬性進行綁定。值得注意的是,可能是為了強調性能,{x:Bind}的默認綁定Mode是OneTime,而不是OneWay。之前Binding寫得太多的各位可能會思維定勢而忘記修改Mode。
有的同學可能會說,既然以Page作為綁定的默認源,是不是意味着就木有ViewModel啥事了,以后代碼就一股腦都寫在Page.cs里了?當然不是啦,因為每個Page里寫個ViewModel是很常見的事情啊,再通過ViewModel來進一層綁定屬性就可以了。
public sealed partial class HostView : Page {
public HostViewModel ViewModel { get; set; }
public HostView() { this.InitializeComponent(); this.ViewModel = new HostViewModel(); } }
<Page x:Class="QuizGame.View.HostView" ... > <Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... /> </Page>
{x:Bind}還有一個非常神奇的地方,就是Xaml里寫了就可以直接在Design視圖顯示出來。而Binding則是沒有辦法做到的,這是因為{x:Bind} 在編譯時就已經生成了一些奇怪的代碼(位於對應的Page.g.cs文件,該文件可以在obj文件夾中找到)而 {Binding} 則是在運行時才獲取對象進行綁定。所以{x:Bind}才能具有更好的性能。
在g.cs文件中我們可以找到這樣的代碼:
private void Update_HelloWorld(global::System.String obj, int phase) { if((phase & ((1 << 0) | NOT_PHASED )) != 0) { XamlBindingSetters.Set_Windows_UI_Xaml_Controls_TextBlock_Text(this.obj2, obj, null); } } public static void Set_Windows_UI_Xaml_Controls_TextBlock_Text(global::Windows.UI.Xaml.Controls.TextBlock obj, global::System.String value, string targetNullValue) { if (value == null && targetNullValue != null) { value = targetNullValue; } obj.Text = value ?? global::System.String.Empty; }
在需要的時候,就可以打個斷點進行調試了,雖然我覺得然並卵……
接下來說的非常重要,就是在DataTemplate中如何使用{x:Bind},稍稍有別於在Page的Xaml里,在DataTemplate中使用{x:Bind}必須要表明綁定的數據類型:
<ListView ItemsSource="{x:Bind PersonList}"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:Person"> <TextBlock Text="{x:Bind Name}"></TextBlock> </DataTemplate> </ListView.ItemTemplate> </ListView>
還是那句話,{x:Bind}是在編譯時處理綁定的,必須明確知道數據類型才能在g.cs里生成一些奇怪的輔助信息。
看看下面這張截圖,因為類型都在g.cs里都生成好了,所以Design視圖里才能根據Xaml生成對應的展示數據,這是Binding所做不到的。

上面說了這么多{x:Bind}的優點,你是不是有些心動了呢?但是!終於到了說但是的時間了,{x:Bind}還是存在一些比不過Binding的地方:
- {x:Bind}不支持Source的用法,比如下面這個常用的功能就實現不了,乖乖滾回Binding的懷抱。Visibility="{Binding Settings.Accessibility,Source={StaticResource Locator}
- 另外呢,UpdateSourceTrigger也是不支持的。Text="{Binding Email,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
- 還有呢,如果你想把{x:Bind}寫到ResouceDirectory里,更是一件麻煩的事情,你需要給ResouceDirectory創建一個class,然后人肉添加類似InitializeComponent的方法……想想都覺得還是算了,哎呀我不想寫了……
- 如果你之前經常寫C# code來創建Binding,那么{x:Bind}又會讓你失望了,完全不支持……
除了以上這些,{x:Bind}還會誘使一種叫做代碼潔癖的疾病發生。你想啊,都把DataType寫到Xaml里了,這特么眼里還有沒有MVVM和王法了,類型都暴露了一點隱私都沒有了。底下寫Service的同事(請不要誤會底下的意思,這不是體位描寫)說:哎喲不好意思,我重構代碼把類型改了。你一看要改N個Xaml文件,抄起一把椅子就砸過去了,然后就被派出所帶走了……
所以這里還是需要通過接口來減少依賴,舉個例子在MainPage里通過IViewModel,而不是具體的MainViewModel來綁定,在DataTemplate里通過IPerson,而不是Person類型。這部分是面向對象的知識了,就不展開討論了。
本篇到此打住,再去開一盤爐石,我就不信薩滿打不上傳說……
