c++ - 使用 C++/WinRT 使用设备填充 ListBox,显示它们的名称?

标签 c++ uwp-xaml c++-winrt

我有一个 DeviceSelector 类,它显示要选择的设备列表。我想以编程方式执行此操作,而不使用 XAML 文件。 由于我发现很难从 C++ 中正确使用 ListBox 控件,因此我有以下问题:

  • 如何正确使用 DisplayMemberPath 属性在 ListBox 中显示 Name 属性?应该传入该属性的路径,但由于某种原因,这在我的程序中似乎不起作用。
  • 是否可以使用ItemsSource 属性通过集合填充列表框?从文档中不清楚将什么作为参数传递,并且没有那么多非 XAML C++ 示例。

下面是我的简化 DeviceSelector 类,我提供了一个简单的应用程序用于故障排除。

编辑 1:

DisplayMemberPath 没有像我预期的那样工作,不是特定于 C++/WinRT。我尝试使用 XAML 和代码隐藏来实现它,使用:

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

用设备填充列表框后,它也不显示名称。


设备选择器.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();
};

设备选择器.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>

最佳答案

按照您描述的方式连接绑定(bind)需要:

  1. Xaml 编译器支持(即将推出,但仍处于预览阶段)

  1. 你需要指向一个实现ICustomPropertyProvider的对象和 ICustomProperty(以及 INotifyPropertyChanged,如果您希望您的 ListBox 项在数据值更改时更新)。

这是因为 DataMemberPath 依赖于运行时反射(它在运行时查询具有给定名称的属性 - 更多详情 here )。普通的旧 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>(); });
}

关于c++ - 使用 C++/WinRT 使用设备填充 ListBox,显示它们的名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48676223/

相关文章:

c++ - 尝试在 Windows 上构建 mysql-connector-cpp

c++ - boolean 类型的递归

c# - UWP 窗口内容不随窗口调整大小

c++-winrt - 是否可以以编程方式创建 xaml 页面?

c++ - 错误 C2664 'HRESULT IUnknown::QueryInterface(const IID &,void **)' : cannot convert argument 1 from 'const winrt::guid' to 'const IID &'

c++ - 指针错误 - "using uninitialized value"即使我认为它已初始化?

c++ - 在对象中构造对象

c# - CreateQueryWithOptions 导致系统互操作 Com 异常

c# - Windows.UI.Xaml.dll 中的 UWP 应用程序在 Release模式下崩溃

windows-runtime - 如何使用midlrt.exe将.idl编译为.winmd?