講講Windows10(UWP)下的Binding


前言

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

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

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

作為XAML的一部分,Binding的功能也隨着XAML版本的變更着。目前為止,XAML一共有以下幾個版本:

  • 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。

開始之前

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

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。

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類庫風格統一。

講講IBinding

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM