本文來自《 ASP.NET AJAX程序設計——第II卷:客戶端Microsoft AJAX Library相關 》第9章第3節。
9.3 示例程序:實現 IDragSource 和 IDropTarget 接口將商品拖放至購物車中
本章第1節介紹了ASP.NET AJAX客戶端拖放框架中的DragDropManager對象以及IDragSource和IDropTarget兩個重要接口。下面就讓我們通過一個簡單但卻足夠完善的示例程序來說明其具體的使用方法。
9.3.1 示例程序功能
本示例程序模擬了一個網上商店。程序加載之后將自動從服務器端Web Service中取得當前的商品信息,并顯示在頁面中。同樣顯示在頁面中的還有用戶的購物車。如圖9-6所示。
圖9-6 網上商店的初始界面
用戶可以非常直觀地在左側的商品列表中用鼠標選擇并將某種商品拖動到右側的購物車中,類似于實際生活中在超市購物的場景,如圖9-7所示。是不是很酷呢?
圖9-7 通過拖動商品到購物車中完成購物過程
在購物車范圍內松開鼠標左鍵,商品即被添加到了購物車中。每拖放一次某商品,購物車中該商品的數量就會加1。圖9-8顯示了購物車中包含一本《Atlas基礎教程》、兩本《ASP.NET AJAX程序設計 第I卷》和三本《CSS禪意花園》時的樣式。
圖9-8 在購物車中添加多個商品
在圖9-8中點擊購物車中的“Order”按鈕,程序將把當前購物車中的商品傳回服務器端Web Service中進行處理。然后把Web Service的處理結果顯示給用戶,如圖9-9所示。
圖9-9 用服務器端Web Service實現購買功能
9.3.2 編寫服務器端 Web Service
既然程序需要從服務器端取得數據,還需要將購物車中的商品提交回服務器處理,那么處理數據的Web Service定是必不可少的。首先創建一個名為ShoppingService的Web Service:
[System.Web.Script.Services.ScriptService]
public class ShoppingService : System.Web.Services.WebService
{
}
在其中創建一個名為Data的私有屬性,表示當前網上商店中的產品集合:
private
List<Product> _products;
private
List<Product> Products
{
get
{
if (_products == null )
{
_products =
new
List<Product>();
_products.Add( new Product(1, "Atlas基礎教程" , 39));
_products.Add( new Product(2, "ASP.NET AJAX程序設計第I卷" , 49));
_products.Add( new Product(3, "ASP.NET AJAX程序設計第II卷" , 55));
_products.Add( new Product(4, "ASP.NET AJAX程序設計第III卷" , 39));
_products.Add( new Product(5, "CSS禪意花園" , 49));
}
return
_products;
}
}
出于演示的目的,我們并沒有查詢數據庫得到商品集合,而是直接硬編碼了幾種商品(這些商品都是我已經出版或即將要出版的圖書)。在實際開發中,這部分數據通常都需要查詢數據庫得到。
上述代碼中用到的Product類表示一種商品,其定義如下。Id、Name和Price屬性分別代表商品的ID、名稱和價格:
public class Product
{
private int _id;
public int Id
{
get {
return
_id; }
set { _id =
value
; }
}
private string _name;
public string Name
{
get {
return
_name; }
set { _name =
value
; }
}
private int _price;
public int Price
{
get {
return
_price; }
set { _price =
value
; }
}
public
Product()
{
}
public Product( int id, string name, int price)
{
this
._id = id;
this
._name = name;
this
._price = price;
}
}
回到ShoppingService這個Web Service中。編寫一個名為GetProducts()的方法,用來返回現有的商品:
[WebMethod]
public
Product[] GetProducts()
{
return
Products.ToArray();
}
還需要一個名為Order()的方法,用來處理客戶端提交回的購物車中商品的信息,并返回處理結果。注意Order()的方法的參數類型為Dictionary<string, int>,其中每個條目的Key為該商品的Id,Value為購物車中該商品的個數:
[WebMethod]
public string Order(Dictionary< string , int > productsToBeOrdered)
{
// 商品總數
int
totalQuantity = 0;
// 商品總價格
int
totalPrice = 0;
// 計算商品總數及商品總價格
foreach (KeyValuePair< string , int > productQuantity in productsToBeOrdered)
{
foreach (Product product in Products)
{
if
(product.Id.ToString() == productQuantity.Key)
{
totalQuantity += productQuantity.Value;
totalPrice += (product.Price * productQuantity.Value);
break
;
}
}
}
// 進行其他處理
// ......
// 返回客戶端處理結果
return string .Format(
"You've ordered {0} product(s) and the total price is {1}. Thank you!"
,
totalQuantity,
totalPrice
);
}
這樣即完成了服務器端Web Service的編寫。
9.3.3 編寫 DraggableProductBehavior 實現可拖動的商品
接下來定義一個名為DraggableProductBehavior的行為,該行為將實現Sys.Preview.UI.IDragSource接口,用來附加到表示產品的DOM元素上并為其提供拖動支持。
新建一個名為ShoppingCart.js的JavaScript文件,先來注冊Dflying命名空間:
Type.registerNamespace(
"Dflying"
);
然后聲明該DraggableProductBehavior的實例字段:
Dflying.DraggableProductBehavior =
function
(element) {
// 初始化基類
Dflying.DraggableProductBehavior.initializeBase(
this
, [element]);
// 按下鼠標鍵時的事件處理函數
this ._mouseDownHandler = Function.createDelegate( this , this ._handleMouseDown);
// 該可拖動對象所表示的產品
this ._product = null ;
// 拖動時跟隨鼠標的半透明元素
this ._visual = null ;
}
接下來是DraggableProductBehavior的原型定義。篇幅所限,這里不能對代碼進行詳細解釋,請參考其中的注釋仔細閱讀。需要特別留意的是IDragSource接口中各個方法的實現方式,以及其中粗體部分告知DragDropManager開始拖放操作的語法:
Dflying.DraggableProductBehavior.prototype = {
// IDragSource接口中的方法
// 取得該可拖動對象的數據類型——"Product"
get_dragDataType:
function
() {
return "Product" ;
},
// 取得該可拖動對象的數據
getDragData:
function
(context) {
return this ._product;
},
// 可拖動對象的拖拽模式——拷貝
get_dragMode:
function
() {
return
Sys.Preview.UI.DragMode.Copy;
},
// 拖動開始時的處理方法
onDragStart:
function
() {
},
// 拖動進行時的處理方法
onDrag:
function
() {
},
// 拖動結束時的處理方法
onDragEnd:
function
(canceled) {
if ( this ._visual)
this .get_element().parentNode.removeChild( this ._visual);
},
// product屬性
get_product:
function
(product) {
return this ._product;
},
set_product:
function
(product) {
this
._product = product;
},
// 初始化方法
initialize:
function
() {
$addHandler( this .get_element(), "mousedown" , this ._mouseDownHandler);
},
// mousedown事件處理函數
_handleMouseDown:
function
(ev) {
// DragDropManager需要該項設定
window._event = ev;
// 設置拖動時跟隨鼠標的半透明元素的樣式
this ._visual = this .get_element().cloneNode( true );
this ._visual.style.opacity = "0.7" ;
this
._visual.style.filter =
"progid:DXImageTransform.Microsoft.BasicImage(opacity=0.7)"
;
this
._visual.style.zIndex = 99999;
this .get_element().parentNode.appendChild( this ._visual);
var location = Sys.UI.DomElement.getLocation( this .get_element());
Sys.UI.DomElement.setLocation(
this
._visual, location.x, location.y);
// 告知DragDropManager開始拖放操作
Sys.Preview.UI.DragDropManager.startDragDrop( this , this ._visual, null );
},
// 析構方法
dispose:
function
() {
if ( this ._mouseDownHandler)
$removeHandler( this .get_element(), "mousedown" , this ._mouseDownHandler);
this ._mouseDownHandler = null ;
Dflying.DraggableProductBehavior.callBaseMethod( this , 'dispose' );
}
}
最后在ASP.NET AJAX客戶端框架中注冊該DraggableProductBehavior行為。可以看到該行為實現了IDragSource接口:
Dflying.DraggableProductBehavior.registerClass(
"Dflying.DraggableProductBehavior"
,
Sys.UI.Behavior,
Sys.Preview.UI.IDragSource
);
9.3.4 編寫 ShoppingCartBehavior 實現可接受商品投放的購物車
依然在ShoppingCart.js這個JavaScript文件中,我們還要定義一個名為ShoppingCartBehavior的行為,該行為將實現Sys.Preview.UI.IDropTarget接口,用來附加到表示購物車的DOM元素上并讓其能夠接受可拖動對象的投放。
首先聲明該ShoppingCartBehavior的實例字段:
Dflying.ShoppingCartBehavior =
function
(element) {
// 初始化基類
Dflying.ShoppingCartBehavior.initializeBase(
this
, [element]);
// 購物車中的產品列表
this ._products = new Object();
}
接下來是ShoppingCartBehavior的原型定義。同樣限于篇幅,這里不一一詳細說明。請注意代碼中IDropTarget接口中各個方法的實現方式,以及其中粗體部分在DragDropManager中注冊/取消注冊該投放目標對象的語法:
Dflying.ShoppingCartBehavior.prototype = {
// IDropTarget接口中的方法
// 返回購物車元素
get_dropTargetElement:
function
() {
return this .get_element();
},
// 判斷某可拖動元素是否能夠投放在該投放目標對象中
canDrop:
function
(dragMode, dataType, data) {
return (dataType == "Product" && data);
},
// 將某可拖動元素投放在該投放目標對象中
drop :
function
(dragMode, dataType, data) {
if (dataType == "Product" && data) {
// 購物車中尚無本產品,設置數量為1
if ( this ._products[data.Id] == null ) {
this
._products[data.Id] = {Product: data, Quantity: 1};
}
// 購物車中已經有本產品,將其數量加1
else
{
this
._products[data.Id].Quantity++;
}
// 刷新購物車的UI
this
._refreshShoppingCart();
// 將購物車背景顏色設置回白色
this .get_element().style.backgroundColor = "#fff" ;
}
},
// 某可拖動元素位于該投放目標對象中時的處理方法
onDragEnterTarget :
function
(dragMode, dataType, data) {
if (dataType == "Product" && data) {
// 設置購物車的背景顏色為灰色
this .get_element().style.backgroundColor = "#E0E0E0" ;
}
},
// 某可拖動元素離開該投放目標對象時的處理方法
onDragLeaveTarget :
function
(dragMode, dataType, data) {
if (dataType == "Product" && data) {
// 將購物車背景顏色設置回白色
this .get_element().style.backgroundColor = "#fff" ;
}
},
// 某可拖動元素在該投放目標對象中拖動時的處理方法
onDragInTarget :
function
(dragMode, dataType, data) {
},
// 根據當前購物車中的產品列表刷新購物車的UI
_refreshShoppingCart:
function
() {
var cartBuilder = new Sys.StringBuilder();
for ( var id in this ._products) {
cartBuilder.append(
"<div>"
);
cartBuilder.append(
this
._products[id].Product.Name);
cartBuilder.append(
" * "
);
cartBuilder.append(
this
._products[id].Quantity);
cartBuilder.append(
"</div>"
);
}
this
.get_element().innerHTML = cartBuilder.toString();
},
// 返回表示當前購物車中的產品Id以及數量的對象
getProductsToBeOrdered:
function
() {
var productsToBeOrdered = new Object();
for ( var id in this ._products) {
productsToBeOrdered[id] =
this
._products[id].Quantity;
}
return
productsToBeOrdered;
},
// 初始化方法
initialize:
function
() {
// 初始化基類
Dflying.ShoppingCartBehavior.callBaseMethod( this , "initialize" );
// 在DragDropManager中注冊該投放目標對象
Sys.Preview.UI.DragDropManager.registerDropTarget(
this
);
},
// 析構方法
dispose:
function
() {
// 在DragDropManager中取消注冊該投放目標對象
Sys.Preview.UI.DragDropManager.unregisterDropTarget(
this
);
Dflying.ShoppingCartBehavior.callBaseMethod( this , "dispose" );
}
}
最后在ASP.NET AJAX客戶端框架中注冊該ShoppingCartBehavior行為。可以看到該行為實現了IDropTarget接口:
Dflying.ShoppingCartBehavior.registerClass(
"Dflying.ShoppingCartBehavior"
,
Sys.UI.Behavior, Sys.Preview.UI.IDropTarget);
在ShoppingCart.js文件的末尾,不要忘記調用Application對象的notifyScriptLoaded()方法,通知ASP.NET AJAX客戶端框架該腳本文件已經順利加載:
if ( typeof (Sys)!== 'undefined' )Sys.Application.notifyScriptLoaded();
9.3.5 編寫頁面代碼
新建一個ASP.NET頁面,在其中添加一個ScriptManager控件并引入必要的腳本文件(PreviewScript.js、PreviewDragDrop.js和ShoppingCart.js)以及9.3.2節中編寫的Web Service:
< asp:ScriptManager ID ="sm" runat ="server" >
< Scripts >
< asp:ScriptReference Assembly ="Microsoft.Web.Preview"
Name ="PreviewScript.js" />
< asp:ScriptReference Assembly ="Microsoft.Web.Preview"
Name ="PreviewDragDrop.js" />
< asp:ScriptReference Path ="ShoppingCart.js" />
</ Scripts >
< Services >
< asp:ServiceReference Path ="ShoppingService.asmx" />
</ Services >
</ asp:ScriptManager >
然后編寫購物車的相關HTML。其中id為shoppingCart的<div />表示購物車元素。id為btnOrder的<input />表示提交訂單的按鈕:
< div id ="shoppingCartContainer" >
< div >< strong > Shopping Cart </ strong ></ div >
< div id ="shoppingCart" > Drop Products Here... </ div >
< input id ="btnOrder" type ="button" value ="Order"
onclick ="return btnOrder_onclick()" />
</ div >
網站中產品,即可拖動元素的容器相關的HTML非常簡單——一個<div />而已。隨后我們將在程序運行時根據Web Service返回的商品集合動態創建容器中的各個表示商品的元素:
< h1 > Dflying's Books </ h1 >
< div id ="productContainer" ></ div >
示例程序中購物車和商品列表相關的CSS Class定義如下:
#shoppingCartContainer
{
float: right;
width: 220px;
border: 1px solid black;
margin: 3px;
}
#productContainer div
{
width: 250px;
padding: 3px;
margin: 3px;
border: 1px solid
#666;
background-color:
#fff;
cursor: move;
}
9.3.6 通過 Web Service 取得商品并顯示在頁面中
在客戶端應用程序初始化之后,我們需要異步調用服務器端Web Service得到商店中產品的信息,并順便為購物車添加ShoppingCartBehavior行為:
function
pageLoad(sender, args) {
// 調用Web Service得到商店中的產品集合
ShoppingService.GetProducts(onProductsGot);
// 為購物車添加ShoppingCartBehavior行為
$create(
Dflying.ShoppingCartBehavior,
{ "name" : "myShoppingCartBehavior" },
null
,
null
,
$get(
"shoppingCart"
)
);
}
在onProductsGot()回調函數中,我們要根據返回的商品集合動態創建容器中的各個表示商品的元素,并為其一一添加DraggableProductBehavior行為:
function
onProductsGot(result) {
// 獲取顯示各個產品的容器
var productContainer = $get( "productContainer" );
// 遍歷服務器端返回的產品集合
for ( var index = 0; index < result.length; ++ index) {
// 當前產品
var
thisProduct = result[index];
// 根據該產品信息創建DOM元素,并添加到產品容器中
var productElem = document.createElement( "div" );
productElem.innerHTML = thisProduct.Name +
" - RMB: "
+ thisProduct.Price;
productContainer.appendChild(productElem);
// 為該產品添加DraggableProductBehavior行為
$create(
Dflying.DraggableProductBehavior,
{ "product" : thisProduct}, // 設置product屬性
null
,
null
,
productElem
);
}
}
9.3.7 將購物車中的商品提交回 Web Service 處理
當用戶點擊購物車中的“Order”按鈕時,我們需要把購物車中當前的產品信息(包括Id和數量)傳回服務器端進行處理。
“Order”按鈕的click事件的處理函數如下。其中首先使用Sys.UI.Behavior.getBehaviorByName()方法得到購物車所附加的ShoppingCartBehavior行為,然后取得當前購物車中各個產品的Id和數量,最后將這部分信息回傳給Web Service進行處理:
function
btnOrder_onclick() {
// 得到購物車所附加的ShoppingCartBehavior行為
var
shoppingCartBehavior = Sys.UI.Behavior.getBehaviorByName(
$get(
"shoppingCart"
),
"myShoppingCartBehavior"
);
// 取得當前購物車中各個產品的Id和數量
var
productsToBeOrdered =
shoppingCartBehavior.getProductsToBeOrdered();
// 調用Web Service處理訂單
ShoppingService.Order(productsToBeOrdered, onOrdered);
}
在productsToBeOrdered()回調函數中,我們只是簡單地使用alert()方法將服務器端的響應提示給用戶:
function
onOrdered(result) {
alert(result);
}
這樣即完成了本示例程序的編寫。運行該程序并嘗試用這種嶄新的拖放方式進行購物,你將看到如圖9-6、圖9-7、圖9-8和圖9-9中所示的界面。這種簡單直觀的購物體驗是不是很令人贊嘆呢?
在本示例程序中,我們完整地演示了ASP.NET AJAX客戶端拖放框架支持的自定義拖放效果的強大功能,以及IDragSource和IDropTarget兩個重要接口在拖放全過程中提供的完善的可定制能力。在實際應用中,我們完全可以簡單地通過實現這兩個接口來實現那些用戶夢寐以求的炫目的拖放效果。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
