數據系結基本概念
.NET 多平臺應用程式 UI (.NET MAUI) 數據系結允許連結兩個對象的屬性,讓其中一個對象的變更造成另一個對象的變更。 這是一個非常有價值的工具,雖然數據系結可以完全在程式碼中定義,但 XAML 提供快捷方式和便利性。
數據系結
數據系結會連接兩個物件的屬性,稱為 來源 和 目標。 在程序代碼中,需要兩個步驟:
- 目標
BindingContext
物件的 屬性必須設定為來源物件, SetBinding
方法(通常與Binding
類別搭配使用)必須在目標物件上呼叫,才能將該對象的 屬性系結至來源物件的屬性。
目標屬性必須是可繫結的屬性,這表示目標對象必須衍生自 BindableObject。 的屬性 Label,例如 Text
,與可系結屬性 TextProperty
相關聯。
在 XAML 中,您也必須執行程式碼中所需的相同兩個步驟,但 Binding
標記延伸會取代 SetBinding
呼叫和 Binding
類別。 不過,當您在 XAML 中定義資料系結時,有多種方式可以設定 BindingContext
目標物件的 。 有時候會從程式代碼後置檔案進行設定,有時使用 StaticResource
或 x:Static
標記延伸,有時則是屬性元素標記的內容 BindingContext
。
檢視對檢視系結
您可以定義數據系結,以連結相同頁面上兩個檢視的屬性。 在這裡情況下,您會使用x:Reference
標記延伸來設定BindingContext
目標物件的 。
下列範例包含 和 Slider 兩 Label 個檢視,其中一個是由 Slider 值旋轉,另一個則顯示 Slider 值:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderBindingsPage"
Title="Slider Bindings Page">
<StackLayout>
<Label Text="ROTATION"
BindingContext="{x:Reference slider}"
Rotation="{Binding Path=Value}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Slider x:Name="slider"
Maximum="360"
VerticalOptions="Center" />
<Label BindingContext="{x:Reference slider}"
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
</StackLayout>
</ContentPage>
Slider包含使用標記延伸之兩Label個x:Name
檢視x:Reference
所參考的屬性。 系 x:Reference
結延伸會定義名為 Name
的屬性,以設定為參考項目的名稱,在此案例 slider
中為 。 不過, ReferenceExtension
定義標記延伸的 x:Reference
類別也會定義 ContentProperty
的屬性 Name
,這表示它並非明確必要。
Binding
標記延伸本身可以有數個屬性,就像和 Binding
類別一樣BindingBase
。 ContentProperty
Binding
的 是 Path
,但如果路徑是標記延伸中的第一個專案,則可以省略標記延伸的 Binding
“Path=” 部分。
第二個 Binding
標記延伸會設定 StringFormat
屬性。 在 .NET MAUI 中,系結不會執行任何隱含類型轉換,而且如果您需要將非字串對象顯示為字串,則必須提供類型轉換器或使用 StringFormat
。
重要
格式化字串必須放在單引號中。
繫結模式
單一檢視可以在其數個屬性上具有數據系結。 不過,每個檢視只能有一個 BindingContext
,因此該檢視上的多個數據系結必須擁有相同物件的所有參考屬性。
這個和其他問題的解決方案牽 Mode
涉到 屬性,該屬性會設定為 列舉的成員 BindingMode
:
Default
OneWay
- 值會從來源傳送至目標OneWayToSource
- 值會從目標傳送到來源TwoWay
— 值會在來源和目標之間雙向傳輸OneTime
— 資料會從來源移至目標,但只有在變更時BindingContext
下列範例示範 和 TwoWay
系結模式的OneWayToSource
一個常見用法:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderTransformsPage"
Padding="5"
Title="Slider Transforms Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Scaled and rotated Label -->
<Label x:Name="label"
Text="TEXT"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<!-- Slider and identifying Label for Scale -->
<Slider x:Name="scaleSlider"
BindingContext="{x:Reference label}"
Grid.Row="1" Grid.Column="0"
Maximum="10"
Value="{Binding Scale, Mode=TwoWay}" />
<Label BindingContext="{x:Reference scaleSlider}"
Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
Grid.Row="1" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for Rotation -->
<Slider x:Name="rotationSlider"
BindingContext="{x:Reference label}"
Grid.Row="2" Grid.Column="0"
Maximum="360"
Value="{Binding Rotation, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationSlider}"
Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
Grid.Row="2" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationX -->
<Slider x:Name="rotationXSlider"
BindingContext="{x:Reference label}"
Grid.Row="3" Grid.Column="0"
Maximum="360"
Value="{Binding RotationX, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationXSlider}"
Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
Grid.Row="3" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationY -->
<Slider x:Name="rotationYSlider"
BindingContext="{x:Reference label}"
Grid.Row="4" Grid.Column="0"
Maximum="360"
Value="{Binding RotationY, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationYSlider}"
Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
Grid.Row="4" Grid.Column="1"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
在此範例中,四Slider個Label檢視是用來控制 Scale
的、 RotateX
Rotate
、 和 RotateY
屬性。 起初,這似乎這四個 Label 屬性應該是數據系結目標,因為每個屬性都是由 所 Slider設定。 不過, BindingContext
的 Label 只能是一個 物件,而且有四個不同的滑桿。 因此, BindingContext
四個滑桿中每個滑桿的 都設定為 Label,而且系結會設定在滑桿的屬性上 Value
。 藉由使用 OneWayToSource
和 TwoWay
模式,這些 Value
屬性可以設定來源屬性,也就是 Scale
的、 Rotate
、 RotateX
和 RotateY
屬性 Label。
三個 Slider 檢視上的系結是 OneWayToSource
,這表示 Slider 值會導致其 BindingContext
的屬性變更,也就是 Label 具名 label
的 。 這三Slider個檢視會導致的、 RotateX
和 RotateY
屬性Label變更Rotate
:
不過,屬性的 Scale
系結是 TwoWay
。 這是因為 Scale
屬性的預設值為1,而使用 TwoWay
系結會導致 Slider 初始值設定為1而不是0。 如果該系結為 OneWayToSource
,則 Scale
屬性一開始會從 Slider 預設值設定為0。 Label不會顯示 。
注意
類別 VisualElement 也有 ScaleX
和 ScaleY
屬性,分別在 x 軸和 Y 軸上縮放 VisualElement 。
系結和集合
ListView 會 ItemsSource
定義 類型的 IEnumerable
屬性,並顯示該集合中的專案。 這些專案可以是任何類型的物件。 根據預設, ListView 會使用 ToString
每個專案的 方法來顯示該專案。 有時候這隻是您想要的,但在許多情況下, ToString
只會傳回物件的完整類別名稱。
不過,您可以透過使用範本,以任何方式顯示集合中的ListView專案,其中包含衍生自 Cell的類別。 範本會針對 中 ListView設定的每個專案複製範本,而且範本上設定的數據系結會傳送至個別複製品。 您可以使用 類別為專案 ViewCell 建立自訂儲存格。
ListView 可以透過 類別的協助 NamedColor
,顯示 .NET MAUI 中可用之每個具名色彩的清單:
using System.Reflection;
using System.Text;
namespace XamlSamples
{
public class NamedColor
{
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public Color Color { get; private set; }
// Expose the Color fields as properties
public float Red => Color.Red;
public float Green => Color.Green;
public float Blue => Color.Blue;
public static IEnumerable<NamedColor> All { get; private set; }
static NamedColor()
{
List<NamedColor> all = new List<NamedColor>();
StringBuilder stringBuilder = new StringBuilder();
// Loop through the public static fields of the Color structure.
foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())
{
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof(Color))
{
// Convert the name to a friendly name.
string name = fieldInfo.Name;
stringBuilder.Clear();
int index = 0;
foreach (char ch in name)
{
if (index != 0 && Char.IsUpper(ch))
{
stringBuilder.Append(' ');
}
stringBuilder.Append(ch);
index++;
}
// Instantiate a NamedColor object.
NamedColor namedColor = new NamedColor
{
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = (Color)fieldInfo.GetValue(null)
};
// Add it to the collection.
all.Add(namedColor);
}
}
all.TrimExcess();
All = all;
}
}
}
每個NamedColor
物件都有 Name
類型的 和 FriendlyName
屬性、Color
類型string
Color為、 和Red
Green
、 和 Blue
屬性的屬性。 此外,靜態建NamedColor
構函式會建立集合IEnumerable<NamedColor>
,其中包含NamedColor
對應至 類別中Colors型Color別字段的物件,並將它指派給其公用靜態All
屬性。
您可以使用x:Static
標記延伸,將靜態NamedColor.All
屬性設定為 ItemsSource
的 ListView :
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ListView ItemsSource="{x:Static local:NamedColor.All}" />
</ContentPage>
結果會建立項目的類型 XamlSamples.NamedColor
為 :
若要定義項目的樣本, ItemTemplate
應該將 設定為 DataTemplate 參考 的 ViewCell。 ViewCell應該定義一或多個檢視的配置,以顯示每個專案:
<ListView ItemsSource="{x:Static local:NamedColor.All}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding FriendlyName}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
注意
單元格和儲存格子系的系結來源是 ListView.ItemsSource
集合。
在此範例中,元素 Label 會設定為 View 的 ViewCell屬性。 ViewCell.View
不需要標記,View因為 屬性是 的內容ViewCell屬性。 此 XAML 會顯示 FriendlyName
每個 NamedColor
物件的 屬性:
專案範本可以展開以顯示更多資訊和實際色彩:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ContentPage.Resources>
<x:Double x:Key="boxSize">50</x:Double>
<x:Int32 x:Key="rowHeight">60</x:Int32>
<local:FloatToIntConverter x:Key="intConverter" />
</ContentPage.Resources>
<ListView ItemsSource="{x:Static local:NamedColor.All}"
RowHeight="{StaticResource rowHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5, 5, 0, 5"
Orientation="Horizontal"
Spacing="15">
<BoxView WidthRequest="{StaticResource boxSize}"
HeightRequest="{StaticResource boxSize}"
Color="{Binding Color}" />
<StackLayout Padding="5, 0, 0, 0"
VerticalOptions="Center">
<Label Text="{Binding FriendlyName}"
FontAttributes="Bold"
FontSize="14" />
<StackLayout Orientation="Horizontal"
Spacing="0">
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
<Label Text="{Binding Green,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G={0:X2}'}" />
<Label Text="{Binding Blue,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B={0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
系結值轉換器
上一個 XAML 範例會顯示每個 NamedColor
的個別Red
、 Green
和 Blue
屬性。 這些屬性的類型 float
和範圍從 0 到 1。 如果您想要顯示十六進位值,則不能只搭配 「X2」 格式規格使用 StringFormat
。 這隻適用於整數和此外,值 float
必須乘以 255。
這個問題可以使用值轉換器來解決,也稱為系結轉換器。 這是實作 介面的 IValueConverter 類別,這表示它有兩個名為 Convert
和 ConvertBack
的方法。 當 Convert
值從來源傳輸至目標時,會呼叫 方法。 方法ConvertBack
會呼叫 ,以便從目標傳輸至 或 TwoWay
系結中的OneWayToSource
來源:
using System.Globalization;
namespace XamlSamples
{
public class FloatToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
float multiplier;
if (!float.TryParse(parameter as string, out multiplier))
multiplier = 1;
return (int)Math.Round(multiplier * (float)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
float divider;
if (!float.TryParse(parameter as string, out divider))
divider = 1;
return ((float)(int)value) / divider;
}
}
}
注意
方法 ConvertBack
在此範例中不會扮演角色,因為系結只是從來源到目標的一種方式。
系結會參考具有 屬性的 Converter
系結轉換子。 係結轉換器也可以接受以 ConverterParameter
屬性指定的參數。 對於某些多功能性,這就是指定乘數的方式。 系結轉換器會檢查轉換器參數是否有有效的 float
值。
轉換器會在頁面的資源字典中具現化,以便在多個系結之間共用:
<local:FloatToIntConverter x:Key="intConverter" />
三個數據系結會參考這個單一實例:
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
專案範本會播放色彩、其易記名稱,以及其 RGB 值:
ListView可以處理動態發生在基礎數據中的變更,但只有在您採取特定步驟時。 如果指派給 ItemsSource
運行時間期間變更屬性 ListView 的專案集合,請使用 ObservableCollection<T> 類別做為這些專案。 ObservableCollection<T> 會實作 INotifyCollectionChanged
介面,並 ListView 會安裝 事件的處理程式 CollectionChanged
。
如果專案的屬性在運行時間本身變更,則集合中的專案應該實 INotifyPropertyChanged
作 介面,並使用 PropertyChanged
事件向屬性值發出變更訊號。
下一步
數據系結提供強大的機制,可在頁面內的兩個對象之間,或可視化對象與基礎數據之間連結屬性。 但是當應用程式開始使用數據源時,熱門的應用程式架構模式會開始以有用的範例形式出現。