使用 C++/WinRT 使用设备填充 ListBox,显示它们的名称?
Populate ListBox with devices, showing their names, using C++/WinRT?
我有一个 DeviceSelector class,它显示 select 的设备列表。我想以编程方式执行此操作,而不使用 XAML 文件。
由于我发现很难从 C++ 中正确使用 ListBox 控件,所以我有以下问题:
- 如何正确使用
DisplayMemberPath
属性在ListBox中显示Name
属性?应该传入 属性 的路径,但由于某种原因,这在我的程序中似乎不起作用。
- 是否可以使用
ItemsSource
属性 通过集合填充列表框?从文档中不清楚将什么作为参数传递,并且没有那么多非XAML C++ 示例。
下面是我的简化 DeviceSelector class,我还提供了一个简单的应用程序用于故障排除。
编辑 1:
DisplayMemberPath 没有像我预期的那样工作,不是特定于 C++/WinRT。我尝试使用 XAML 和后面的代码来实现它,使用:
<ListBox x:Name="DeviceSelector" DisplayMemberPath="Name">
...
</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>
按照您描述的方式连接绑定需要:
- Xaml 编译器支持(即将推出,但仍处于预览阶段)
或
- 您需要指向实现
ICustomPropertyProvider
和 ICustomProperty
的对象(以及 INotifyPropertyChanged
,如果您希望 ListBox
项目在数据值更改时更新)。
这是因为 DataMemberPath
依赖于运行时反射(它在运行时查询具有给定名称的 属性 - 更多详细信息 here)。普通的旧 WinRT class 不提供该功能,因此您必须将其包装在可以提供的东西中。
如果您决定采用 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>(); });
}
我有一个 DeviceSelector class,它显示 select 的设备列表。我想以编程方式执行此操作,而不使用 XAML 文件。 由于我发现很难从 C++ 中正确使用 ListBox 控件,所以我有以下问题:
- 如何正确使用
DisplayMemberPath
属性在ListBox中显示Name
属性?应该传入 属性 的路径,但由于某种原因,这在我的程序中似乎不起作用。 - 是否可以使用
ItemsSource
属性 通过集合填充列表框?从文档中不清楚将什么作为参数传递,并且没有那么多非XAML C++ 示例。
下面是我的简化 DeviceSelector class,我还提供了一个简单的应用程序用于故障排除。
编辑 1:
DisplayMemberPath 没有像我预期的那样工作,不是特定于 C++/WinRT。我尝试使用 XAML 和后面的代码来实现它,使用:
<ListBox x:Name="DeviceSelector" DisplayMemberPath="Name">
...
</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>
按照您描述的方式连接绑定需要:
- Xaml 编译器支持(即将推出,但仍处于预览阶段)
或
- 您需要指向实现
ICustomPropertyProvider
和ICustomProperty
的对象(以及INotifyPropertyChanged
,如果您希望ListBox
项目在数据值更改时更新)。
这是因为 DataMemberPath
依赖于运行时反射(它在运行时查询具有给定名称的 属性 - 更多详细信息 here)。普通的旧 WinRT class 不提供该功能,因此您必须将其包装在可以提供的东西中。
如果您决定采用 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>(); });
}