Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Важные API
Замечание
В этом разделе подробно описаны функции привязки данных. Краткое практическое введение см. в обзоре привязки данных.
В этом разделе описывается привязка данных для API, которые находятся в пространстве имен Windows.UI.Xaml.Data.
Привязка данных позволяет пользовательскому интерфейсу приложения отображать данные и, при необходимости, оставаться с ними синхронизированным. Привязка данных позволяет отделять данные от проблем пользовательского интерфейса, что приводит к более простой концептуальной модели, а также более эффективной удобочитаемости, удобства тестирования и удобства обслуживания приложения.
Привязка данных позволяет просто отображать значения из источника данных при первом отображении пользовательского интерфейса, но не реагировать на изменения этих значений. Это режим привязки, называемый однократным, и он хорошо подходит для значения, которое не изменяется во время выполнения. Кроме того, можно выбрать "наблюдать" значения и обновлять пользовательский интерфейс при их изменении. Этот режим называется односторонним, и он хорошо подходит для данных только для чтения. В конечном счете можно выбрать как наблюдать, так и обновлять, чтобы изменения, внесенные пользователем в значения в пользовательском интерфейсе, автоматически отправляются обратно в источник данных. Этот режим называется двусторонним, и он хорошо подходит для данных чтения и записи. Ниже приведены некоторые примеры.
- Вы можете использовать одноразовый режим для привязки изображения к фотографии текущего пользователя.
- Вы можете использовать односторонний режим для привязки ListView к коллекции новостей в режиме реального времени, сгруппированных по разделу газеты.
- Вы можете использовать двусторонний режим для привязки TextBox к имени клиента в форме.
Независимо от режима, существует два типа привязки, и они обычно объявляются в разметке пользовательского интерфейса. Вы можете использовать расширение разметки {x:Bind} или расширение разметки {Binding}. И вы даже можете использовать смесь двух в одном приложении, даже в одном элементе пользовательского интерфейса. {x:Bind} является новым для Windows 10 и имеет более высокую производительность. Все сведения, описанные в этом разделе, применяются к обоим типам привязки, если мы явно не говорим в противном случае.
Примеры приложений, демонстрирующих {x:Bind}
Примеры приложений, демонстрирующих {Binding}
- Скачайте приложение Bookstore1 .
- Скачайте приложение Bookstore2 .
Каждая привязка включает в себя эти части
- Источник привязки. Это источник данных для привязки, и он может быть экземпляром любого класса, имеющего элементы, значения которых необходимо отобразить в пользовательском интерфейсе.
- Целевой объект привязки. Это зависимостьPropertyFrameworkElement в пользовательском интерфейсе, отображающая данные.
- Объект привязки. Это часть, которая передает значения данных из источника в целевой объект и при необходимости из целевого объекта обратно в источник. Объект привязки создается во время загрузки XAML из расширения разметки {x:Bind} или {Binding} .
В следующих разделах мы рассмотрим источник привязки, целевой объект привязки и объект привязки. И мы свяжит разделы вместе с примером привязки содержимого кнопки к строковому свойству с именем NextButtonText, который принадлежит классу HostViewModel.
Источник привязки
Вот очень рудиментная реализация класса, который мы могли бы использовать в качестве источника привязки.
Если вы используете C++/WinRT, добавьте новые элементы Midl File (IDL) в проект, как показано в примере кода C++/WinRT ниже. Замените содержимое этих новых файлов кодом MIDL 3.0 , показанным в списке, создайте проект для создания HostViewModel.h
и .cpp
добавьте код в созданные файлы, чтобы соответствовать списку. Дополнительные сведения о созданных файлах и их копировании в проект см. в элементах управления XAML; привязке к свойству C++/WinRT.
public class HostViewModel
{
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText { get; set; }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
runtimeclass HostViewModel
{
HostViewModel();
String NextButtonText;
}
}
// HostViewModel.h
// Implement the constructor like this, and add this field:
...
HostViewModel() : m_nextButtonText{ L"Next" } {}
...
private:
std::wstring m_nextButtonText;
...
// HostViewModel.cpp
// Implement like this:
...
hstring HostViewModel::NextButtonText()
{
return hstring{ m_nextButtonText };
}
void HostViewModel::NextButtonText(hstring const& value)
{
m_nextButtonText = value;
}
...
Эта реализация HostViewModel и его свойство NextButtonText подходят только для однократной привязки. Но односторонняя и двусторонняя привязка крайне распространена, и в этих типах привязка пользовательского интерфейса автоматически обновляется в ответ на изменения значений данных источника привязки. Чтобы эти типы привязки работали правильно, необходимо сделать источник привязки наблюдаемым для объекта привязки. Поэтому в нашем примере, если мы хотим выполнить одностороннюю или двустороннюю привязку к свойству NextButtonText , все изменения, которые происходят во время выполнения, к значению этого свойства необходимо сделать наблюдаемым для объекта привязки.
Один из способов сделать это — наследовать класс, представляющий источник привязки из DependencyObject, и предоставить значение данных через DependencyProperty. Вот как платформа FrameworkElement становится наблюдаемой. FrameworkElements — это хорошие источники привязки прямо из поля.
Более упрощенный способ создания наблюдаемого класса (и необходимого для классов, у которых уже есть базовый класс), — реализовать System.ComponentModel.INotifyPropertyChanged. Это действительно просто включает реализацию одного события с именем PropertyChanged. Ниже приведен пример использования HostViewModel .
...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
private string nextButtonText;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText
{
get { return this.nextButtonText; }
set
{
this.nextButtonText = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// HostViewModel.idl
namespace DataBindingInDepth
{
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
HostViewModel();
String NextButtonText;
}
}
// HostViewModel.h
// Add this field:
...
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
void PropertyChanged(winrt::event_token const& token) noexcept;
private:
winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
...
// HostViewModel.cpp
// Implement like this:
...
void HostViewModel::NextButtonText(hstring const& value)
{
if (m_nextButtonText != value)
{
m_nextButtonText = value;
m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"NextButtonText" });
}
}
winrt::event_token HostViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
return m_propertyChanged.add(handler);
}
void HostViewModel::PropertyChanged(winrt::event_token const& token) noexcept
{
m_propertyChanged.remove(token);
}
...
Теперь наблюдаемое свойство NextButtonText . При создании односторонняя или двусторонняя привязка к свойству (мы покажем, как позже), результирующий объект привязки подписывается на событие PropertyChanged . При возникновении этого события обработчик объекта привязки получает аргумент, содержащий имя измененного свойства. Вот как объект привязки знает, какое значение свойства следует идти и читать снова.
Таким образом, вам не нужно реализовать шаблон, показанный выше несколько раз, если вы используете C#, вы можете просто получить производный от базового класса BindableBase , который вы найдете в примере QuizGame (в папке Common). Ниже приведен пример того, как это выглядит.
public class HostViewModel : BindableBase
{
private string nextButtonText;
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText
{
get { return this.nextButtonText; }
set { this.SetProperty(ref this.nextButtonText, value); }
}
}
// Your BindableBase base class should itself derive from Windows::UI::Xaml::DependencyObject. Then, in HostViewModel.idl, derive from BindableBase instead of implementing INotifyPropertyChanged.
Замечание
Для C++/WinRT любой класс среды выполнения, объявленный в приложении, который является производным от базового класса, называется составным классом. Существуют ограничения для составных классов. Чтобы приложение передавало тесты комплекта сертификации приложений Windows , используемые Visual Studio и Microsoft Store для проверки отправки (и поэтому для успешного приема приложения в Microsoft Store), составной класс должен в конечном итоге быть производным от базового класса Windows. Это означает, что класс в самом корне иерархии наследования должен быть типом, происходящим в пространстве имен Windows.* . Если необходимо наследовать класс среды выполнения из базового класса, например для реализации класса BindableBase для всех моделей представления, производных от них, можно получить от Windows.UI.Xaml.DependencyObject.
Вызов события PropertyChanged с аргументом String.Empty или NULL указывает, что все свойства, отличные от индексатора в объекте, должны быть повторно считываются. Событие можно вызвать, чтобы указать, что свойства индексатора объекта изменились, используя аргумент Item[indexer], для определенных индексаторов (где индексатор является значением индекса), или значение "Item[]" для всех индексаторов.
Источник привязки можно рассматривать как один объект, свойства которого содержат данные или как коллекцию объектов. В коде C# и Visual Basic можно выполнить однократную привязку к объекту, реализующем list(Of T), чтобы отобразить коллекцию, которая не изменяется во время выполнения. Для наблюдаемой коллекции (наблюдая, когда элементы добавляются и удаляются из коллекции), односторонняя привязка к ObservableCollection(Of T) вместо этого. В коде C++/CX можно привязать к Vector<T> как для наблюдаемых, так и не наблюдаемых коллекций, а C++/WinRT имеет собственные типы. Чтобы выполнить привязку к собственным классам коллекции, используйте инструкции в следующей таблице.
Scenario | C# и VB (CLR) | C++/WinRT | C++/CX |
---|---|---|---|
Привязка к объекту. | Может быть любым объектом. | Может быть любым объектом. | Объект должен иметь BindableAttribute или реализовать ICustomPropertyProvider. |
Получение уведомлений об изменении свойства из привязанного объекта. | Объект должен реализовать INotifyPropertyChanged. | Объект должен реализовать INotifyPropertyChanged. | Объект должен реализовать INotifyPropertyChanged. |
Привязка к коллекции. | List(Of T) | IVectorIInspectable или IBindableObservableVector. См. элементы управления XAML; привязка к коллекции C++/WinRT и коллекциям с помощью C++/WinRT. | Вектор<T> |
Получение уведомлений об изменении коллекции из связанной коллекции. | ObservableCollection(Of T) | IObservableVector iInspectable. Например, winrt::single_threaded_observable_vector<T>. | IObservableVector<Т>. Вектор<T> реализует этот интерфейс. |
Реализуйте коллекцию, поддерживающую привязку. | Расширение списка (T) или реализация IList, IList(of Object), IEnumerable или IEnumerable(Of Object). Привязка к универсальным IList(Of T) и IEnumerable(Of T) не поддерживается. | Реализуйте IVector iInspectable. См. элементы управления XAML; привязка к коллекции C++/WinRT и коллекциям с помощью C++/WinRT. | РеализуйтеIBindableVector, IBindableIterable, IVector< Object^, IIterable<Object^>>, IVector<IInspectable*или IIterable<IInspectable*>.> Привязка к универсальному IVector<T> и IIterable<T> не поддерживается. |
Реализуйте коллекцию, которая поддерживает уведомления об изменении коллекции. | Расширение ObservableCollection(Of T) или реализация (не универсального) IList и INotifyCollectionChanged. | Реализуйте IObservableVector iInspectable или IBindableObservableVector. | Реализуйте IBindableVector и IBindableObservableVector. |
Реализуйте коллекцию, которая поддерживает добавочную загрузку. | Расширение ObservableCollection(Of T) или реализация (не универсального) IList и INotifyCollectionChanged. Кроме того, реализуйте ISupportIncrementalLoading. | Реализуйте IObservableVector iInspectable или IBindableObservableVector. Кроме того, реализуйте ISupportIncrementalLoading | Реализуйте IBindableVector, IBindableObservableVector и ISupportIncrementalLoading. |
Можно привязать элементы управления списка к произвольным большим источникам данных и обеспечить высокую производительность с помощью добавочной загрузки. Например, можно привязать элементы управления списками к результатам запроса изображений Bing, не загружая все результаты одновременно. Вместо этого вы загружаете только некоторые результаты немедленно и загружаете дополнительные результаты по мере необходимости. Для поддержки добавочной загрузки необходимо реализовать ISupportIncrementalLoading в источнике данных, поддерживающем уведомления об изменении коллекции. Когда подсистема привязки данных запрашивает больше данных, источник данных должен выполнять соответствующие запросы, интегрировать результаты, а затем отправлять соответствующие уведомления для обновления пользовательского интерфейса.
Целевой объект привязки
В двух примерах ниже свойство Button.Content является целевым объектом привязки, а его значение имеет расширение разметки, которое объявляет объект привязки. Сначала отображается {x:Bind} , а затем {Binding}. Объявление привязок в разметке является обычным случаем (удобно, доступно для чтения и инструментирования). Но при необходимости можно избежать разметки и императивно (программно) создать экземпляр класса Binding .
<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />
Если вы используете расширения компонентов C++/WinRT или Visual C++ (C++/CX), необходимо добавить атрибут BindableAttribute в любой класс среды выполнения, с которым вы хотите использовать расширение разметки {Binding} .
Это важно
Если вы используете C++/WinRT, атрибут BindableAttribute доступен, если установлен пакет SDK для Windows версии 10.0.17763.0 (Windows 10, версия 1809) или более поздней версии. Без этого атрибута необходимо реализовать интерфейсы ICustomPropertyProvider и ICustomProperty , чтобы использовать расширение разметки {Binding} .
Объект привязки, объявленный с помощью {x:Bind}
Перед созданием разметки {x:Bind} необходимо выполнить один шаг. Нам нужно предоставить исходный класс привязки из класса, представляющего страницу разметки. Мы это делаем, добавив свойство (типа HostViewModel в данном случае) в класс страницы MainPage .
namespace DataBindingInDepth
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.ViewModel = new HostViewModel();
}
public HostViewModel ViewModel { get; set; }
}
}
// MainPage.idl
import "HostViewModel.idl";
namespace DataBindingInDepth
{
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
HostViewModel ViewModel{ get; };
}
}
// MainPage.h
// Include a header, and add this field:
...
#include "HostViewModel.h"
...
DataBindingInDepth::HostViewModel ViewModel();
private:
DataBindingInDepth::HostViewModel m_viewModel{ nullptr };
...
// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
InitializeComponent();
}
DataBindingInDepth::HostViewModel MainPage::ViewModel()
{
return m_viewModel;
}
...
Это сделано, теперь можно более подробно взглянуть на разметку, которая объявляет объект привязки. В приведенном ниже примере используется тот же целевой объект привязки Button.Content , который мы использовали в разделе "Целевой объект привязки" ранее и показывает, что он привязан к свойству HostViewModel.NextButtonText .
<!-- MainPage.xaml -->
<Page x:Class="DataBindingInDepth.Mainpage" ... >
<Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Page>
Обратите внимание на значение, которое мы указываем для Path. Это значение интерпретируется в контексте самой страницы, и в этом случае путь начинается путем ссылки на свойство ViewModel , которое мы только что добавили на страницу MainPage . Это свойство возвращает экземпляр HostViewModel и поэтому мы можем указать этот объект для доступа к свойству HostViewModel.NextButtonText . И мы указываем режим, чтобы переопределить значение по умолчанию {x:Bind} однократно.
Свойство Path поддерживает различные варианты синтаксиса привязки к вложенным свойствам, присоединенным свойствам и целым числам и строковым индексаторам. Дополнительные сведения см. в разделе "Синтаксис пути свойства". Привязка к строковым индексаторам дает эффект привязки к динамическим свойствам без реализации ICustomPropertyProvider. Сведения о других параметрах см. в разделе {x:Bind}.
Чтобы иллюстрировать, что свойство HostViewModel.NextButtonText действительно наблюдаемо, добавьте обработчик событий Click в кнопку и обновите значение HostViewModel.NextButtonText. Выполните сборку, запустите и нажмите кнопку, чтобы увидеть значение обновления содержимого кнопки.
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
this.ViewModel.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
ViewModel().NextButtonText(L"Updated Next button text");
}
Замечание
Изменения в TextBox.Text отправляются в двустороннюю привязку источника, когда TextBox теряет фокус, а не после каждого нажатия клавиш пользователей.
DataTemplate и x:DataType
Внутри DataTemplate (используется ли в качестве шаблона элемента, шаблона содержимого или шаблона заголовка), значение Path не интерпретируется в контексте страницы, но в контексте шаблона объекта данных. При использовании {x:Bind} в шаблоне данных, чтобы его привязки можно было проверить (и эффективный код, созданный для них) во время компиляции, DataTemplate должен объявить тип объекта данных с помощью x:DataType. Приведенный ниже пример можно использовать в качестве элемента управления ItemTemplate элемента управления, привязанного к коллекции объектов SampleDataGroup .
<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
<StackPanel Orientation="Vertical" Height="50">
<TextBlock Text="{x:Bind Title}"/>
<TextBlock Text="{x:Bind Description}"/>
</StackPanel>
</DataTemplate>
Слабо типизированные объекты в пути
Рассмотрим, например, что у вас есть тип SampleDataGroup, который реализует строковое свойство Title. У вас есть свойство MainPage.SampleDataGroupAsObject, которое является объектом типа, но на самом деле возвращает экземпляр SampleDataGroup. Привязка <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/>
приведет к ошибке компиляции, так как свойство Title не найдено в объекте типа. Исправление этого заключается в добавлении приведения к синтаксису Path, как показано ниже <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>
. Вот еще один пример, в котором элемент объявлен как объект, но на самом деле является TextBlock: <TextBlock Text="{x:Bind Element.Text}"/>
И приведение средств устранения проблемы: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>
Если данные загружаются асинхронно
Код для поддержки {x:Bind} создается во время компиляции в частичных классах для страниц. Эти файлы можно найти в obj
папке с такими именами, как (для C#). <view name>.g.cs
Созданный код включает обработчик события загрузки страницы, а обработчик вызывает метод Initialize в созданном классе, представляющем привязки страницы.
Инициализация в свою очередь вызывает Update , чтобы начать перемещение данных между источником привязки и целевым объектом.
Загрузка возникает непосредственно перед первым прохождением меры страницы или пользовательского элемента управления. Таким образом, если данные загружаются асинхронно, к моменту вызова инициализации может не быть готово. Таким образом, после загрузки данных можно принудительно инициализировать однократные привязки путем вызова this.Bindings.Update();
. Если вам нужны только однократные привязки для асинхронно загруженных данных, то это гораздо дешевле инициализировать их таким образом, чем иметь односторонние привязки и прослушивать изменения. Если данные не проходят детализированные изменения, и если они, скорее всего, будут обновлены в рамках определенного действия, вы можете выполнить однократные привязки и принудительно обновить вручную при вызове обновления.
Замечание
{x:Bind} не подходит для сценариев с поздней привязкой, таких как навигация по структуре словаря объекта JSON, а также для ввода утки. "Дак ввод" является слабой формой ввода на основе лексических совпадений по именам свойств (как в, "если он идет, плавает, и квак, как утка, то это утка"). При вводе утки привязка к свойству Age будет одинаково удовлетворена объектом Person или Wine (предполагая, что эти типы имеют свойство Age ). Для этих сценариев используйте расширение разметки {Binding} .
Объект привязки, объявленный с помощью {Binding}
Если вы используете расширения компонентов C++/WinRT или Visual C++ (C++/CX), то для использования расширения разметки {Binding} необходимо добавить атрибут BindableAttribute в любой класс среды выполнения, к которому требуется привязаться. Чтобы использовать {x:Bind}, этот атрибут не нужен.
// HostViewModel.idl
// Add this attribute:
[Windows.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
...
Это важно
Если вы используете C++/WinRT, атрибут BindableAttribute доступен, если установлен пакет SDK для Windows версии 10.0.17763.0 (Windows 10, версия 1809) или более поздней версии. Без этого атрибута необходимо реализовать интерфейсы ICustomPropertyProvider и ICustomProperty , чтобы использовать расширение разметки {Binding} .
{Binding} предполагает, что по умолчанию вы привязываетесь к DataContext страницы разметки. Поэтому мы задали DataContext нашей страницы как экземпляр исходного класса привязки (типа HostViewModel в данном случае). В приведенном ниже примере показана разметка, которая объявляет объект привязки. Мы используем тот же целевой объект привязки Button.Content , который мы использовали в разделе "Целевой объект привязки" ранее, и мы привязываемся к свойству HostViewModel.NextButtonText .
<Page xmlns:viewmodel="using:DataBindingInDepth" ... >
<Page.DataContext>
<viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
</Page.DataContext>
...
<Button Content="{Binding Path=NextButtonText}" ... />
</Page>
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
this.viewModelInDataContext.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
viewModelInDataContext().NextButtonText(L"Updated Next button text");
}
Обратите внимание на значение, которое мы указываем для Path. Это значение интерпретируется в контексте DataContext страницы, в котором в этом примере устанавливается экземпляр HostViewModel. Путь ссылается на свойство HostViewModel.NextButtonText . Мы можем опустить режим, так как {Binding} по умолчанию используется односторонняя работа.
Значение dataContext по умолчанию для элемента пользовательского интерфейса является унаследованным значением родительского элемента. Конечно, можно переопределить это по умолчанию, установив DataContext явным образом, который, в свою очередь, наследуется дочерними элементами по умолчанию. Параметр DataContext явно используется для элемента, если требуется иметь несколько привязок, использующих один и тот же источник.
Объект привязки имеет свойство Source , которое по умолчанию использует dataContext элемента пользовательского интерфейса, для которого объявлена привязка. Этот параметр по умолчанию можно переопределить, задав source, RelativeSource или ElementName явно в привязке (дополнительные сведения см. в разделе {Binding} ).
Внутри DataTemplatedataContext автоматически устанавливается для шаблона объекта данных. Приведенный ниже пример можно использовать в качестве элемента управления ItemTemplate элемента управления, привязанного к коллекции любого типа, имеющего строковые свойства с именем Title и Description.
<DataTemplate x:Key="SimpleItemTemplate">
<StackPanel Orientation="Vertical" Height="50">
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding Description"/>
</StackPanel>
</DataTemplate>
Замечание
По умолчанию изменения в TextBox.Text отправляются в двусторонне привязанный источник при потере фокуса TextBox . Чтобы вызвать отправку изменений после каждого нажатия клавиш пользователей, задайте для СвойстваSourceTrigger значение PropertyChanged в разметке привязки. Вы также можете полностью контролировать, когда изменения отправляются в источник, задав UpdateSourceTriggerзначение "Явный". Затем вы обрабатываете события в текстовом поле (обычно TextBox.TextChanged), вызываете GetBindingExpression в целевом объекте, чтобы получить объект BindingExpression, и, наконец, вызовите BindingExpression.UpdateSource для программного обновления источника данных.
Свойство Path поддерживает различные варианты синтаксиса привязки к вложенным свойствам, присоединенным свойствам и целым числам и строковым индексаторам. Дополнительные сведения см. в разделе "Синтаксис пути свойства". Привязка к строковым индексаторам дает эффект привязки к динамическим свойствам без реализации ICustomPropertyProvider. Свойство ElementName полезно для привязки элемента к элементу. Свойство RelativeSource имеет несколько вариантов использования, один из которых является более мощной альтернативой привязке шаблона внутри ControlTemplate. Сведения о других параметрах см. в разделе "Расширение разметки {Binding} и класс Binding ".
Что делать, если источник и целевой объект не совпадают с типом?
Если вы хотите управлять видимостью элемента пользовательского интерфейса на основе значения логического свойства или вы хотите отобразить элемент пользовательского интерфейса с цветом, который является функцией диапазона или тренда числового значения, или если вы хотите отобразить значение даты и времени в свойстве элемента пользовательского интерфейса, которое ожидает строку, затем необходимо преобразовать значения из одного типа в другой. Там будут случаи, когда правильное решение заключается в том, чтобы предоставить другое свойство правильного типа из исходного класса привязки и сохранить логику преобразования, инкапсулированную и проверяемую там. Но это не гибко или масштабируемо, если у вас есть большое количество или большие сочетания исходных и целевых свойств. В этом случае у вас есть несколько вариантов:
- При использовании {x:Bind} можно привязать непосредственно к функции для этого преобразования.
- Или можно указать преобразователь значений, который является объектом, предназначенным для выполнения преобразования.
Преобразователи значений
Вот преобразователь значений, подходящий для однократной или односторонняя привязка, которая преобразует значение DateTime в строковое значение, содержащее месяц. Класс реализует IValueConverter.
public class DateToStringConverter : IValueConverter
{
// Define the Convert method to convert a DateTime value to
// a month string.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// value is the data from the source object.
DateTime thisdate = (DateTime)value;
int monthnum = thisdate.Month;
string month;
switch (monthnum)
{
case 1:
month = "January";
break;
case 2:
month = "February";
break;
default:
month = "Month not found";
break;
}
// Return the value to pass to the target.
return month;
}
// ConvertBack is not implemented for a OneWay binding.
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
// See the "Formatting or converting data values for display" section in the "Data binding overview" topic.
И вот как вы используете этот преобразователь значений в разметке объекта привязки.
<UserControl.Resources>
<local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0"
Text="{x:Bind ViewModel.Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="0"
Text="{Binding Month, Converter={StaticResource Converter1}}"/>
Подсистема привязки вызывает методы Convert и ConvertBack , если параметр Converter определен для привязки. При передаче данных из источника подсистема привязки вызывает преобразование и передает возвращенные данные в целевой объект. При передаче данных из целевого объекта (для двусторонней привязки) подсистема привязки вызывает ConvertBack и передает возвращенные данные источнику.
Преобразователь также имеет необязательные параметры: ConverterLanguage, который позволяет указывать язык, используемый в преобразовании, и ConverterParameter, что позволяет передавать параметр для логики преобразования. Пример использования параметра преобразователя см. в разделе IValueConverter.
Замечание
Если в преобразовании возникает ошибка, не вызывайте исключение. Вместо этого возвращает значение DependencyProperty.UnsetValue, которое остановит передачу данных.
Чтобы отобразить значение по умолчанию, используемое всякий раз, когда источник привязки не может быть разрешен, задайте свойство FallbackValue в объекте привязки в разметке. Это полезно для обработки ошибок преобразования и форматирования. Также полезно привязать к свойствам источника, которые могут не существовать во всех объектах в связанной коллекции разнородных типов.
Если привязать текстовый элемент управления к значению, которое не является строкой, подсистема привязки данных преобразует значение в строку. Если значение является ссылочным типом, подсистема привязки данных извлекает строковое значение путем вызова ICustomPropertyProvider.GetStringRepresentation или IStringable.ToString , если оно доступно, и в противном случае вызовет Object.ToString. Обратите внимание, что подсистема привязки будет игнорировать любую реализацию ToString , которая скрывает реализацию базового класса. Реализации подклассов должны переопределить метод ToString базового класса. Аналогичным образом, на собственных языках все управляемые объекты, как представляется, реализуют ICustomPropertyProvider и IStringable. Однако все вызовы GetStringRepresentation и IStringable.ToString направляются в Object.ToString или переопределение этого метода, и никогда не в новую реализацию ToString , которая скрывает реализацию базового класса.
Замечание
Начиная с Windows 10 версии 1607 платформа XAML предоставляет встроенный логический преобразователь видимости. Преобразователь сопоставляется со значением перечисления Visible и false с свернутым , чтобы можно было привязать свойство Видимости к логическому объекту без создания преобразователя. Чтобы использовать встроенный преобразователь, минимальная целевая версия пакета SDK приложения должна быть 14393 или более поздней. Его нельзя использовать, если приложение предназначено для более ранних версий Windows 10. Дополнительные сведения о целевых версиях см. в разделе "Адаптивный код версии".
Привязка функции в {x:Bind}
{x:Bind} позволяет окончательному шагу в пути привязки быть функцией. Это можно использовать для выполнения преобразований и выполнения привязок, зависящих от нескольких свойств. См . функции в x:Bind
Привязка элемента к элементу
Свойство одного элемента XAML можно привязать к свойству другого элемента XAML. Ниже приведен пример того, как это выглядит в разметке.
<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />
Это важно
Необходимый рабочий процесс для привязки элемента к элементу с помощью C++/WinRT см. в разделе привязки "Элемент — элемент".
Словари ресурсов с {x:Bind}
Расширение разметки {x:Bind} зависит от создания кода, поэтому для инициализации созданного кода требуется файл программной части, содержащий конструктор, вызывающий InitializeComponent (для инициализации созданного кода). Вы повторно используете словарь ресурсов, создав экземпляр своего типа (так что вызывается InitializeComponent ), а не ссылаясь на его имя файла. Ниже приведен пример того, что делать, если у вас есть существующий словарь ресурсов, и вы хотите использовать {x:Bind} в нем.
TemplatesResourceDictionary.xaml
<ResourceDictionary
x:Class="ExampleNamespace.TemplatesResourceDictionary"
.....
xmlns:examplenamespace="using:ExampleNamespace">
<DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
<Grid>
<TextBlock Text="{x:Bind Name}"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
TemplatesResourceDictionary.xaml.cs
using Windows.UI.Xaml.Data;
namespace ExampleNamespace
{
public partial class TemplatesResourceDictionary
{
public TemplatesResourceDictionary()
{
InitializeComponent();
}
}
}
MainPage.xaml
<Page x:Class="ExampleNamespace.MainPage"
....
xmlns:examplenamespace="using:ExampleNamespace">
<Page.Resources>
<ResourceDictionary>
....
<ResourceDictionary.MergedDictionaries>
<examplenamespace:TemplatesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
</Page>
Сочетание {x:Bind} и {Binding} в повторно используемый стиль
Хотя предыдущий пример показал использование {x:Bind} в DataTemplates, вы также можете создавать повторно используемые стили, которые объединяют расширения разметки {x:Bind} и {Binding}. Это полезно, если требуется привязать некоторые свойства к известным значениям во время компиляции с помощью {x:Bind} и других свойств к значениям DataContext во время выполнения с помощью {Binding}.
Ниже приведен пример, в который показано, как создать повторно используемый стиль Button, использующий оба подхода к привязке:
TemplatesResourceDictionary.xaml
<ResourceDictionary
x:Class="ExampleNamespace.TemplatesResourceDictionary"
.....
xmlns:examplenamespace="using:ExampleNamespace">
<!-- DataTemplate using x:Bind -->
<DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
<Grid>
<TextBlock Text="{x:Bind Name}"/>
</Grid>
</DataTemplate>
<!-- Style that mixes x:Bind and Binding -->
<Style x:Key="CustomButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{Binding ButtonBackgroundBrush}"/>
<Setter Property="Foreground" Value="{Binding ButtonForegroundBrush}"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Margin" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="RootBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<!-- x:Bind to a static property or page-level property -->
<Ellipse Width="8" Height="8"
Fill="{x:Bind DefaultIndicatorBrush}"
Margin="0,0,8,0"/>
<!-- Binding to DataContext -->
<ContentPresenter x:Name="ContentPresenter"
Content="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
FontSize="{TemplateBinding FontSize}"/>
</StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<!-- Binding to DataContext for hover color -->
<Setter Target="RootBorder.Background"
Value="{Binding ButtonHoverBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<!-- x:Bind to a compile-time known resource -->
<Setter Target="RootBorder.Background"
Value="{x:Bind DefaultPressedBrush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
TemplatesResourceDictionary.xaml.cs
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace ExampleNamespace
{
public partial class TemplatesResourceDictionary
{
public TemplatesResourceDictionary()
{
InitializeComponent();
}
// Properties for x:Bind - these are compile-time bound
public SolidColorBrush DefaultIndicatorBrush { get; } =
new SolidColorBrush(Colors.Green);
public SolidColorBrush DefaultPressedBrush { get; } =
new SolidColorBrush(Colors.DarkGray);
}
}
Использование в MainPage.xaml с ViewModel, предоставляющим значения среды выполнения:
<Page x:Class="ExampleNamespace.MainPage"
....
xmlns:examplenamespace="using:ExampleNamespace">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<examplenamespace:TemplatesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<Page.DataContext>
<examplenamespace:ButtonThemeViewModel/>
</Page.DataContext>
<StackPanel Margin="20">
<!-- This button uses the mixed binding style -->
<Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
<Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
</StackPanel>
</Page>
ButtonThemeViewModel.cs (DataContext, предоставляющий значения привязки среды выполнения):
using System.ComponentModel;
using Windows.UI;
using Windows.UI.Xaml.Media;
namespace ExampleNamespace
{
public class ButtonThemeViewModel : INotifyPropertyChanged
{
private SolidColorBrush _buttonBackgroundBrush = new SolidColorBrush(Colors.LightBlue);
private SolidColorBrush _buttonForegroundBrush = new SolidColorBrush(Colors.DarkBlue);
private SolidColorBrush _buttonHoverBrush = new SolidColorBrush(Colors.LightCyan);
public SolidColorBrush ButtonBackgroundBrush
{
get => _buttonBackgroundBrush;
set
{
_buttonBackgroundBrush = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonBackgroundBrush)));
}
}
public SolidColorBrush ButtonForegroundBrush
{
get => _buttonForegroundBrush;
set
{
_buttonForegroundBrush = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonForegroundBrush)));
}
}
public SolidColorBrush ButtonHoverBrush
{
get => _buttonHoverBrush;
set
{
_buttonHoverBrush = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonHoverBrush)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
В этом примере:
- {Binding} используется для свойств, зависящих от DataContext (ButtonBackgroundBrush, ButtonForegroundBrush, ButtonHoverBrush)
- {x:Bind} используется для свойств, известных во время компиляции и принадлежащих самому ResourceDictionary (DefaultIndicatorBrush, DefaultPressedBrush)
- Стиль можно использовать повторно и применять к любой кнопке
- Их можно использовать с помощью DataContext при использовании {x:Bind} для статических элементов.
Привязка событий и ICommand
{x:Bind} поддерживает функцию, называемую привязкой событий. С помощью этой функции можно указать обработчик события с помощью привязки, которая является дополнительным параметром поверх обработки событий с помощью метода в файле кода программной части. Предположим, что у вас есть свойство RootFrame в классе MainPage .
public sealed partial class MainPage : Page
{
...
public Frame RootFrame { get { return Window.Current.Content as Frame; } }
}
Затем можно привязать событие click кнопки к методу объекта Frame , возвращаемого свойством RootFrame , как показано ниже. Обратите внимание, что мы также привязываем свойство IsEnabled кнопки к другому члену того же кадра.
<AppBarButton Icon="Forward" IsCompact="True"
IsEnabled="{x:Bind RootFrame.CanGoForward, Mode=OneWay}"
Click="{x:Bind RootFrame.GoForward}"/>
Перегруженные методы нельзя использовать для обработки события с помощью этого метода. Кроме того, если метод, обрабатывающий событие, имеет параметры, они должны быть назначены из типов всех параметров события соответственно. В этом случае Frame.GoForward не перегружен и не имеет параметров (но он по-прежнему будет допустимым, даже если он принял два параметра объекта ). Frame.GoBack перегружен, поэтому мы не можем использовать этот метод с этим методом.
Метод привязки событий аналогичен реализации и потреблению команд (команда — это свойство, возвращающее объект, реализующий интерфейс ICommand ). Как {x:Bind}, так и {Binding} работают с командами. Так что вам не нужно реализовать шаблон команды несколько раз, можно использовать вспомогательный класс ДелегатCommand , который вы найдете в примере QuizGame (в папке Common).
Привязка к коллекции папок или файлов
API в пространстве имен Windows.Storage можно использовать для получения данных папок и файлов. Однако различные методы GetFilesAsync, GetFoldersAsync и GetItemsAsync не возвращают значения, которые подходят для привязки к элементам управления списком. Вместо этого необходимо привязать к возвращаемым значениям методов GetVirtualizedFilesVector, GetVirtualizedFoldersVector и GetVirtualizedItemsVector класса FileInformationFactory . В следующем примере кода из примера StorageDataSource и GetVirtualizedFilesVector показан типичный шаблон использования. Не забудьте объявить функцию picturesLibrary в манифесте пакета приложения и убедиться, что в папке библиотеки изображений есть изображения.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var library = Windows.Storage.KnownFolders.PicturesLibrary;
var queryOptions = new Windows.Storage.Search.QueryOptions();
queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;
var fileQuery = library.CreateFileQueryWithOptions(queryOptions);
var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
fileQuery,
Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
190,
Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
false
);
var dataSource = fif.GetVirtualizedFilesVector();
this.PicturesListView.ItemsSource = dataSource;
}
Обычно этот подход используется для создания представления сведений о файлах и папках только для чтения. Можно создать двусторонние привязки к свойствам файла и папки, например, чтобы пользователи могли оценить песню в музыкальном представлении. Однако любые изменения не сохраняются, пока не вызовете соответствующий метод SavePropertiesAsync (например, MusicProperties.SavePropertiesAsync). Если элемент теряет фокус, необходимо зафиксировать изменения, так как это активирует сброс выделения.
Обратите внимание, что двусторонняя привязка с помощью этого метода работает только с индексированных расположений, таких как Музыка. Можно определить, индексируется ли расположение, вызвав метод FolderInformation.GetIndexedStateAsync .
Обратите внимание также, что виртуализированный вектор может возвращать значение NULL для некоторых элементов перед заполнением их значения. Например, перед использованием значения SelectedItem элемента управления списком, привязанного к виртуализированному вектору, необходимо проверить значение NULL или вместо этого использовать SelectedIndex.
Привязка к данным, сгруппированных по ключу
Если вы принимаете плоскую коллекцию элементов (например, книги, представленные классом BookSku ) и группируете элементы с помощью общего свойства в качестве ключа (например, свойство BookSku.AuthorName ), результат называется сгруппированными данными. При группировке данных она больше не является плоской коллекцией. Сгруппированные данные — это коллекция объектов группы, где каждый объект группы имеет
- ключ и
- коллекция элементов, свойство которых соответствует указанному ключу.
Чтобы повторить пример книг, результат группировки книг по имени автора приводит к коллекции групп имен автора, где каждая группа имеет
- ключ, который является именем автора, и
- коллекция bookSku, свойство AuthorName которого соответствует ключу группы.
Как правило, для отображения коллекции привязывается элемент управления ItemsSource элемента управления (например , ListView или GridView) непосредственно к свойству, возвращающего коллекцию. Если это плоская коллекция элементов, вам не нужно делать ничего специального. Но если это коллекция объектов группы (как и при привязке к группируемым данным), то вам нужны службы промежуточного объекта с именем CollectionViewSource , который находится между элементом управления элементами и источником привязки. Вы привязываете CollectionViewSource к свойству, возвращающее сгруппированные данные, и привязываете элемент управления items к CollectionViewSource. Дополнительная надстройка CollectionViewSource заключается в том, что она отслеживает текущий элемент, поэтому вы можете сохранить несколько элементов управления в синхронизации, привязав их ко всем тем же CollectionViewSource. Вы также можете получить доступ к текущему элементу программным способом с помощью свойства ICollectionView.CurrentItem объекта, возвращаемого свойством CollectionViewSource.View .
Чтобы активировать объект группировки объекта CollectionViewSource, задайте для IsSourceGroupedзначение true. Необходимо ли также задать свойство ItemsPath , зависит от того, как создавать объекты группы. Существует два способа создания объекта группы: шаблона is-a-group и шаблона has-a-group. В шаблоне "is-a-group" объект группы является производным от типа коллекции (например, List<T>), поэтому объект группы фактически является группой элементов. С помощью этого шаблона вам не нужно задавать ItemsPath. В шаблоне "has-a-group" объект группы имеет одно или несколько свойств типа коллекции (например , List<T>), поэтому группа "имеет" группу элементов в виде свойства (или несколько групп элементов в виде нескольких свойств). С помощью этого шаблона необходимо задать ItemsPath имя свойства, содержащего группу элементов.
В приведенном ниже примере показан шаблон "has-a-group". Класс страницы имеет свойство ViewModel, которое возвращает экземпляр модели представления. CollectionViewSource привязывается к свойству Author модели представления (Авторы — это коллекция объектов группы), а также указывает, что это свойство Author.BookSkus, содержащее сгруппированные элементы. Наконец, GridView привязан к CollectionViewSource и имеет свой стиль группы, чтобы он смог отобразить элементы в группах.
<Page.Resources>
<CollectionViewSource
x:Name="AuthorHasACollectionOfBookSku"
Source="{x:Bind ViewModel.Authors}"
IsSourceGrouped="true"
ItemsPath="BookSkus"/>
</Page.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
<GridView.GroupStyle>
<GroupStyle
HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
</GridView.GroupStyle>
</GridView>
Вы можете реализовать шаблон "is-a-group" одним из двух способов. Одним из способов является создание собственного класса группы. Наследуйте класс из списка<T> (где T является типом элементов). Например: public class Author : List<BookSku>
. Второй способ — использовать выражение LINQ для динамического создания объектов группы (и класса группы) из таких значений свойств элементов BookSku . Такой подход — обслуживание только плоского списка элементов и группирование их вместе на лету — обычно это приложение, которое обращается к данным из облачной службы. Вы получаете гибкость для группирования книг по автору или по жанру (например), не нуждаясь в специальных классах группы, таких как автор и жанр.
В приведенном ниже примере показан шаблон "is-a-group" с помощью LINQ. На этот раз мы группируем книги по жанру, отображаемым с именем жанра в заголовках группы. Это означает путь свойства Key в ссылке на значение ключа группы.
using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;
public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
get
{
if (this.genres == null)
{
this.genres = from book in this.bookSkus
group book by book.genre into grp
orderby grp.Key
select grp;
}
return this.genres;
}
}
Помните, что при использовании {x:Bind} с шаблонами данных необходимо указать тип, к которому привязан тип, задав значение x:DataType . Если тип является универсальным, мы не можем выразить это в разметке, поэтому вместо этого необходимо использовать {Binding} в шаблоне заголовка стиля группы.
<Grid.Resources>
<CollectionViewSource x:Name="GenreIsACollectionOfBookSku"
Source="{x:Bind Genres}"
IsSourceGrouped="true"/>
</Grid.Resources>
<GridView ItemsSource="{x:Bind GenreIsACollectionOfBookSku}">
<GridView.ItemTemplate x:DataType="local:BookTemplate">
<DataTemplate>
<TextBlock Text="{x:Bind Title}"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
Элемент управления SemanticZoom — отличный способ просмотра и навигации пользователей сгруппированных данных. Пример приложения Bookstore2 иллюстрирует использование SemanticZoom. В этом приложении можно просмотреть список книг, сгруппированных по автору (увеличенное представление) или просмотреть список переходов авторов (увеличенное представление). Список переходов обеспечивает гораздо более быструю навигацию, чем прокрутка по списку книг. Представления с увеличением и увеличением масштаба фактически являются элементами управления ListView или GridView , привязанными к одному и тому же объекту CollectionViewSource.
При привязке к иерархическим данным, таким как подкатегории в категориях, можно выбрать отображение иерархических уровней в пользовательском интерфейсе с рядом элементов управления. Выбор в одном элементе управления элементами определяет содержимое последующих элементов управления. Списки можно синхронизировать, привязав каждый список к собственному CollectionViewSource и привязав экземпляры CollectionViewSource вместе в цепочке. Это называется представлением master/details (или list/details). Дополнительные сведения см. в разделе "Как привязать к иерархическим данным" и создать представление master/details.
Диагностика и отладка проблем привязки данных
Разметка привязки содержит имена свойств (и для C#, иногда поля и методы). Поэтому при переименовании свойства также потребуется изменить любую привязку, которая ссылается на нее. Забыли это сделать, что приводит к типичному примеру ошибки привязки данных, и ваше приложение либо не будет компилироваться, либо не будет работать правильно.
Объекты привязки, созданные {x:Bind} и {Binding} , в значительной степени эквивалентны функционально. Но {x:Bind} содержит сведения о типе источника привязки, и он создает исходный код во время компиляции. При использовании {x:Bind} вы получаете то же самое обнаружение проблем, которое вы получаете с остальным кодом. Это включает проверку во время компиляции выражений привязки и отладку путем задания точек останова в исходном коде, созданного в качестве частичного класса для страницы. Эти классы можно найти в файлах в obj
папке с такими именами, как (для C#). <view name>.g.cs
Если у вас возникли проблемы с привязкой, включите необработанные исключения в отладчике Microsoft Visual Studio. Отладчик разорвит выполнение на этом этапе, и вы можете отладить то, что произошло неправильно. Код, созданный {x:Bind}, следует одному и тому же шаблону для каждой части графа исходных узлов привязки, и вы можете использовать сведения в окне стека вызовов , чтобы определить последовательность вызовов, которые привели к проблеме.
{Binding} не содержит сведений о типе источника привязки. Но при запуске приложения с присоединенным отладчиком все ошибки привязки отображаются в окне вывода в Visual Studio.
Создание привязок в коде
Заметка Этот раздел применяется только к {Binding}, так как в коде нельзя создавать привязки {x:Bind} . Однако некоторые из одних и того же преимущества {x:Bind} можно достичь с помощью DependencyObject.RegisterPropertyChangedCallback, что позволяет зарегистрировать уведомления об изменениях для любого свойства зависимостей.
Вы также можете подключить элементы пользовательского интерфейса к данным с помощью процедурного кода вместо XAML. Для этого создайте объект Binding, задайте соответствующие свойства, а затем вызовите FrameworkElement.SetBinding или BindingOperations.SetBinding.SetBinding. Создание привязок программным способом полезно при выборе значений свойств привязки во время выполнения или совместного использования одной привязки между несколькими элементами управления. Обратите внимание, что после вызова SetBinding нельзя изменить значения свойств привязки.
В следующем примере показано, как реализовать привязку в коде.
<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class
// that implements INotifyPropertyChanged.
MyColors textcolor = new MyColors();
// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);
// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;
// Create the binding and associate it with the text box.
Binding binding = new Binding() { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
' Create an instance of the MyColors class
' that implements INotifyPropertyChanged.
Dim textcolor As New MyColors()
' Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = New SolidColorBrush(Colors.Red)
' Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor
' Create the binding and associate it with the text box.
Dim binding As New Binding() With {.Path = New PropertyPath("Brush1")}
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding)
Сравнение функций {x:Bind} и {Binding}
Функция | {x:Bind} и {Binding} | Примечания. |
---|---|---|
Путь — это свойство по умолчанию | {x:Bind a.b.c} - {Binding a.b.c} |
|
Свойство Path | {x:Bind Path=a.b.c} - {Binding Path=a.b.c} |
В x:Bind путь коренится на странице по умолчанию, а не DataContext. |
Indexer | {x:Bind Groups[2].Title} - {Binding Groups[2].Title} |
Привязывается к указанному элементу в коллекции. Поддерживаются только целые индексы. |
Присоединенные свойства | {x:Bind Button22.(Grid.Row)} - {Binding Button22.(Grid.Row)} |
Присоединенные свойства задаются с помощью скобок. Если свойство не объявляется в пространстве имен XAML, префиксируйте его с пространством имен XML, которое должно быть сопоставлено с пространством имен кода в начале документа. |
Литьё | {x:Bind groups[0].(data:SampleDataGroup.Title)} - Не требуется для {Binding}. |
Приведения задаются с помощью круглых скобок. Если свойство не объявляется в пространстве имен XAML, префиксируйте его с пространством имен XML, которое должно быть сопоставлено с пространством имен кода в начале документа. |
Преобразователь | {x:Bind IsShown, Converter={StaticResource BoolToVisibility}} - {Binding IsShown, Converter={StaticResource BoolToVisibility}} |
Преобразователи должны быть объявлены в корне страницы или resourceDictionary или в App.xaml. |
ConverterParameter, ConverterLanguage | {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr} - {Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr} |
Преобразователи должны быть объявлены в корне страницы или resourceDictionary или в App.xaml. |
TargetNullValue | {x:Bind Name, TargetNullValue=0} - {Binding Name, TargetNullValue=0} |
Используется, когда конечная часть выражения привязки имеет значение NULL. Используйте одинарные кавычки для строкового значения. |
Резервное значение | {x:Bind Name, FallbackValue='empty'} - {Binding Name, FallbackValue='empty'} |
Используется, если любая часть пути для привязки (за исключением конечной) имеет значение NULL. |
ElementName | {x:Bind slider1.Value} - {Binding Value, ElementName=slider1} |
При использовании {x:Bind} вы привязываетесь к полю; Путь коренится на странице по умолчанию, поэтому любой именованный элемент можно получить через его поле. |
RelativeSource: Self | <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... /> - <Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... /> |
При помощи {x:Bind}назовите элемент и используйте его имя в Path. |
RelativeSource: TemplatedParent | Не требуется для {x:Bind} - {Binding <path>, RelativeSource={RelativeSource TemplatedParent}} |
При использовании {x:Bind} TargetType в ControlTemplate указывает привязку к родительскому элементу шаблона. Для {Binding} Обычная привязка шаблона может использоваться в шаблонах элементов управления для большинства используемых элементов управления. Но используйте TemplatedParent, где необходимо использовать преобразователь или двусторонняя привязка.< |
Исходный материал | Не требуется для {x:Bind} - <ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/> |
Для {x:Bind} можно напрямую использовать именованный элемент, использовать свойство или статический путь. |
Mode | {x:Bind Name, Mode=OneWay} - {Binding Name, Mode=TwoWay} |
Режим может быть OneTime, OneWay или TwoWay. {x:Bind} по умолчанию используется oneTime; {Binding} по умолчанию имеет значение OneWay. |
UpdateSourceTrigger | {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged} - {Binding UpdateSourceTrigger=PropertyChanged} |
UpdateSourceTrigger может быть Default, LostFocus или PropertyChanged. {x:Bind} не поддерживает UpdateSourceTrigger=Explicit. {x:Bind} использует поведение PropertyChanged для всех случаев, кроме TextBox.Text, где используется поведение LostFocus. |