我正在开发一个在 Windows 10 下用 C++/WinRT 编写的简单 UWP 应用程序,其中包含两个ListView
控件。此应用程序的目标是学习如何从一个ListView
控件中选择一个项目,将其拖动到另一个ListView
控件,然后放下该项目,以便将其从源ListView
控件复制到目标ListView
控件。
ListView
到目前为止,我发现的所有示例都使用 C#,其中一些使用 C++/CX 而不是 C++/WinRT 和本机 C++,但是我已经设法坚持到从源代码中选择项目的基本机制可以正常工作的地步拖放到目的地ListView
。但是,当尝试从 drop 事件中获取信息以更新目标ListView
时,我遇到了异常。
问题:我需要进行哪些更改才能将源ListView
控件中的选定文本拖放到目标ListView
控件上,然后将文本添加到目标ListView
控件中?
Visual Studio 2017 的输出窗口显示以下文本,我将其解释为错误地址异常:
Unhandled exception at 0x0259DC3C (Windows.UI.Xaml.dll) in TouchExperiment_01.exe: 0xC000027B: An application-internal exception has occurred (parameters: 0x05F5E3D8, 0x00000005).
Unhandled exception at 0x74ECE61D (combase.dll) in TouchExperiment_01.exe: 0xC0000602: A fail fast exception occurred. Exception handlers will not be invoked and the process will be terminated immediately.
Unhandled exception at 0x74F9D7D9 (combase.dll) in TouchExperiment_01.exe: Stack cookie instrumentation code detected a stack-based buffer overrun.
Unhandled exception at 0x74F9D7D9 (combase.dll) in TouchExperiment_01.exe: Stack cookie instrumentation code detected a stack-based buffer overrun.
当函数中的以下源代码行(void MainPage::OnListViewDrop()
MainPage.cpp 源文件中的最后一个函数)执行时引发异常:
auto x = e.DataView().GetTextAsync();
附加信息 A:使用调试器,我发现与异常相关的错误消息意味着方法提供的数据中存在错误OnListViewDragItemsStarting()
。异常错误消息的文本是:
{m_handle={m_value=0x05550330 L"DataPackage does not contain the specified format. Verify its presence using DataPackageView.Contains or DataPackageView.AvailableFormats." } }
我还在 Visual Studio 首次引发和捕获异常的站点上发现,停止应用程序base.h
(来自 C++/WinRT 模板),错误文本0x8004006a : Invalid clipboard format
表明我对数据格式缺乏一致意见拖动开始创建并且拖放尝试消耗。
源代码概述
我在 MainPage.xml、MainPage.cpp、MainPage.h 和 pch.h 区域中修改了标准 C++/WinRT 应用程序模板。我还为一个新类 DataSource 添加了类文件,它使用 astd::vector<>
来包含一些测试数据。此内存驻留数据在App
构造函数中使用一些虚拟数据进行初始化:
App::App()
{
InitializeComponent();
DataSource::InitializeDataBase();
Suspending({ this, &App::OnSuspending });
// … other code
首先,我必须在 pch.h 文件中添加一行以提供拖放模板:
#include "winrt/Windows.ApplicationModel.DataTransfer.h" // ADD_TO: need to add to allow use of drag and drop in MainPage.cpp
XAML 源文件包含两个ListView
控件的源以及一个TextBlock
显示源中所选项目的完整描述的控件ListView
:
<Page
x:Class="TouchExperiment_01.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TouchExperiment_01"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Width="1130" Margin="0,0,0,0">
<ListView x:Name="myList" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="300" SelectionChanged="OnSelectionChanged"
CanDragItems="True" DragItemsStarting="OnListViewDragItemsStarting" BorderBrush="AliceBlue" BorderThickness="3">
</ListView>
<TextBlock x:Name="myTextBlock" Height="200" Width="200" Text="this is temp text to replace." TextWrapping="WrapWholeWords" Margin="5"/>
<ListView x:Name="myList2" HorizontalAlignment="Right" Height="100" VerticalAlignment="Top" Width="300" SelectionChanged="OnSelectionChanged" AllowDrop="True"
DragOver="OnListViewDragOver" Drop="OnListViewDrop" BorderBrush="DarkGreen" BorderThickness="5">
</ListView>
</StackPanel>
</Page>
的类声明DataSource
很简单。类定义如下:
#pragma once
class DataSource
{
public:
DataSource();
~DataSource();
static int InitializeDataBase();
struct DataSourceType
{
std::wstring name;
std::wstring description;
};
static std::vector<DataSourceType> myDataBase;
}
;
和 的初始化vector
,这是App
在应用程序启动时的构造时完成的:
int DataSource::InitializeDataBase()
{
myDataBase.clear();
for (int i = 0; i < 50; i++) {
DataSourceType x;
wchar_t buffer[256] = { 0 };
swprintf_s(buffer, 255, L"Name for %d Item", i);
x.name = buffer;
swprintf_s(buffer, 255, L"Description %d. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.", i);
x.description = buffer;
myDataBase.push_back(x);
}
return 0;
}
XAML 页面背后的 MainPage.cpp 源代码是:
#include "pch.h"
#include "MainPage.h"
#include "DataSource.h"
using namespace winrt;
using namespace Windows::UI::Xaml;
namespace winrt::TouchExperiment_01::implementation
{
MainPage::MainPage()
{
InitializeComponent();
// load up the source ListView with the name field from out
// in memory database.
auto p = myList().Items();
for (auto a : DataSource::myDataBase) {
p.Append(box_value(a.name));
}
// add a single ListViewItem to the destination ListView so that we
// know where it is.
p = myList2().Items();
p.Append(box_value(L"list"));
}
int32_t MainPage::MyProperty()
{
throw hresult_not_implemented();
}
void MainPage::MyProperty(int32_t /* value */)
{
throw hresult_not_implemented();
}
void MainPage::OnSelectionChanged(Windows::Foundation::IInspectable const & target, Windows::UI::Xaml::RoutedEventArgs const & )
{
// the user has selected a different item in the source ListView so we want to display
// the associated description information for the selected ListViewItem.
winrt::Windows::UI::Xaml::Controls::ListView p = target.try_as<winrt::Windows::UI::Xaml::Controls::ListView>();
if (p) {
int iIndex = p.SelectedIndex();
myTextBlock().Text(DataSource::myDataBase[iIndex].description);
}
}
void MainPage::OnListViewDragItemsStarting(Windows::Foundation::IInspectable const & target, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs const & e)
{
// provide the data that we have in the ListView which the user has selected
// to drag to the other ListView. this is the data that will be copied from
// the source ListView to the destination ListView.
auto p = target.try_as<winrt::Windows::UI::Xaml::Controls::ListView>();
if (p) {
int iIndex = p.SelectedIndex();
e.Items().SetAt(0, box_value(iIndex));
}
}
void MainPage::OnListViewDragOver(Windows::Foundation::IInspectable const & target, Windows::UI::Xaml::DragEventArgs const & e)
{
// indicate that we are Copy of data from one ListView to another rather than one of the other
// operations such as Move. This provides the operation type informative user indicator when the
// user is doing the drag operation.
e.AcceptedOperation(Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
}
void MainPage::OnListViewDrop(Windows::Foundation::IInspectable const & target, Windows::UI::Xaml::DragEventArgs const & e)
{
// update the destination ListView with the data that was dragged from the
// source ListView.
auto p = target.try_as<winrt::Windows::UI::Xaml::Controls::ListView>();
auto x = e.DataView().GetTextAsync(); // ** this line triggers exception on drop.
}
}
在开始拖动之前在源中选择项目的应用程序的屏幕截图ListView
如下所示。源ListView
控件在左侧,目标ListView
控件在右侧。
附录:参考资料和文档
Microsoft Docs - Windows.ApplicationModel.DataTransfer 命名空间
Microsoft Docs - DragItemsStartingEventArgs 类,其中包含指向此示例项目的链接,该示例项目看起来正在使用 C++/CX 将示例拖放到 GitHub 上,其中包含Windows-universal-samples/Samples/XamlDragAndDrop/cpp/Scenario1_ListView.xaml.cpp,它有一个有用的例子。