講講Windows10(UWP)下的Binding

前言

貌似最近來問我XAML這塊的東西的人挺多的。有時候看他們寫XAML這塊覺着也挺吃力的,所謂基礎不牢,地動山搖。XAML這塊雖然說和HTML同樣屬於標記語言,可是世界觀相對更加龐大一點。git

今天講講XAML中的Binding。沒啥技術含量,全當是快速閱讀。github

Binding做爲MVVM模式的一個相對核心的功能,一直是有爭議的。使用數據綁定能夠將咱們的View和Model解耦,可是若是一旦出現Bug,咱們將很難調試,還有一個問題就是數據綁定會帶來過大的內存開銷。
跟多數的技術同樣,都有本身的兩面性,具體運用場景如何抉擇,應該充分考慮。windows

做爲XAML的一部分,Binding的功能也隨着XAML版本的變動着。目前爲止,XAML一共有如下幾個版本:mvc

  • WPF Version
  • Silverlight 3 Version
  • Silverlight 4 Version
  • Windows 8 XAML/Jupiter(Windows Runtime XAML Framework) Version

其中WPF版本的Binding功能最強大,但也開銷最大。本文中,咱們主要講述最新版本,即__Windows 8 XAML/Jupiter__這個版本的XAML中的Binding。app

開始以前

要想講明白Binding這個東西,咱們先要從Binding類的繼承層次開始講。mvvm

public class Binding : BindingBase, IBinding, IBinding2
{
    public Binding();

    public IValueConverter Converter { get; set; }
    public System.String ConverterLanguage { get; set; }
    public System.Object ConverterParameter { get; set; }
    public System.String ElementName { get; set; }
    public BindingMode Mode { get; set; }
    public PropertyPath Path { get; set; }
    public RelativeSource RelativeSource { get; set; }
    public System.Object Source { get; set; }
    
    public System.Object FallbackValue { get; set; }   
    public System.Object TargetNullValue { get; set; }
    public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
}

能夠看到Binding繼承了BindingBase類,還繼承了IBinding,IBinding2的接口。咱們再分別看下這三個類和接口。首先看下咱們的BindingBase。ide

public class BindingBase : DependencyObject, IBindingBase
{
    public BindingBase();
}

BindingBase又繼承了DependencyObject和IBindingBase。DependencyObject這個咱們就很少講了,IBindingBase只是一個空接口。看來BindingBase沒有看到太多信息,咱們再來看下IBinding和IBinding2。函數

internal interface IBinding
{
    IValueConverter Converter { get; set; }
    System.String ConverterLanguage { get; set; }
    System.Object ConverterParameter { get; set; }
    System.String ElementName { get; set; }
    BindingMode Mode { get; set; }
    PropertyPath Path { get; set; }
    RelativeSource RelativeSource { get; set; }
    System.Object Source { get; set; }
}
internal interface IBinding2
{
    System.Object FallbackValue { get; set; }
    System.Object TargetNullValue { get; set; }
    UpdateSourceTrigger UpdateSourceTrigger { get; set; }
}

微軟設計了一個Binding的基礎模型,蘊含了接口分離原則(ISP)的思想,又提供了一個IBindingBase的空接口,若是你想實現本身的Binding模型,能夠繼承這個接口,這樣能夠和.NET類庫風格統一。ui

講講IBinding

既然咱們已經瞭解了Binding的大概的層次結構,那咱們開始一個個講講這些都是怎麼用的。this

IBinding中的Path

Path是咱們相對用的比較多的,多數狀況下,咱們能夠這樣寫

Text="{Binding}"

XAML會取綁定源的ToString的值,因此咱們能夠重寫Override方法來實現咱們的須要的綁定。
咱們也能夠綁定具體的屬性,好比:

Text="{Binding Name}"

若是咱們綁定了一個集合,那咱們也能夠嘗試這樣寫:

Text="{Binding MyList[1].Name}"

除了上述比較經常使用的,咱們還有一個叫作ICustomPropertyProvider的接口,當你的類實現了這個接口中的

string GetStringRepresentation()

XAML就會去取這個函數返回的值。

因此總結下咱們的Path的綁定方式:

默認狀況:
target Text="{Binding}"
source ToString()
//你能夠重寫ToString方法來改變值

屬性綁定:
target Text="{Binding Name}"
source public String Name { get;}

索引器綁定:
target Text="{Binding MyList[1].Name}"

實現ICustomPropertyProvider的綁定:
target Text="{Binding}"
source String GetStringRepresentation() 
//實現方法獲取值

IBinding中的Mode

Mode一共有三種,OneTime,OneWay,TwoWay。看字面的意思就很容易理解。

//OneTime
Text="{Binding, Mode=OneTime}"
//OneWay
Text="{Binding, Mode=OneWay}"
//TwoWay
Text="{Binding, Mode=TwoWay}"

在OneWay和TwoWay中,若是想要對象的值變動時讓綁定目標也變化,須要注意一下兩點

  • 對於普通的屬性,須要類實現INotifyPropertyChanged,而且對象值變化時手動通知變動。
  • 對於依賴屬性,當觸發SetValue方法後,PropertyChangedCallBack會通知變動,因此無需咱們手動操做。
    在UWP系統中,Mode的默認值爲__OneWay__。

