Megosztás a következőn keresztül:


Adatkötés áttekintése

Ez a témakör bemutatja, hogyan köthet egy vezérlőt (vagy más felhasználói felületi elemet) egyetlen elemhez, vagy hogyan köthet egy elemvezérlőt egy univerzális Windows-platform (UWP) alkalmazás elemeinek gyűjteményéhez. Emellett azt is bemutatjuk, hogyan szabályozható az elemek renderelése, hogyan valósítható meg a részletek nézete egy kijelölés alapján, és hogyan konvertálhatók adatok megjelenítésre. További részleges információért lásd: az Adatkötés mélyreható áttekintése.

Előfeltételek

Ez a témakör feltételezi, hogy tudja, hogyan hozhat létre alapszintű UWP-alkalmazást. Az első UWP-alkalmazás létrehozásával kapcsolatos utasításokért lásd: A Windows-alkalmazások használatának első lépései.

A projekt létrehozása

Hozzon létre egy új Üres alkalmazás (Windows Universal) projektet. Nevezze el "Gyorsindításnak".

Kötés egyetlen elemhez

Minden kötés egy kötési célból és egy kötési forrásból áll. A cél általában egy vezérlő vagy más felhasználói felületi elem tulajdonsága, a forrás pedig egy osztálypéldány (adatmodell vagy nézetmodell) tulajdonsága. Ez a példa bemutatja, hogyan lehet egy vezérlőt egyetlen elemhez kötni. A cél a Text tulajdonsága egy TextBlock-nak. A forrás egy Recording nevű egyszerű osztály egy példánya, amely hangfelvételt jelöl. Először nézzük meg az osztályt.

C# vagy C++/CX használata esetén adjon hozzá egy új osztályt a projekthez, és nevezze el az osztályt Felvétel.

Ha éppen a C++/WinRT-t használja, akkor adjon hozzá új Midl-fájl (.idl) elemeket a projekthez, a lenti C++/WinRT kódpélda felsorolásban látható módon elnevezve. Cserélje le az új fájlok tartalmát a listában látható MIDL 3.0 kódra, készítse el a projektet a Recording.h, .cpp, RecordingViewModel.h és .cpplétrehozásához, majd adjon hozzá kódot a létrehozott fájlokhoz a listaelemnek megfelelően. A létrehozott fájlokról és azok projektbe való másolásáról további információt az alábbi hivatkozásokban talál: XAML-vezérlők; kötés C++/WinRT tulajdonsághoz.

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"

Ezután tegye közzé a kötési forrásosztályt a korrektúraoldalt képviselő osztályból. Ehhez úgy járunk el, hogy hozzáadunk egy RecordingViewModel típusú tulajdonságot a MainPage-hoz.

Ha C++/WinRThasznál, először frissítse MainPage.idl. Hozza létre a projektet a MainPage.h és a .cppújragenerálásához, és egyesítse a létrehozott fájlok módosításait a projektben lévőkkel.

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();
}

Az utolsó lépés egy TextBlock összekapcsolása a ViewModel.DefaultRecording.OneLineSummary tulajdonsággal.

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"/>
    </Grid>
</Page>

Ha C++/WinRThasznál, el kell távolítania a MainPage::ClickHandler függvényt a projekt létrehozásához.

Itt az eredmény.

egy szövegblokk kötése

Az elemek gyűjteményéhez való kötés

Gyakori forgatókönyv az üzleti objektumok gyűjteményéhez való kapcsolás. A C# és a Visual Basic esetében az általános ObservableCollection<T> osztály jó adatkötési választás, mivel implementálja az INotifyPropertyChanged és INotifyCollectionChanged interfészeket. Ezek az interfészek változásértesítést biztosítanak a kötésekhez, amikor elemeket adnak hozzá vagy távolítanak el, vagy maga a lista egy tulajdonsága megváltozik. Ha azt szeretné, hogy a kötött vezérlők frissüljenek a gyűjtemény objektumtulajdonságainak módosításával, az üzleti objektumnak INotifyPropertyChangedis implementálnia kell. További információért lásd: Adatkötés részletesen.

Ha C++/WinRThasznál, akkor az XAML-elemek vezérlőinek megfigyelhető gyűjteményhez való kötéséről tudhat meg többet; kötés egy C++/WinRT-gyűjteményhez. Ha először olvassa el ezt a témakört, akkor az alább látható C++/WinRT-kódlista szándéka egyértelműbb lesz.

