Windows 10 UWP 2 of N : New data binding

  • 1615
  • 0
  • UAP
  • 2021-04-30

Windows 10 UWP 2 of N : New data binding

在XAML上面呈現資料有兩種模式:

  1. Code Behind
  2. Data binding

兩種方式都可以達到將資料呈現在UI上的方式!但有個問題就是Code Behind並不會自動的更新資料的變更,於是Data Binding的概念就出現拉,可以選擇更新模式來達到資料同步的需求(要求)

但是再從Mobile的開發上面來看DataBinding很好,但效能就會受到影響!太多的DataBinding就是導致效能變差,而在Build 2015上介紹了新的Data Binding稱之為Compiled binding

而Compiled binding有以下特性

  • Data binding是在runtime的時候產生Code而Compiled就是在Compiled時期完成
  • 消除在執行時期的映射(reflection)的流程
  • 程式碼可以被預期的和具有除錯性
Data binding在XAML上的寫法 Compiled binding在XAML上的寫法
Text = “{Binding Title}” Text = “{x : Bind Title}”

以效能來看就是如下

NO binding > x : Binding > Binding

不論是在CPU使用或是Memory都是用以上的順序!

所以直接來說就是換成使用{ x : Bind }

Complied data binding還有兩的特點

  1. x : Bind 是強型別!
  2. Mode = OneTime 是預設值!

Binding的Mode還是一樣有三個Value,分別是

分類 說明
One time 初始化binding成功後的值,之後Source的變更並不會更新到該屬性
One way 當Source端資料更新的時候,會同步在Binding的Target上
Two way 不論是Source或是Target都可以變更來同步資料

One-way listen to change notification可以使用如下的方式

  • INotifyPropertyChanged
  • DependencyProperty
  • INotifyCollectionChanged / IObservableVector

Two-way

  • UI property需要DependencyProperty
  • TextBox會是個特殊的case(若是要同步TextBox的Text屬性),要等到LostFocus的時候才會觸發!

接者介紹再ListViewBase中客製化DataTemplate並結合Compiled binding的寫法


    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:triggers="using:App1.Triggers"
    xmlns:vm="using:App1.ViewModel"
    xmlns:model="using:App1.Model"
    mc:Ignorable="d">
    
    <Page.Resources>
        <vm:MainPageViewModel x:Key="MainVM"/>
    </Page.Resources>

    <Grid DataContext="{StaticResource MainVM}" Background="Black" HorizontalAlignment="Center" VerticalAlignment="Center">
        <ListView  ItemsSource="{Binding EmployeeCollections}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="model:EmployeeModel">
                    <RelativePanel>
                        <Image x:Name="UserImage" Width="80" Height="80" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True">
                            <Image.Source>
                                <BitmapImage DecodePixelHeight="80" DecodePixelWidth="80" UriSource="ms-appx:///Assets/Logo.png"/>
                            </Image.Source>
                        </Image>
                        <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignTopWithPanel="True" Text="{x:Bind FirstName}" Foreground="Blue"/>
                        <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignVerticalCenterWithPanel="True" Text="{x:Bind LastName}" Foreground="Blue"/>
                        <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignBottomWithPanel="True" Text="{x:Bind Age}" Foreground="Blue"/>
                    </RelativePanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

需要注意的事情是!現在使用DataTemplate和Compiled binding需要加上一個Attribute x : DataType (上圖Code的20行) 強型別該DataTemplate!

DataTemplate可以建立成被重複使用的方式跟8.1的時期一樣!但如果要使用Compile binding就得調整如下

  1. 將DataTemplate建立x : key,加上 x : DataType。
  2. 放在Resource的Code block內。

但如果想把該DataTemplate寫在ResourceDictionary內的話就得調整如下步驟!

  1. 建立ResourceDictionary檔案 (比如叫做Dictionary1.xaml)
  2. 建立該檔案之Class檔(該Class就會命名成Dictionary1.xaml.cs)
  3. 在Class檔案放入如下的Code

將該ResourceDictionary加上

 


 

 

 


    {
        public Dictionary1()
        {
            InitializeComponent();
        }
    }

接者在Resource將該Dictionary給Merge到現在的Dictionary

.Resources>
        <ResourceDictionary>
            <vm:MainPageViewModel x:Key="MainVM"/>
            <ResourceDictionary.MergedDictionaries>
                <local:Dictionary1/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
        
        <!--<vm:MainPageViewModel x:Key="MainVM"/>-->
        <!--<DataTemplate x:DataType="model:EmployeeModel" x:Key="SampleDataTemplate">
            <RelativePanel>
                <Image x:Name="UserImage" Width="80" Height="80" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True">
                    <Image.Source>
                        <BitmapImage DecodePixelHeight="80" DecodePixelWidth="80" UriSource="ms-appx:///Assets/Logo.png"/>
                    </Image.Source>
                </Image>
                <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignTopWithPanel="True" Text="{x:Bind FirstName, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
                <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignVerticalCenterWithPanel="True" Text="{x:Bind LastName, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
                <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignBottomWithPanel="True" Text="{x:Bind Age, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
            </RelativePanel>
        </DataTemplate>-->
