如何在 ListView 中启用重新排序和拖放
[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]
了解如何向 ListView 控件添加重新排序和拖放项功能。 (仅限 Windows)
你需要了解的内容
技术
先决条件
你应当能够使用 JavaScript(使用 WinJS 控件)创建基本的 Windows 应用商店应用。有关如何开始使用 WinJS 控件的说明,请参阅快速入门:添加 WinJS 控件和样式。
添加其他功能之前,你应知道如何创建一个基本的 ListView 控件。有关如何创建简单 ListView 的简单概述,请参阅快速入门:添加 ListView 或查阅 ListView 控件参考资料。
说明
步骤 1: 设置示例
本示例演示如何创建一个 ListView 控件和一个 ItemContainer 来显示列表中某项的信息。
使用以下 HTML 标记作为 ListView 控件的基础。你可将该代码复制并粘贴到 Microsoft Visual Studio 2013 中一个 空白应用程序中的 default.html 文件中。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>List_View_demo</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.2.0/js/base.js"></script>
<script src="//Microsoft.WinJS.2.0/js/ui.js"></script>
<!-- List_View_demo references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
<script src="js/data.js"></script>
</head>
<body>
<div id="listViewTemplate">
<div id="listTemplate"
data-win-control="WinJS.Binding.Template">
<div class="listTemplate">
<div>
<h4 data-win-bind="innerText: title"></h4>
<h6 data-win-bind="innerText: text"></h6>
</div>
</div>
</div>
<div id="listView"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemDataSource : DataExample.itemList.dataSource,
itemTemplate: select('#listTemplate'),
itemsDraggable: true,
itemsReorderable: true,
layout: { type: WinJS.UI.GridLayout }
}">
</div>
</div>
<div id="listViewDetail" >
<h2>Details</h2><br/><br/>
<div id="listViewDetailInfo"
draggable="true"
data-win-control="WinJS.UI.ItemContainer">
<h4>Cherry chocolate swirl</h4>
<h6>Ice cream</h6>
<p>Description:
<span>A sumptious blending of cherry
and dark chocolate.</span>
</p>
</div>
</div>
</body>
</html>
该实例还使用 CSS 样式调整 HTML 页面上的 ListView 控件和 ItemContainer。将以下 CSS 代码添加到与 ListView 控件(位于空白应用程序模板的 css/default.css 中)关联的样式表中。
/* Layout the app page as a grid. */
body {
display: -ms-grid;
-ms-grid-columns: 600px 1fr;
-ms-grid-rows: 1fr;
}
/* Style the template for the ListView control.
.listTemplate {
width: 282px;
height: 70px;
padding: 5px;
overflow: hidden;
}
.listTemplate div {
margin: 5px;
}
/* Style the ListView control. */
#listView {
-ms-grid-column: 1;
-ms-grid-row: 1;
height: 500px;
width: 500px;
border: 2px solid gray;
}
#listView .win-container {
margin: 10px;
}
#listView .win-container:hover {
color: red;
}
/* Style the ItemContainer control.*/
#listViewDetail {
-ms-grid-column: 2;
-ms-grid-row: 1;
}
#listViewDetailInfo {
width: 300px;
}
该示例使用一些预定义的数据填充 ListView 控件。这些数据包含在 js 文件夹中一个名为‘data.js’的文件中 (js/data.js)。按照以下说明将 ListView 数据添加到应用中。
将一个 JavaScript 数据文件添加到应用中
在“解决方案资源管理器”中,右键单击“js”文件夹,选择“添加”>“新 JavaScript 文件”。
在“添加新项”对话框中,在“名称”****框中键入‘data.js’,然后单击“添加”。
在“解决方案资源管理器”****中,双击新 JavaScript 文件并添加以下代码。
(function () { "use strict"; // Define the dataset. var dataArray = [ { title: "Basic banana", text: "Low-fat frozen yogurt", description: "Go bananas for some frozen yogurt." }, { title: "Banana blast", text: "Ice cream", description: "More banana than allowed by law." }, { title: "Brilliant banana", text: "Frozen custard", description: "Custard with banana; an excellent desert." }, { title: "Orange surprise", text: "Sherbet", description: "Orange sherbert with a little extra something." }, { title: "Original orange", text: "Sherbet", description: "The orange sherbert you know and love." }, { title: "Vanilla", text: "Ice cream", description: "The one and only, classic vanilla ice cream." }, { title: "Very vanilla", text: "Frozen custard", description: "What's better than custard with vanilla flavoring?" }, { title: "Marvelous mint", text: "Gelato", description: "Mint meets gelato in this delicious desert." }, { title: "Succulent strawberry", text: "Sorbet", description: "A joyful confection of strawberries." } ]; // Load the dataset into a List object. var dataList = new WinJS.Binding.List(dataArray); // Expose the List object to the rest of the app. WinJS.Namespace.define("DataExample", { itemList: dataList }); })();
注意 可将提供的数据替换为你需要的任何数据。要替换数据源,可更改传递给 WinJS.Binding.List 构造函数方法的数据集。
如果决定使用某个不同于 IListDataSource 对象的数据源,你必须实现 moveBefore、moveAfter 和 moveToStart 方法。在实际中,建议使用 WinJS 所提供的 List 对象来包装数据源。
另请注意,应用的 HTML 标记中定义的 Template 对象接受的一个数据源包含具有 title
和 text
属性的项。如果你的数据未包含 title
或 text
属性,你需要修改 Template 对象的定义。
步骤 2: 向 ListView 控件添加重新排序功能
可非常轻松地向 ListView 控件添加重新排序功能。它仅需要在代码中进行非常少的更改或添加。基本而言,只需将 ListView 控件的 itemsReorderable 属性设置为‘true’。(默认设置为‘false’。)
你可在控件的 HTML 标记中声明性地完成此任务,也可在运行时使用 JavaScript 添加此功能。下面的示例演示了如何修改控件的 HTML 标记来添加重新排序功能。
<!-- The definition of the ListView control.
Note that the data-win-options attribute for the
control includes the itemsReorderable property. -->
<div id="listView"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemDataSource : DataExample.itemList.dataSource,
itemTemplate: select('#listTemplate'),
itemsReorderable : true,
layout: { type : WinJS.UI.GridLayout }
}">
</div>
下一个示例演示了如何在运行时使用 JavaScript 向 ListView 控件添加重新排序功能。
(function () {
// Other JavaScript code ...
// Get a reference to the ListView control.
var listView =
document.querySelector('#listView').winControl;
// Set the controls itemsReorderable property.
listView.itemsReorderable = true;
// Other JavaScript code ...
})();
更改 ListView 控件的 itemsReorderable 属性后,运行项目(按 F5)。在 ListView 中选择一项并将它拖到相同ListView的另一个位置。
步骤 3: 向 ListView 添加拖动功能
基本来讲,向 ListView 控件添加拖动功能就像添加重新排序功能一样轻松。ListView 控件包含一个 itemsDraggable 属性,可在控件的 HTML 中声明性地设置它或者在运行时更改它。
下一个示例演示了如何向控件的 HTML 标记中的一个 ListView 添加简单的拖动功能。
<!-- The definition of the ListView control.
Note that the data-win-options attribute for the
control includes the itemsDraggable property. -->
<div id="listView"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemDataSource : DataExample.itemList.dataSource,
itemTemplate: select('#listTemplate'),
itemsDraggable : true,
layout: { type : WinJS.UI.GridLayout }
}">
</div>
下一个示例演示了如何在运行时使用 JavaScript 向 ListView 控件添加简单的拖动功能。
(function () {
// Other JavaScript code ...
// Get a reference to the ListView control.
var listView =
document.querySelector('#listView').winControl;
// Set the controls itemsReorderable property.
listView.itemsDraggable = true;
// Other JavaScript code ...
})();
将 itemsDraggable 属性设置为 true 后,运行应用(按 F5)。在应用中,从 ListView 控件选择一项并将它拖到 ListView 外部。该项漂浮在 ListView 外部的应用表面。松开鼠标按钮时,该项就会消失。然后会触发相应的丢失事件。
如果希望与 ListView 控件的基础数据源交互,还需要为 ListView 的一些拖放事件实现处理程序。在下面的示例中,已将一个处理程序添加到 ItemContainer 的 ListView.itemdragstart 事件,以及 dragover 和 drop 事件。
要在前面的示例中使用此代码,可将此代码添加到 Blank app 模板 (js/default.js) 中提供的 default.js 文件中定义的 app.onactivated 事件处理程序内。
// Get the data from the ListView when the user drags an item.
listView.addEventListener("itemdragstart", function (evt) {
// Store the index of the item from the data source of
// the ListView in the DataTransfer object of the event.
evt.detail.dataTransfer.setData("Text",
JSON.stringify(evt.detail.dragInfo.getIndices()));
});
// Allows the drop to occur. The default behavior disallows
// an element from being dropped upon another.
listViewDetailInfo.addEventListener('dragover', function (evt) {
evt.preventDefault();
});
// Insert the content (from the ListView) into the ItemContainer.
listViewDetailInfo.addEventListener('drop', function (evt) {
// Get the index of the selected item out of the event object.
var dragIndex = JSON.parse(evt.dataTransfer.getData("Text")),
dataSource = listView.winControl.itemDataSource;
// Extract the selected data from the data source
// connected to the ListView control.
dataSource.itemFromIndex(Number(dragIndex)).
then(function (item) {
if (item) {
var itemData = item.data;
// Update the ItemContainer with the data from
// the item dragged from the ListView control.
listViewDetailInfo.querySelector('h4').innerText = itemData.title;
listViewDetailInfo.querySelector('h6').innerText = itemData.text;
istViewDetailInfo.querySelector('span').innerText = itemData.description;
}
});
});
在前面的示例中,所选的且从 ListView 拖来的数据已存储在与 itemdragstart 事件关联的 DataTransfer 对象中。因为两个处理程序都可访问同一个数据源,所以仅存储了选定项的索引。否则,你可在 DataTransfer 对象中以一个 JSON 格式的字符串形式序列化该对象。
在 ItemContainer 的 dragover 事件的处理程序中,默认行为已禁止(默认行为是禁止将一个元素拖到另一个元素上)。在 ItemContainer 对象的 drop 事件处理程序中,数据源选定项的索引从 DataTransfer 对象提取,然后从 ListView 的数据源检索该项。最后,使用新数据更新 ItemContainer 的 HTML。
注意 如果你的应用在一个分组的 ListView 控件中的不同组之间重新排序或拖放项,你需要从数据源删除这些项,然后重新将它们插入新组中。不能使用 moveAfter、moveBefore 或 moveToStart 完成此任务。
步骤 4: 向 ListView 添加放置功能
可向 ListView 控件添加放置功能,类似于前一个示例中向 ItemContainer 控件添加放置功能。
使用下面的代码示例添加将数据从 ItemContainer 拖到 ListView 的能力。
要在前面的示例中使用此代码,可将此代码添加到 Blank app 模板 (js/default.js) 中提供的 default.js 文件中定义的 app.onactivated 事件处理程序内。
// Drop content (from the ItemContainer) onto the ListView control.
listView.addEventListener("itemdragdrop", function (evt) {
if (evt.detail.dataTransfer) {
var dragData = JSON.parse(
evt.detail.dataTransfer.getData("Text"));
// It's a good idea to validate the data before
// attempting to insert it into the data source!
if (dragData && dragData.title && dragData.text) {
var dropIndex = evt.detail.insertAfterIndex;
// Insert the new item into the data source.
DataExample.itemList.splice(dropIndex, 0, {
title: dragData.title,
text: dragData.text,
description: dragData.description
});
}
}
});
// Allows the drop to occur. The default behavior disallows
// an element from being dropped upon another.
listView.addEventListener("itemdragenter", function (evt) {
if (evt.detail.dataTransfer &&
evt.detail.dataTransfer.types.contains("Text")) {
evt.preventDefault();
}
});
// Drag content from the ItemContainer.
listViewDetailInfo.addEventListener('dragstart', function (evt) {
// Get the data displayed in the ItemContainer and
// store it in an anonymous object.
var target = evt.target,
title = target.querySelector('h4').innerText,
text = target.querySelector('h6').innerText,
description = target.querySelector('span').innerText,
dragData = {
source: target.id,
title: title,
text: text,
description: description
};
// Store the data in the DataTransfer object as a
// JSON-formatted string.
evt.dataTransfer.setData("Text",
JSON.stringify(dragData));
});
在前面的代码示例中,将一个事件处理程序添加到了 ItemContainer 的 dragstart 事件中,以及 ListView 控件的 itemdragenter 和 itemdragdrop 事件。ItemContainer.dragstart 事件的处理程序从 ItemContainer 提取数据并将它作为一个 JSON 格式字符串存储在与事件关联的 DataTransfer 对象中。在 ListView.onitemdragenter 事件处理程序中,事件的默认行为已禁止,以允许 HTML 内容放在 ListView 控件中。最后,引发 ListView.onitemdragdrop 事件时,数据从 DataTransfer 对象提取,然后插入到 ListView 控件的数据源中。
注意 如果用户尝试使用键盘重新排序 ListView 控件,则作为参数传递给 itemdragdrop 事件的 DataTransfer 对象未定义。在 itemdragdrop 事件的处理程序中,你需要确保 DataTransfer 对象存在,才能尝试读取它包含的数据。
备注
有关如何使用 ListView 控件和在 ListView 控件中启用重新排序、拖动和放置的详细信息,请参阅 HTML ListView 拖放和重新排序示例。