Ez a következő példa egy ListView- köt egy Recording objektumgyűjteményhez. Kezdjük azzal, hogy hozzáadjuk a gyűjteményt a nézetmodellünkhöz. Egyszerűen vegye fel ezeket az új tagokat a RecordingViewModel osztályba.

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;
        };
    }
};

Ezután kössön egy ListView a ViewModel.Recordings tulajdonsághoz.

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListView ItemsSource="{x:Bind ViewModel.Recordings}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Page>

Még nem adtunk meg adatsablont a Felvétel osztályhoz, ezért a felhasználói felületi keretrendszer a ListViewminden eleménél legfeljebb az ToString függvény hívásával tudja kezelni az elemeket. A ToString alapértelmezett implementációja a típusnév visszaadása.

Listanézet kötése 1

Ennek kijavítása érdekében felülbírálhatjuk ToString, hogy visszaadjuk OneLineSummaryértékét, vagy megadhatunk egy adatsablont. Az adatsablon lehetőség egy szokásosabb megoldás, és rugalmasabb megoldás. Adatsablont egy tartalomvezérlő ContentTemplate tulajdonságával vagy egy elemvezérlő ItemTemplate tulajdonságával adhat meg. Az alábbi két módon tervezhetünk adatsablont Felvétel az eredmény illusztrációjával együtt.

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

Egy lista nézetének kötése 2

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

Listanézet kötése 3

További információ az XAML szintaxisról: Felhasználói felület létrehozása XAML-. További információ a vezérlőelrendezésről: Elrendezések definiálása XAML-.

Részletek nézet hozzáadása

Kiválaszthatja, hogy megjelenítse az összes részletet a rögzítési objektumokról a ListView elemekben. De ez sok helyet foglal el. Ehelyett elég adatot jeleníthet meg az elemben az azonosításhoz, majd amikor a felhasználó kiválaszt egy elemet, a kijelölt elem összes részletét megjelenítheti egy külön felhasználói felületen, a részletek nézetben. Ezt az elrendezést master/details nézetnek vagy lista/részletek nézetnek is nevezik.

Kétféleképpen lehet ezt végighaladni. A részletek nézetet a ListViewSelectedItem tulajdonságához kötheti. Vagy használhat egy CollectionViewSource-t is, amely esetben a ListView-öt és a részletek nézetet is a CollectionViewSource-hez kötjük (ezzel gondoskodva az aktuálisan kiválasztott elemről). Mindkét technika alább látható, és mindkettő ugyanazt az eredményt adja (az ábrán látható).

Megjegyzés:

A jelen témakörben eddig csak a(z) {x:Bind} jelölőbővítménythasználtuk, de az alábbiakban ismertetett két módszerhez rugalmasabb (de kevésbé teljesítményigényes) {Binding} jelölőbővítményrevan szükség.

Ha C++/WinRT vagy Visual C++ összetevőbővítményeket (C++/CX) használ, akkor a ({Binding} jelölőbővítmény) használatához hozzá kell adnia a BindableAttribute attribútumot minden olyan futtatókörnyezeti osztályhoz, amelyhez csatlakozni szeretne. A {x:Bind}használatához nincs szükség az attribútumra.

Fontos

Ha a C++/WinRT-t használja, akkor a BindableAttribute attribútum elérhető, ha telepítve van a Windows SDK 10.0.17763.0-s (Windows 10, 1809-es verzió) vagy egy újabb verziója. Az attribútum nélkül implementálnia kell az ICustomPropertyProvider és ICustomProperty interfészeket, hogy használni tudja a {Binding} jelölőkiterjesztést.

Először is a KiválasztottElemek technikát.

// 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
{
    ...
};

A jelölés az egyetlen további szükséges módosítás.

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

A CollectionViewSource technikához először adjon hozzá egy CollectionViewSource lapi erőforrásként.

<Page.Resources>
    <CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Page.Resources>

Ezután módosítsa a kötéseket a ListView-on (amelyet már nem kell elnevezni), valamint a részletnézeten azzal a céllal, hogy a CollectionViewSource-t használja. Vegye észre, hogy ha a részletek nézet közvetlenül a CollectionViewSource-hoz kötődik, az arra utal, hogy az aktuális elemhez szeretne kötést létrehozni olyan esetekben, amikor az elérési út nem található meg magában a gyűjteményben. A kötés elérési útjaként nem kell megadnia a CurrentItem tulajdonságot, bár ezt kétértelműség esetén megteheti).

...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...

És minden esetben ugyanaz az eredmény.

