Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе показано, как привязать элемент управления (или другой элемент пользовательского интерфейса) к одному элементу или привязать элемент управления элементами к коллекции элементов в приложении универсальной платформы Windows (UWP). Кроме того, мы покажем, как управлять отрисовкой элементов, реализовывать представление сведений на основе выделения и преобразовывать данные для отображения. Более подробную информацию см. в Подробное изучение привязки данных.
Предпосылки
В этом разделе предполагается, что вы знаете, как создать базовое приложение UWP. Инструкции по созданию первого приложения UWP см. в статье Начало работы с приложениями Windows.
Создание проекта
Создайте новый проект Пустое приложение (универсальное приложение Windows). Назовите его "Быстрый старт".
Привязка к одному элементу
Каждая привязка состоит из целевого объекта привязки и источника привязки. Как правило, целевой объект — это свойство элемента управления или другого элемента пользовательского интерфейса, а источник — это свойство экземпляра класса (модель данных или модель представления). В этом примере показано, как привязать элемент управления к одному элементу. Целью является свойство Text блока TextBlock. Источник — это экземпляр простого класса с именем Recording, который представляет собой звукозапись. Давайте сначала рассмотрим класс.
Если вы используете C# или C++/CX, добавьте новый класс в проект и присвойте классу имя записи.
Если вы используете C++/WinRT, добавьте новые элементы Midl File (.idl) в проект, как показано в примере кода C++/WinRT ниже. Замените содержимое этих новых файлов кодом MIDL 3.0, показанным в списке, создайте проект для генерации Recording.h
и .cpp
и RecordingViewModel.h
и .cpp
, а затем добавьте код в созданные файлы, чтобы он соответствовал списку. Дополнительные сведения о созданных файлах и их копировании в проект см. в разделе об элементах управления XAML и о том, как привязать их к свойству C++/WinRT.
namespace Quickstart
{
public class Recording
{
public string ArtistName { get; set; }
public string CompositionName { get; set; }
public DateTime ReleaseDateTime { get; set; }
public Recording()
{
this.ArtistName = "Wolfgang Amadeus Mozart";
this.CompositionName = "Andante in C for Piano";
this.ReleaseDateTime = new DateTime(1761, 1, 1);
}
public string OneLineSummary
{
get
{
return $"{this.CompositionName} by {this.ArtistName}, released: "
+ this.ReleaseDateTime.ToString("d");
}
}
}
public class RecordingViewModel
{
private Recording defaultRecording = new Recording();
public Recording DefaultRecording { get { return this.defaultRecording; } }
}
}
// Recording.idl
namespace Quickstart
{
runtimeclass Recording
{
Recording(String artistName, String compositionName, Windows.Globalization.Calendar releaseDateTime);
String ArtistName{ get; };
String CompositionName{ get; };
Windows.Globalization.Calendar ReleaseDateTime{ get; };
String OneLineSummary{ get; };
}
}
// RecordingViewModel.idl
import "Recording.idl";
namespace Quickstart
{
runtimeclass RecordingViewModel
{
RecordingViewModel();
Quickstart.Recording DefaultRecording{ get; };
}
}
// Recording.h
// Add these fields:
...
#include <sstream>
...
private:
std::wstring m_artistName;
std::wstring m_compositionName;
Windows::Globalization::Calendar m_releaseDateTime;
...
// Recording.cpp
// Implement like this:
...
Recording::Recording(hstring const& artistName, hstring const& compositionName, Windows::Globalization::Calendar const& releaseDateTime) :
m_artistName{ artistName.c_str() },
m_compositionName{ compositionName.c_str() },
m_releaseDateTime{ releaseDateTime } {}
hstring Recording::ArtistName(){ return hstring{ m_artistName }; }
hstring Recording::CompositionName(){ return hstring{ m_compositionName }; }
Windows::Globalization::Calendar Recording::ReleaseDateTime(){ return m_releaseDateTime; }
hstring Recording::OneLineSummary()
{
std::wstringstream wstringstream;
wstringstream << m_compositionName.c_str();
wstringstream << L" by " << m_artistName.c_str();
wstringstream << L", released: " << m_releaseDateTime.MonthAsNumericString().c_str();
wstringstream << L"/" << m_releaseDateTime.DayAsString().c_str();
wstringstream << L"/" << m_releaseDateTime.YearAsString().c_str();
return hstring{ wstringstream.str().c_str() };
}
...
// RecordingViewModel.h
// Add this field:
...
#include "Recording.h"
...
private:
Quickstart::Recording m_defaultRecording{ nullptr };
...
// RecordingViewModel.cpp
// Implement like this:
...
Quickstart::Recording RecordingViewModel::DefaultRecording()
{
Windows::Globalization::Calendar releaseDateTime;
releaseDateTime.Year(1761);
releaseDateTime.Month(1);
releaseDateTime.Day(1);
m_defaultRecording = winrt::make<Recording>(L"Wolfgang Amadeus Mozart", L"Andante in C for Piano", releaseDateTime);
return m_defaultRecording;
}
...
// Recording.h
#include <sstream>
namespace Quickstart
{
public ref class Recording sealed
{
private:
Platform::String^ artistName;
Platform::String^ compositionName;
Windows::Globalization::Calendar^ releaseDateTime;
public:
Recording(Platform::String^ artistName, Platform::String^ compositionName,
Windows::Globalization::Calendar^ releaseDateTime) :
artistName{ artistName },
compositionName{ compositionName },
releaseDateTime{ releaseDateTime } {}
property Platform::String^ ArtistName
{
Platform::String^ get() { return this->artistName; }
}
property Platform::String^ CompositionName
{
Platform::String^ get() { return this->compositionName; }
}
property Windows::Globalization::Calendar^ ReleaseDateTime
{
Windows::Globalization::Calendar^ get() { return this->releaseDateTime; }
}
property Platform::String^ OneLineSummary
{
Platform::String^ get()
{
std::wstringstream wstringstream;
wstringstream << this->CompositionName->Data();
wstringstream << L" by " << this->ArtistName->Data();
wstringstream << L", released: " << this->ReleaseDateTime->MonthAsNumericString()->Data();
wstringstream << L"/" << this->ReleaseDateTime->DayAsString()->Data();
wstringstream << L"/" << this->ReleaseDateTime->YearAsString()->Data();
return ref new Platform::String(wstringstream.str().c_str());
}
}
};
public ref class RecordingViewModel sealed
{
private:
Recording ^ defaultRecording;
public:
RecordingViewModel()
{
Windows::Globalization::Calendar^ releaseDateTime = ref new Windows::Globalization::Calendar();
releaseDateTime->Year = 1761;
releaseDateTime->Month = 1;
releaseDateTime->Day = 1;
this->defaultRecording = ref new Recording{ L"Wolfgang Amadeus Mozart", L"Andante in C for Piano", releaseDateTime };
}
property Recording^ DefaultRecording
{
Recording^ get() { return this->defaultRecording; };
}
};
}
// Recording.cpp
#include "pch.h"
#include "Recording.h"
Затем откройте доступ к классу источника привязки из класса, представляющего вашу страницу разметки. Мы делаем это путем добавления свойства типа RecordingViewModel в MainPage.
Если вы используете C++/WinRT, сначала обновите MainPage.idl
. Соберите проект, чтобы повторно сгенерировать MainPage.h
и .cpp
, а также объединить изменения в этих сгенерированных файлах с файлами вашего проекта.
namespace Quickstart
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.ViewModel = new RecordingViewModel();
}
public RecordingViewModel ViewModel{ get; set; }
}
}
// MainPage.idl
// Add this property:
import "RecordingViewModel.idl";
...
RecordingViewModel ViewModel{ get; };
...
// MainPage.h
// Add this property and this field:
...
#include "RecordingViewModel.h"
...
Quickstart::RecordingViewModel ViewModel();
private:
Quickstart::RecordingViewModel m_viewModel{ nullptr };
...
// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
InitializeComponent();
m_viewModel = winrt::make<RecordingViewModel>();
}
Quickstart::RecordingViewModel MainPage::ViewModel()
{
return m_viewModel;
}
...
// MainPage.h
...
#include "Recording.h"
namespace Quickstart
{
public ref class MainPage sealed
{
private:
RecordingViewModel ^ viewModel;
public:
MainPage();
property RecordingViewModel^ ViewModel
{
RecordingViewModel^ get() { return this->viewModel; };
}
};
}
// MainPage.cpp
...
MainPage::MainPage()
{
InitializeComponent();
this->viewModel = ref new RecordingViewModel();
}
Последний фрагмент заключается в привязке TextBlock
<Page x:Class="Quickstart.MainPage" ... >
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Page>
Если вы используете C++/WinRT, необходимо удалить функцию MainPage::ClickHandler для сборки проекта.
Вот результат.
Привязка к коллекции элементов
Распространенный сценарий — привязка к коллекции бизнес-объектов. В C# и Visual Basic универсальный класс ObservableCollection<T> является хорошим выбором для привязки данных, так как он реализует интерфейсы INotifyPropertyChanged и INotifyCollectionChanged. Эти интерфейсы предоставляют уведомление об изменении привязок при добавлении или удалении элементов или свойстве самого списка. Если вы хотите, чтобы связанные элементы управления обновлялись с изменениями свойств объектов в коллекции, бизнес-объект также должен реализовать INotifyPropertyChanged. Дополнительные сведения см. в разделе Подробное изучение привязки данных.
Если вы используете C++/WinRT, вы можете узнать больше о привязке к наблюдаемой коллекции в элементах управления XAML, связывая с коллекцией C++/WinRT. Если сначала прочитать этот раздел, намерение кода C++/WinRT, показанное ниже, будет более понятным.
Следующий пример привязывает ListView к коллекции объектов Recording
. Начнем с добавления коллекции в модель представления. Просто добавьте эти новые члены в класс RecordingViewModel.
public class RecordingViewModel
{
...
private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
public ObservableCollection<Recording> Recordings{ get{ return this.recordings; } }
public RecordingViewModel()
{
this.recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
this.recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
this.recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
}
}
// RecordingViewModel.idl
// Add this property:
...
#include <winrt/Windows.Foundation.Collections.h>
...
Windows.Foundation.Collections.IVector<IInspectable> Recordings{ get; };
...
// RecordingViewModel.h
// Change the constructor declaration, and add this property and this field:
...
RecordingViewModel();
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> Recordings();
private:
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> m_recordings;
...
// RecordingViewModel.cpp
// Update/add implementations like this:
...
RecordingViewModel::RecordingViewModel()
{
std::vector<Windows::Foundation::IInspectable> recordings;
Windows::Globalization::Calendar releaseDateTime;
releaseDateTime.Month(7); releaseDateTime.Day(8); releaseDateTime.Year(1748);
recordings.push_back(winrt::make<Recording>(L"Johann Sebastian Bach", L"Mass in B minor", releaseDateTime));
releaseDateTime = Windows::Globalization::Calendar{};
releaseDateTime.Month(11); releaseDateTime.Day(2); releaseDateTime.Year(1805);
recordings.push_back(winrt::make<Recording>(L"Ludwig van Beethoven", L"Third Symphony", releaseDateTime));
releaseDateTime = Windows::Globalization::Calendar{};
releaseDateTime.Month(3); releaseDateTime.Day(12); releaseDateTime.Year(1737);
recordings.push_back(winrt::make<Recording>(L"George Frideric Handel", L"Serse", releaseDateTime));
m_recordings = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>(std::move(recordings));
}
Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> RecordingViewModel::Recordings() { return m_recordings; }
...
// Recording.h
...
public ref class RecordingViewModel sealed
{
private:
...
Windows::Foundation::Collections::IVector<Recording^>^ recordings;
public:
RecordingViewModel()
{
...
releaseDateTime = ref new Windows::Globalization::Calendar();
releaseDateTime->Year = 1748;
releaseDateTime->Month = 7;
releaseDateTime->Day = 8;
Recording^ recording = ref new Recording{ L"Johann Sebastian Bach", L"Mass in B minor", releaseDateTime };
this->Recordings->Append(recording);
releaseDateTime = ref new Windows::Globalization::Calendar();
releaseDateTime->Year = 1805;
releaseDateTime->Month = 2;
releaseDateTime->Day = 11;
recording = ref new Recording{ L"Ludwig van Beethoven", L"Third Symphony", releaseDateTime };
this->Recordings->Append(recording);
releaseDateTime = ref new Windows::Globalization::Calendar();
releaseDateTime->Year = 1737;
releaseDateTime->Month = 12;
releaseDateTime->Day = 3;
recording = ref new Recording{ L"George Frideric Handel", L"Serse", releaseDateTime };
this->Recordings->Append(recording);
}
...
property Windows::Foundation::Collections::IVector<Recording^>^ Recordings
{
Windows::Foundation::Collections::IVector<Recording^>^ get()
{
if (this->recordings == nullptr)
{
this->recordings = ref new Platform::Collections::Vector<Recording^>();
}
return this->recordings;
};
}
};
А затем привязать ListView к свойству ViewModel.Recordings.
<Page x:Class="Quickstart.MainPage" ... >
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
Мы еще не предоставили шаблон данных для записи класса
Чтобы устранить эту проблему, можно переопределить ToString, чтобы вернуть значение OneLineSummaryили предоставить шаблон данных. Опция использования шаблона данных является более обычным и более гибким решением. Вы указываете шаблон данных с помощью свойства ContentTemplate элемента управления содержимым или свойства ItemTemplate элемента управления элементами. Ниже приведены два способа разработки шаблона данных для записи вместе с иллюстрацией результата.
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<TextBlock Text="{x:Bind OneLineSummary}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind ArtistName}" FontWeight="Bold"/>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Дополнительные сведения о синтаксисе XAML см. в статье Создание пользовательского интерфейса с помощью XAML. Дополнительные сведения о макете элемента управления см. в статье Определение макетов с помощью XAML.
Добавление детализированного вида
Вы можете отобразить все сведения о объектах записи
Это можно сделать двумя способами. Вы можете привязать детальное представление к свойству SelectedItem ListView. Кроме того, можно использовать CollectionViewSource. В этом случае вы привязываете ListView и представление сведений к CollectionViewSource (при этом выполняется забота о выбранном в данный момент элементе). Оба метода показаны ниже, и оба они дают одинаковые результаты (показанные на рисунке).
Замечание
До сих пор в этом разделе мы использовали только расширение разметки {x:Bind} , но оба метода, которые мы рассмотрим ниже, требуют более гибкого (но менее производительного) расширения разметки {Binding} .
Если вы используете расширения компонентов C++/WinRT или Visual C++ (C++/CX), то для использования расширения разметки {Binding} необходимо добавить атрибут BindableAttribute в любой класс среды выполнения, к которому требуется привязаться. Чтобы использовать {x:Bind}, этот атрибут не нужен.
Это важно
Если вы используете C++/WinRT, то атрибут bindableAttribute доступен, если установлен пакет SDK для Windows версии 10.0.17763.0 (Windows 10, версия 1809) или более поздней версии. Без этого атрибута необходимо реализовать интерфейсы ICustomPropertyProvider и ICustomProperty, чтобы иметь возможность использовать расширение разметки {Binding}.
Во-первых, вот метод SelectedItem.
// No code changes necessary for C#.
// Recording.idl
// Add this attribute:
...
[Windows.UI.Xaml.Data.Bindable]
runtimeclass Recording
...
[Windows::UI::Xaml::Data::Bindable]
public ref class Recording sealed
{
...
};
Единственное другое необходимое изменение — разметка.
<Page x:Class="Quickstart.MainPage" ... >
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView x:Name="recordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel DataContext="{Binding SelectedItem, ElementName=recordingsListView}"
Margin="0,24,0,0">
<TextBlock Text="{Binding ArtistName}"/>
<TextBlock Text="{Binding CompositionName}"/>
<TextBlock Text="{Binding ReleaseDateTime}"/>
</StackPanel>
</StackPanel>
</Grid>
</Page>
Для метода CollectionViewSource сначала добавьте CollectionViewSource как ресурс страницы.
<Page.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Page.Resources>
А затем настройте привязки в ListView (которым теперь не нужно давать имя) и в представлении деталей для использования CollectionViewSource. Обратите внимание, что, привязывая представление сведений непосредственно к CollectionViewSource, это подразумевает привязку к текущему элементу коллекции в случаях, где путь не может быть найден в самой коллекции. Нет необходимости указывать свойство CurrentItem в качестве пути для привязки, хотя это можно сделать, если есть какая-либо неоднозначность).
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
И вот одинаковый результат в каждом случае.
Замечание
Если вы используете C++, пользовательский интерфейс не будет выглядеть точно так же, как на рисунке ниже: отрисовка свойства ReleaseDateTime отличается. Дополнительные сведения см. в следующем разделе.
Форматирование или преобразование значений данных для отображения
Возникла проблема с отрисовкой выше. Свойство ReleaseDateTime не является просто датой, это DateTime (если вы используете C++, то это Календарь). Таким образом, в C#он отображается с большей точностью, чем нам нужно. И в C++ он отображается как имя типа. Одним из решений является добавление строкового свойства в класс записи, возвращающий эквивалент this.ReleaseDateTime.ToString("d")
. Именование этого свойства ReleaseDate указывает, что возвращает дату, а не дату и время. Именование ReleaseDateAsString будет указывать на то, что она возвращает строку.
Более гибкое решение — использовать что-то известное как преобразователь значений. Ниже приведен пример создания собственного преобразователя значений. Если вы используете C#, добавьте приведенный ниже код в файл исходного кода Recording.cs
. Если вы используете C++/WinRT, добавьте новый элемент Midl File (IDL) в проект, названный как показано в приведенном ниже примере кода C++/WinRT, создайте проект для создания StringFormatter.h
и .cpp
, добавьте эти файлы в проект и вставьте в них списки кода. Кроме того, добавьте #include "StringFormatter.h"
в MainPage.h
.
public class StringFormatter : Windows.UI.Xaml.Data.IValueConverter
{
// This converts the value object to the string to display.
// This will work with most simple types.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(formatString, value);
}
// If the format string is null or empty, simply
// call ToString() on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
// pch.h
...
#include <winrt/Windows.Globalization.h>
// StringFormatter.idl
namespace Quickstart
{
runtimeclass StringFormatter : [default] Windows.UI.Xaml.Data.IValueConverter
{
StringFormatter();
}
}
// StringFormatter.h
#pragma once
#include "StringFormatter.g.h"
#include <sstream>
namespace winrt::Quickstart::implementation
{
struct StringFormatter : StringFormatterT<StringFormatter>
{
StringFormatter() = default;
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
};
}
namespace winrt::Quickstart::factory_implementation
{
struct StringFormatter : StringFormatterT<StringFormatter, implementation::StringFormatter>
{
};
}
// StringFormatter.cpp
#include "pch.h"
#include "StringFormatter.h"
#include "StringFormatter.g.cpp"
namespace winrt::Quickstart::implementation
{
Windows::Foundation::IInspectable StringFormatter::Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& /* targetType */, Windows::Foundation::IInspectable const& /* parameter */, hstring const& /* language */)
{
// Retrieve the value as a Calendar.
Windows::Globalization::Calendar valueAsCalendar{ value.as<Windows::Globalization::Calendar>() };
std::wstringstream wstringstream;
wstringstream << L"Released: ";
wstringstream << valueAsCalendar.MonthAsNumericString().c_str();
wstringstream << L"/" << valueAsCalendar.DayAsString().c_str();
wstringstream << L"/" << valueAsCalendar.YearAsString().c_str();
return winrt::box_value(hstring{ wstringstream.str().c_str() });
}
Windows::Foundation::IInspectable StringFormatter::ConvertBack(Windows::Foundation::IInspectable const& /* value */, Windows::UI::Xaml::Interop::TypeName const& /* targetType */, Windows::Foundation::IInspectable const& /* parameter */, hstring const& /* language */)
{
throw hresult_not_implemented();
}
}
...
public ref class StringFormatter sealed : Windows::UI::Xaml::Data::IValueConverter
{
public:
virtual Platform::Object^ Convert(Platform::Object^ value, TypeName targetType, Platform::Object^ parameter, Platform::String^ language)
{
// Retrieve the value as a Calendar.
Windows::Globalization::Calendar^ valueAsCalendar = dynamic_cast<Windows::Globalization::Calendar^>(value);
std::wstringstream wstringstream;
wstringstream << L"Released: ";
wstringstream << valueAsCalendar->MonthAsNumericString()->Data();
wstringstream << L"/" << valueAsCalendar->DayAsString()->Data();
wstringstream << L"/" << valueAsCalendar->YearAsString()->Data();
return ref new Platform::String(wstringstream.str().c_str());
}
// No need to implement converting back on a one-way binding
virtual Platform::Object^ ConvertBack(Platform::Object^ value, TypeName targetType, Platform::Object^ parameter, Platform::String^ language)
{
throw ref new Platform::NotImplementedException();
}
};
...
Замечание
Для листинга кода C++/WinRT, приведенного выше, в StringFormatter.idl
мы используем атрибут для объявленияIValueConverter в качестве интерфейса по умолчанию. В списке StringFormatter имеет только конструктор, а для него нет методов, поэтому для него не создается интерфейс по умолчанию. Атрибут default
является оптимальным, если вы не собираетесь добавлять элементы экземпляра в StringFormatter, потому что для вызова методов IValueConverter не потребуется QueryInterface. Кроме того, можно вызвать генерацию интерфейса IStringFormatter по умолчанию, аннотируя сам класс среды выполнения с помощью атрибута default_interface. Этот вариант является оптимальным, если добавить элементы экземпляра в StringFormatter, которые вызываются чаще, чем методы IValueConverter, потому что тогда для вызова элементов экземпляра не потребуется QueryInterface.
Теперь можно добавить экземпляр StringFormatter в качестве ресурса страницы и использовать его в привязке TextBlock, отображающей свойство ReleaseDateTime.
<Page.Resources>
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Page.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
Как видно выше, для гибкости форматирования мы используем разметку для передачи строки формата в преобразователь с помощью параметра преобразователя. В примерах кода, показанных в этом разделе, только преобразователь значений C# использует этот параметр. Но можно легко передать строку формата C++в качестве параметра конвертера и использовать ее в преобразователе значений с функцией форматирования, например wprintf или swprintf.
Вот результат.
Замечание
Начиная с Windows 10, версии 1607, XAML предоставляет встроенный логический преобразователь в Видимость. Преобразователь сопоставляет значение перечисления