用设备填充ListBox,用C ++ / WinRT显示它们的名字?

时间:2018-02-08 01:51:25

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

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

  • 如何正确使用DisplayMemberPath属性在ListBox中显示Name属性?应传入属性的路径,但由于某种原因,这似乎在我的程序中不起作用。
  • 是否可以使用ItemsSource属性使用Collection填充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>

1 个答案:

答案 0 :(得分:1)

以您描述的方式连接Bindings需要:

  1. Xaml编译器支持(即将推出,但仍在预览中)
    1. 如果您希望在数据值更改时更新ICustomProperty项,则需要指向实现ICustomPropertyProviderINotifyPropertyChanged(以及ListBox)的对象。
    2. 这是因为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>(); });
      }