IBinding中的RelativeSource

RelativeSource是一種相對關係找數據源的綁定。目前有兩種:Self和TemplatedParent

//Self
<TextBlock Text="{Binding Foreground.Color.R, RelativeSource={RelativeSource Mode=Self}}" Foreground="Red"/>

//TemplatedParent
<DataTemplate> 
    <Rectangle Fill="Red" Height="30" Width="{Binding Width, RelativeSource={RelativeSource Mode=TemplatedParent}}">
</DataTemplate>

RelativeSource綁定的方式咱們經常使用於控件模板。默認值通常爲null。

IBinding中的ElementName

ElementName也是咱們最經常使用的一種綁定方式,使用這個咱們須要注意兩點:

  • 指定的ElementName必須在當前XAML名稱範圍裏。
  • 若是綁定目標位於數據模板或控件模板中,則爲模板化父級的XAML名稱範圍。
    舉個例子:
<UserControl x:Name="Instance" Background="Red"> 
  <Grid Background="{Binding Background, ElementName=Instance}"/> 
</UserControl> 
//or
<Page x:Name="Instance"> 
  <Grid> 
    <ItemsControl ItemsSource="{Binding Users}"> 
      <ItemsControl.ItemTemplate> 
        <DataTemplate> 
          <Button Command=" {Binding DataContext.TestCommand, ElementName=Instance}"/> 
        </DataTemplate> 
      </ItemsControl.ItemTemplate> 
    </ItemsControl> 
   </Grid> 
 </Page>

IBinding中的Source

Source也是咱們經常使用的一種方式。

<Page> 
  <Page.Resources> 
    <SolidColorBrush x:Key="MainBrush" Color="Orange"/> 
  </Page.Resources> 
  <Grid Background="{Binding Source={StaticResource MainBrush}}"/> 
</Page>
//固然咱們也能夠簡寫爲
<Grid Background="{StaticResource MainBrush}"/>

通常狀況下,Source,ElementName和RelativeSource三者是互斥的,指定多餘一種的綁定方式會引起異常。

IBinding中的Converter

咱們很難保證咱們的對象值和咱們綁定目標的類型一直,因此轉換器能夠將類型就行轉換。
使用轉換器咱們要實現IValueConverter接口:

public class BoolVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        bool? result = value as Nullable<bool>;
        if(result == true)
        {
            return Visibility.Visible;
        }
        return Visibility.Collapsed;

    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

若是你的值須要轉換回去,你也能夠繼續實現ConvertBack方法。

<Page> 
  <Page.Resources> 
    <local:BoolVisibilityConverter x:Key="BoolVisibilityConverter"/> 
  </Page.Resources> 
  <Grid> 
    <Border Visibility="{Binding Busy, Converter={StaticResource BoolVisibilityConverter}}"/> 
  </Grid> 
</Page>

IBinding中的ConverterParameter和ConverterLanguage

這兩個參數不能綁定,只能指定常量值。

<Border Visibility="{Binding Busy, Converter={StaticResource BoolVisibilityConverter}, 
  ConverterParameter=One, ConverterLanguage=en-US}"/>

IBinding中的參數基本上覆蓋了咱們多數的需求。儘管相對於WPF缺乏了多值綁定等等,但咱們也可以經過自定義一些附加屬性來實現這些功能。

IBinding2

IBinding2中的參數就相對使用的比較少了。

IBinding2中的FallbackValue

FallbackValue的用途是:當綁定對象不存在時,咱們就使用FallbackValue的值:

<Page> 
  <Page.Resources> 
    <x:String x:Key="ErrorString">Not Found</x:String> 
  </Page.Resources> 
  <Grid> 
    <TextBlock Text="{Binding Busy, FallbackValue={StaticResource ErrorString}}"/> 
  </Grid> 
</Page>

IBinding2中的TargetNullValue

TargetNullValue的用途是:當綁定對象爲空時,咱們就使用TargetNullValue的值:

<Page> 
  <Page.Resources> 
    <x:String x:Key="ErrorString">Not Found</x:String> 
  </Page.Resources> 
  <Grid> 
    <TextBlock Text="{Binding Busy, TargetNullValue={StaticResource ErrorString}}"/> 
  </Grid> 
</Page>

IBinding2中的UpdateSourceTrigger

UpdateSourceTrigger的值有三種:Default,PropertyChanged,Explicit。
多數狀況下大多數依賴項屬性的默認值都爲 PropertyChanged。可是Text屬性不是。
PropertyChanged的意思是當綁定目標屬性更改時,當即更新綁定源。而Explicit是隻有UpdateSource方法時才更新綁定源。
舉個例子:

<Grid> 
    <TextBox x:Name="TitleTextBox" Text="{Binding Title, ElementName=Instance, UpdateSourceTrigger=Explicit, Mode=TwoWay}" /> 
    <Button Click="Button_Click"/> 
</Grid>


private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    var current = this.Title; 
    TitleTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource(); 
    current = this.Title; 
}

有關uwp的Binding就說到這裏。謝謝~

參考資料

被誤解的MVC和被神化的MVVM
Extensible Application Markup Language
RelativeSource 標記擴展
UpdateSourceTrigger enumeration

我的推薦

個人博客園
個人簡書
個人Github

相關文章
相關標籤/搜索