</Page.Resources>

第五行就是將Dictionary的File給Merge到APP的Resource裡面~


接者介紹新的方式階段性載入Binding的資料

使用的語法是 x : Phase = “放入整數,大於0”


            <Image x:Name="UserImage" Width="80" Height="80" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True">
                <Image.Source>
                    <BitmapImage DecodePixelHeight="80" DecodePixelWidth="80" UriSource="ms-appx:///Assets/Logo.png"/>
                </Image.Source>
            </Image>
            <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignTopWithPanel="True" Text="{x:Bind FirstName, FallbackValue=ErrorFirstName}" x:Phase="1" Foreground="Blue"/>
            <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignVerticalCenterWithPanel="True" Text="{x:Bind LastName, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
            <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignBottomWithPanel="True" Text="{x:Bind Age, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
</RelativePanel>

這樣就可以在大量的資料載入時優先顯示FirstName的TextBlock

補述幾點

  • CCC事件(ContainerContentChanging)依然可以使用!而且會在所有的phase之前
  • Phase的數字可以非連貫性
  • 當需要載入大量資料在較為低階的裝置時使用可以提升UX

接者是可以將Event給Binding起來!

如下Code所示


<Button Background="White" Content="ShowDialog" Click="{x:Bind Button_Click}"/>

或者可以把Event寫在Model裡面啦!


using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.UI.Xaml;

namespace App1.Model
{
    public class EmployeeModel : BaseModel
    {
        private string _FirstName;

        public string FirstName
        {
            get { return _FirstName; }
            set { _FirstName = value; }
        }

        private string _LastName;

        public string LastName
        {
            get { return _LastName; }
            set { _LastName = value; }
        }

        private int _Age;

        public int Age
        {
            get { return _Age; }
            set { _Age = value; }
        }

        public async void Button_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new MessageDialog(String.Format("{0} {1} has been poke", this.FirstName, this.LastName), "Dialog");
            var UICmd = await dialog.ShowAsync();
        }
    }
}

然後稍微修改一下DataTemplate如下


            <RelativePanel>
                <Image x:Name="UserImage" Width="80" Height="80" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True">
                    <Image.Source>
                        <BitmapImage DecodePixelHeight="80" DecodePixelWidth="80" UriSource="ms-appx:///Assets/Logo.png"/>
                    </Image.Source>
                </Image>
                <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignTopWithPanel="True" Text="{x:Bind FirstName, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
                <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignVerticalCenterWithPanel="True" Text="{x:Bind LastName, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
                <TextBlock RelativePanel.RightOf="UserImage" RelativePanel.AlignBottomWithPanel="True" Text="{x:Bind Age, FallbackValue=ErrorFirstName}" Foreground="Blue"/>
                <Button Background="White" Content="ShowDialog" RelativePanel.RightOf="UserImage" RelativePanel.AlignVerticalCenterWithPanel="True" RelativePanel.AlignRightWithPanel="True" Click="{x:Bind Button_Click}"/>
            </RelativePanel>
</DataTemplate>

Event binding幾點說明

  • 可以支援沒有參數的Event如Void DoSomething() {…}
  • 和原本Event相同的參數Void DoSomething(object sender, RoutedEventArgs e) {…}
  • 和原本Event相同的參數的基礎類型Void DoSomething(object sender, object e) {…}
  • 多載並不支援

這樣的寫法並不支援所有的事件!!

有個觀念要記住!在Model的Event binding在不同的Item是不同的Event!


Binding 生命週期以及效能

Binding初始化完成在Loading的事件

  • 使用Bindings.Initialize()將會初始化Bindings
  • 使用Bindings.Update()在非同步的資料更新
  • 使用Bindings.StopTracking()會停止追蹤Property的變更,呼叫Update()會重新Tracking

OneTime 效能較OneWay好

  • Bindings.Update()可以在OneTime上同步資料
  • 在列表上 考慮使用INCC(INotifyCollectionChanged)來更新資料取代INPC(INotifyPropertyChanged)在屬性上

想使用INCC其實只需要用ObservableCollection就有實作INCC的部分了!

 

2015/07/23 補述:Compile Binding不可以搭配Converter!因為Compile的Binding已經將資料型別確切寫死在(頁面名稱).g.cs檔案的Update_Data的這段~

2015/08/11 補述:Compile Binding可以搭配Converter!但是須將Converter寫在APP.Resource裡面或者ResourceDictionary並把該reosourcedictionary merge到application.resource下!目前不能再UIControle的Resource下了定義Converter。感謝Bobson告知

 

***以上Code以及說明都有可能隨者Windows 10 的版本有所調整!***

 

參考資料 Build 2015 3-635 Data Binding: Boost Your App`s Performance Through New Enhancements to XAML Data Binding

 

下次再分享Windows 10 的新技術拉~