UWP XAML — заполнить ListBox с устройствами, показывая их имена, используя C ++ / WinRT?

У меня есть класс DeviceSelector, который отображает список устройств для выбора. Я хотел бы сделать это программно, без использования файлов XAML.
Поскольку мне трудно правильно использовать элемент управления ListBox из C ++, у меня есть следующие вопросы:

  • Как я могу использовать DisplayMemberPath свойство правильно отображать Name свойство в ListBox? Необходимо указать путь к свойству, но по какой-то причине в моей программе это не работает.
  • Можно ли использоватьItemsSource свойство для заполнения ListBox с помощью коллекции? Из документов не ясно, что передавать в качестве параметра, и таких примеров, не относящихся к XAML C ++, не так много.

Ниже у меня есть упрощенный класс DeviceSelector, и я предоставил простое приложение для устранения неполадок.

Изменить 1:

DisplayMemberPath не работает так, как я ожидаю, не относится к C ++ / WinRT. Я попытался реализовать это, используя XAML и код позади, используя:

<ListBox x:Name="DeviceSelector" DisplayMemberPath="Name">
...
</ListBox>

После заполнения ListBox устройствами, он также не отображает имена.


DeviceSelector.h

#pragma once

#include <winrt\Windows.Foundation.h>
#include <winrt\Windows.UI.Xaml.Controls.h>struct DeviceSelector : winrt::Windows::UI::Xaml::Controls::ListBox
{
DeviceSelector();
winrt::Windows::Foundation::IAsyncAction ShowAllAsync();
};

DeviceSelector.cpp

#include "pch.h"#include "DeviceSelector.h"
#include <winrt\Windows.Devices.Enumeration.h>
#include <winrt\Windows.Foundation.h>
#include <winrt\Windows.UI.Xaml.Controls.h>using namespace winrt::Windows::Devices::Enumeration;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml::Controls;DeviceSelector::DeviceSelector()
{
//DOES NOT WORK:
//DisplayMemberPath(L"Name");
}IAsyncAction DeviceSelector::ShowAllAsync()
{
DeviceInformationCollection devices = co_await DeviceInformation::FindAllAsync();

//DOES NOT WORK:
//ItemsSource(devices);

//DOES WORK:
//But does not display device names, without the right DisplayMemberPath.
for (DeviceInformation device : devices)
{
Items().Append(device);
}
}

main.cpp

#include "pch.h"
#include <winrt\Windows.ApplicationModel.Activation.h>
#include <winrt\Windows.UI.Xaml.h>

#include "DeviceSelector.h"

using namespace winrt;
using namespace winrt::Windows::ApplicationModel::Activation;
using namespace winrt::Windows::UI::Xaml;struct App : ApplicationT<App>
{
DeviceSelector selector;

void OnLaunched(LaunchActivatedEventArgs const &)
{
//Create window with a DeviceSelector instance.
Window window = Window::Current();
window.Content(selector);
window.Activate();

//Populate selector with devices.
selector.ShowAllAsync();
}

static void Initialize(ApplicationInitializationCallbackParams const &)
{
make<App>();
}

static void Start()
{
Application::Start(App::Initialize);
}
};

int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
App::Start();
}

pch.h

#pragma once

#pragma comment(lib, "windowsapp")

#include <winrt\Windows.ApplicationModel.Activation.h>
#include <winrt\Windows.Devices.Enumeration.h>
#include <winrt\Windows.Foundation.h>
#include <winrt\Windows.Media.Devices.h>
#include <winrt\Windows.UI.Xaml.h>
#include <winrt\Windows.UI.Xaml.Controls.h>

0

Решение

Для подключения Bindings, как вы описываете, требуется либо:

  1. Поддержка компилятора Xaml (которая скоро появится, но все еще в предварительном просмотре)

или же

  1. Вам нужно указать на объект, реализующий ICustomPropertyProvider а также ICustomProperty (а также INotifyPropertyChanged если ты хочешь ListBox элементы для обновления при изменении значения данных).

