Путь привязки Xamarin.Forms
Во всех предыдущих примерах привязки данных в свойстве Path
класса Binding
(или свойстве Path
расширения разметки Binding
) указывалось одно свойство. В действительности Path
может указывать на вложенное свойство (свойство свойства) или элемент коллекции.
Например, предположим, что страница содержит объект TimePicker
:
<TimePicker x:Name="timePicker">
Свойство Time
класса TimePicker
имеет тип TimeSpan
, но, возможно, вам нужно создать привязку данных, которая ссылается на свойство TotalSeconds
этого значения TimeSpan
. Вот эта привязка данных:
{Binding Source={x:Reference timePicker},
Path=Time.TotalSeconds}
Свойство Time
имеет тип TimeSpan
, который имеет свойство TotalSeconds
. Свойства Time
и TotalSeconds
соединяются точкой. Элементы в строке Path
всегда ссылаются на свойства, а не на их типы.
Этот и еще ряд примеров представлены на странице Path Variations (Варианты пути):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:globe="clr-namespace:System.Globalization;assembly=netstandard"
x:Class="DataBindingDemos.PathVariationsPage"
Title="Path Variations"
x:Name="page">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="FontSize" Value="Large" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="10, 0">
<TimePicker x:Name="timePicker" />
<Label Text="{Binding Source={x:Reference timePicker},
Path=Time.TotalSeconds,
StringFormat='{0} total seconds'}" />
<Label Text="{Binding Source={x:Reference page},
Path=Content.Children.Count,
StringFormat='There are {0} children in this StackLayout'}" />
<Label Text="{Binding Source={x:Static globe:CultureInfo.CurrentCulture},
Path=DateTimeFormat.DayNames[3],
StringFormat='The middle day of the week is {0}'}" />
<Label>
<Label.Text>
<Binding Path="DateTimeFormat.DayNames[3]"
StringFormat="The middle day of the week in France is {0}">
<Binding.Source>
<globe:CultureInfo>
<x:Arguments>
<x:String>fr-FR</x:String>
</x:Arguments>
</globe:CultureInfo>
</Binding.Source>
</Binding>
</Label.Text>
</Label>
<Label Text="{Binding Source={x:Reference page},
Path=Content.Children[1].Text.Length,
StringFormat='The second Label has {0} characters'}" />
</StackLayout>
</ContentPage>
Во втором элементе Label
источником привязки является сама страница. Свойство Content
имеет тип StackLayout
, который имеет свойство Children
типа IList<View>
. Этот тип, в свою очередь, имеет свойство Count
, в котором указывается число дочерних объектов.
Пути с индексаторами
Привязка в третьем элементе Label
на странице Path Variations ссылается на класс CultureInfo
в пространстве имен System.Globalization
:
<Label Text="{Binding Source={x:Static globe:CultureInfo.CurrentCulture},
Path=DateTimeFormat.DayNames[3],
StringFormat='The middle day of the week is {0}'}" />
В качестве источника задано статическое свойство CultureInfo.CurrentCulture
, представляющее собой объект типа CultureInfo
. В этом классе определено свойство DateTimeFormat
типа DateTimeFormatInfo
, содержащее коллекцию DayNames
. Индекс выбирает четвертый элемент.
Четвертый элемент Label
выполняет схожие действия, но для французского языка и региональных параметров. Свойству Source
привязки присваивается объект CultureInfo
с конструктором:
<Label>
<Label.Text>
<Binding Path="DateTimeFormat.DayNames[3]"
StringFormat="The middle day of the week in France is {0}">
<Binding.Source>
<globe:CultureInfo>
<x:Arguments>
<x:String>fr-FR</x:String>
</x:Arguments>
</globe:CultureInfo>
</Binding.Source>
</Binding>
</Label.Text>
</Label>
Дополнительные сведения об указании аргументов конструктора в XAML см. в разделе Передача аргументов конструктора.
Наконец, последний пример аналогичен второму, за тем исключением, что он ссылается на один из дочерних элементов StackLayout
:
<Label Text="{Binding Source={x:Reference page},
Path=Content.Children[1].Text.Length,
StringFormat='The first Label has {0} characters'}" />
Этот дочерний объект представляет собой элемент Label
, который имеет свойство Text
типа String
, имеющего свойство Length
. Первый элемент Label
сообщает значение TimeSpan
, заданное в TimePicker
, поэтому при изменении текста итоговый элемент Label
также меняется.
Вот работающая программа:
Отладка сложных путей
Составлять определения сложных путей может быть непросто: нужно знать тип каждого вложенного свойства или элементов в коллекции, чтобы правильно добавить следующее вложенное свойство. Однако сами типы в пути отсутствуют. Один из приемов — формировать путь поэтапно, проверяя промежуточные результаты. В последнем примере можно было начать без определения Path
:
<Label Text="{Binding Source={x:Reference page},
StringFormat='{0}'}" />
Этот код отображает тип источника привязки (DataBindingDemos.PathVariationsPage
). Известно, что страница PathVariationsPage
является производной ContentPage
, поэтому она имеет свойство Content
:
<Label Text="{Binding Source={x:Reference page},
Path=Content,
StringFormat='{0}'}" />
Теперь видно, что свойство Content
имеет тип Xamarin.Forms.StackLayout
. Добавьте свойство Children
в Path
, и будет ясно, что его тип — Xamarin.Forms.ElementCollection'1[Xamarin.Forms.View]
. Это внутренний класс Xamarin.Forms, однако очевидно, что он представляет собой коллекцию. Добавьте индекс. Тип — Xamarin.Forms.Label
. Продолжайте тем же образом.
Когда Xamarin.Forms обрабатывает путь привязки, для каждого объекта, реализующего интерфейс PropertyChanged
, в пути устанавливается обработчик INotifyPropertyChanged
. Например, последняя привязка реагирует на изменение в первом элементе Label
, так как меняется свойство Text
.
Если свойство в пути привязки не реализует интерфейс INotifyPropertyChanged
, изменения этого свойства игнорируются. Некоторые изменения могут сделать путь привязки полностью недействительным, поэтому этот прием следует использовать, только если строка из свойств и вложенных свойств не может стать недействительной.