Megjegyzés:

Ha C++-ot használ, akkor a felhasználói felület nem fog pontosan úgy kinézni, mint az alábbi ábrán: a ReleaseDateTime tulajdonság renderelése eltérő. Erről a következő szakaszban olvashat bővebben.

Listanézet kötése 4

Adatértékek formázása vagy konvertálása megjelenítésre

A fenti rendereléssel kapcsolatban probléma merült fel. A ReleaseDateTime tulajdonság nem csak dátum, hanem DateTime (ha C++-ot használ, akkor Naptár). A C#-ban tehát a szükségesnél nagyobb pontossággal jelenik meg. A C++-ban pedig típusnévként jelenik meg. Az egyik megoldás egy karakterlánc tulajdonság hozzáadása a Felvétel osztályhoz, amely a this.ReleaseDateTime.ToString("d")megfelelője. Egy ilyen tulajdonság ReleaseDate elnevezése arra utalna, hogy dátumot ad vissza, nem pedig dátumot és időt. Annak, hogy ReleaseDateAsString nevet adunk, az a további jelentése lenne, hogy sztringet ad vissza.

A rugalmasabb megoldás egy értékkonverternek nevezett megoldás használata. Íme egy példa arra, hogyan hozhat létre saját értékkonvertert. Ha C#-ot használ, adja hozzá az alábbi kódot a Recording.cs forráskódfájlhoz. Ha C++/WinRT-t használ, akkor adjon hozzá egy új Midl-fájl (.idl) elemet a projekthez, amely az alábbi C+++/WinRT-kódpéldában látható módon van elnevezve, hozza létre a projektet StringFormatter.h és .cpplétrehozásához, adja hozzá ezeket a fájlokat a projekthez, majd illessze be a kódelemeket. Adja hozzá a #include "StringFormatter.h"-t a MainPage.h-hez.

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();
    }
};
...

Megjegyzés:

A fenti C++/WinRT-kódlista esetében a StringFormatter.idlaz alapértelmezett attribútumot használjuk IValueConverter alapértelmezett interfészként való deklarálásához. A listában a StringFormatter csak konstruktorral rendelkezik, és nincsenek metódusai, ezért nem kerül alapértelmezett felület létrehozásra. A default attribútum optimális, ha nem ad hozzá példánytagokat a StringFormatter-hoz, mert a(z) IValueConverter metódusok meghívásához nincs szükség QueryInterface-re. Alternatívaként kérheti az alapértelmezett IStringFormatter felület létrehozását, és ezt úgy teheti meg, hogy a futtatókörnyezeti osztály maga van jelölve a default_interface attribútummal. Ez az opció akkor optimális, ha a StringFormatter példánytagokat ad hozzá, amelyeket gyakrabban hívnak meg, mint a IValueConverter metódusait, mert így nem lesz szükség a QueryInterface-re a példánytagok meghívásához.

Most hozzáadhatunk StringFormatter egy példányát laperőforrásként, és használhatjuk a TextBlock kötésében, amely megjeleníti a ReleaseDateTime tulajdonságot.

<Page.Resources>
    <local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Page.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
    Converter={StaticResource StringFormatterValueConverter},
    ConverterParameter=Released: \{0:d\}}"/>
...

Ahogy fent látható, a formázási rugalmasság érdekében jelölőnyelvet használunk, hogy egy formátum stringet adjunk át a konverternek a konverter paramétere segítségével. A jelen témakörben bemutatott kód példákban csak a C# értékkonverter használja ezt a paramétert. A C++-stílusú formázási sztringeket azonban egyszerűen átadhatja konverterparaméterként, és ezt az értékkonverterben használhatja egy formázási függvénysel, például wprintf vagy swprintf.

Itt az eredmény.

dátum megjelenítése egyéni formázással

Megjegyzés:

A Windows 10 1607-es verziójától kezdve az XAML-keretrendszer beépített logikai-láthatósági konvertert biztosít. A konverter a igaz értéket a Visibility.Visible enumerálási értékre térképezi, és a hamis értéket a Visibility.Collapsed értékre, így anélkül kötheti a láthatósági tulajdonságot egy logikai értékhez, hogy konvertert kellene létrehoznia. A beépített konverter használatához az alkalmazás minimális cél SDK-verziójának 14393-as vagy újabb verziónak kell lennie. Nem használható, ha az alkalmazás a Windows 10 korábbi verzióit célozza meg. További információ a célverziókról: Verzió-adaptív kód.

Lásd még