Это потому что DataMemberPath полагается на отражение во время выполнения (во время выполнения он запрашивает свойство с заданным именем — более подробная информация Вот). Обычный старый класс WinRT не предоставляет такой функциональности, поэтому вам придется обернуть его во что-то, что может.

Если вы решили пойти по маршруту ICustomPropertyProvider, то вот пример сгруппированной реализации, которая подключает только Name имущество. Это было просто быстрое подтверждение концепции; Есть гораздо лучшие и более расширяемые способы сделать это:

#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.UI.Xaml.Interop.h>

using namespace winrt;

using namespace Windows::ApplicationModel::Activation;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Interop;

struct DeviceInfoCustomProperty : implements<DeviceInfoCustomProperty, ICustomProperty>
{
DeviceInfoCustomProperty(bool canRead, bool canWrite, hstring name, TypeName type)
: m_CanRead(canRead)
, m_CanWrite(canWrite)
, m_Name(std::move(name))
, m_Type(std::move(type))
{
}

bool CanRead() const noexcept
{
return m_CanRead;
}

bool CanWrite() const noexcept
{
return m_CanWrite;
}

hstring Name() const noexcept
{
return m_Name;
}

TypeName Type() const noexcept
{
return m_Type;
}

IInspectable GetIndexedValue(const IInspectable&, const IInspectable&) const noexcept { return nullptr; }
IInspectable GetValue(const IInspectable& target) const;
void SetIndexedValue(const IInspectable&, const IInspectable&, const IInspectable&) const noexcept {}
void SetValue(const IInspectable&, const IInspectable&) const noexcept {}

IInspectable m_Object;
bool m_CanRead;
bool m_CanWrite;
hstring m_Name;
TypeName m_Type;
};

struct DeviceInfoWrapper : implements<DeviceInfoWrapper, ICustomPropertyProvider>
{
explicit DeviceInfoWrapper(DeviceInformation info)
: m_info(std::move(info))
{
}

TypeName Type() const noexcept
{
return xaml_typename<DeviceInformation>();
}

ICustomProperty GetCustomProperty(const hstring& name)
{
if (name == L"Name")
{
return make<DeviceInfoCustomProperty>(true, false, name, xaml_typename<hstring>());
}
return nullptr;
}

ICustomProperty GetIndexedProperty(const hstring&, const TypeName&)
{
return nullptr;
}

hstring GetStringRepresentation()
{
return L"DeviceWrapper";
}

DeviceInformation m_info;
};

IInspectable DeviceInfoCustomProperty::GetValue(const IInspectable& target) const
{
// Temporary workaround if preview SDK <= 17095
auto wrapper = from_abi<DeviceInfoWrapper>(target.as<ICustomPropertyProvider>());
// else
auto wrapper = target.as<DeviceInfoWrapper>();

if (m_Name == L"Name")
{
return box_value(wrapper->m_info.Name());
}
return nullptr;
}

struct DeviceSelector : winrt::Windows::UI::Xaml::Controls::ListBoxT<DeviceSelector>
{
DeviceSelector()
{
DisplayMemberPath(L"Name");
SelectionChanged([](const IInspectable&, const SelectionChangedEventArgs& args)
{
for (const auto& item : args.AddedItems())
{
// DEBUG - verifying that this is, in fact, the object
auto wrapper = item.as<DeviceInfoWrapper>();
wrapper->m_info.Name().c_str();
}
});
}

fire_and_forget ShowAllAsync()
{
DeviceInformationCollection devices = co_await DeviceInformation::FindAllAsync();
for (const auto& device : devices)
{
Items().Append(make<DeviceInfoWrapper>(device));
}
}
};

struct App : ApplicationT<App>
{
DeviceSelector selector;

void OnLaunched(LaunchActivatedEventArgs const &)
{

Window window = Window::Current();
window.Content(selector.try_as<UIElement>());
window.Activate();

selector.ShowAllAsync();
}
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
Application::Start([](auto &&) { make<App>(); });
}
1

Другие решения

Других решений пока нет …