65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 AJAX 拖放的拼图游戏

2008年3月30日

CPOL

5分钟阅读

viewsIcon

154118

downloadIcon

4088

使用 ASP.NET 2.0 AJAX Futures November CTP 实现的拼图游戏。

Screenshot.jpg

引言

本教程旨在快速解释如何使用 ASP.NET 2.0 AJAX Futures November CTP 实现拖放功能。为了解释这项技术,我创建了一个简单的项目,其中包含一个实现了拼图游戏的自定义 AJAX 控件。

背景

当我开始学习新的 ASP.NET AJAX 框架时,我让公司给我买了一本书,我选择了一本非常棒的书

在这本书中,我找到了一个解释如何使用 PreviewDragDrop 在 Web 上实现拖放功能的章节。我使用了这一章以及从互联网 (Google) 获得的其他信息来创建我的 AJAX 控件。

ASP.NET AJAX Framework

ASP.NET AJAX Framework 的基本思想是能够使用面向对象编程 (模拟 OOP) 的 JavaScript,并使其类似于 C#。大多数 C# 功能在 JavaScript 中都可用:命名空间、类、接口、枚举等。除了 OOP 功能外,还可以实现可视化的自定义控件 (客户端控件),从而扩展 HTML 元素的功能。

基本上,我们有两种类型的控件 (可视控件)

  • 行为 (继承自 Sys.UI.Behavior)
  • 控件 (继承自 Sys.UI.Control)

区别仅仅在于逻辑,但通常 Sys.UI.Behavior 用于扩展不同类型 HTML 元素的行为,而 Sys.UI.Control 则用于扩展单一类型 HTML 元素的行为。

例如,如果我们想实现一个在点击事件上显示警报的行为,我们可以创建一个继承自 Sys.UI.Behavior 的类,然后我们可以将此代码用于多种 HTML 元素类型:DIVSPANINPUT 等。

//Namespace declaration
Type.registerNamespace("MyNamespace");

//Constructor
MyNamespace.MyBehavior = function(element)
{
   MyNamespace.MyBehavior.initializeBase(this, [element]);
}

MyNamespace.MyBehavior.prototype =
{
   initialize : function()
   {
     // Add event handler on click
      // Parameters: 
      // 1) The HTML element
      // 2) Event name without "on" ("onclick"="click")
      // 3) Control instance
      $addHandlers(this.get_element(), { "click" : this._onClick }, this);
   },

   dispose : function()
   {
      // Remove all events handlers for the current HTML element
      $clearHandlers(this.get_element());
   },

   // Event handler onclick
   _onClick : function(evt)
   {
      // Show the id of current HTML Element
      alert(this.get_id());
   }
};

// Register class
MyNamespace.MyBehavior.registerClass("MyNamespace.MyBehavior", Sys.UI.Behavior);

当我们需要在页面中使用它时,我们可以写如下代码

// Create an instance of our Behavior
// and attach it to HTML element with id 'elementId'
// Parameters:
// 1) Class name with namespace
// 2) Properties in JSON format
// 3) Events in JSON format
// 4) References in JSON format
// 5) HMTL element
$create(MyNamespace.MyBehavior, {}, {}, {}, $get('elementId'));

如果我们想为一种元素类型实现行为,或者想实现一个复杂的控件 (由不同元素组成的视觉控件),我们需要继承 Sys.UI.Control

支持 AJAX 的服务器控件

通常,控件不是通过 JavaScript 中的 $create 手动创建的,而是由 ASP.NET 服务器控件 (控件的服务器端版本) 自动生成的。这样,我们就可以实现一个服务器控件并在 Visual Studio 中获得设计时支持。当我们想创建一个支持 AJAX 的服务器控件时,我们需要继承 ScriptControl (而不是 WebControl)。这个基类包含了所有有用的方法,可以使客户端控件 (JavaScript) 和服务器控件 (.NET) 之间建立关联。

ScriptControl 中,我们需要重写两个方法

  • GetScriptDescriptors: 将 JavaScript 类名和属性传递给客户端控件。
  • GetScriptReferences: 传递我们客户端控件使用的脚本。(ScriptManager 会在页面加载期间自动加载这些脚本)。
protected override IEnumerable<System.Web.UI.ScriptDescriptor> GetScriptDescriptors()
{
    if (!string.IsNullOrEmpty(this.Filename))
    {
        ScriptControlDescriptor descriptor = new 
          ScriptControlDescriptor("JigsawPuzzleGameControl.PuzzleGameAjax", 
          this.ClientID);
        descriptor.AddProperty("nRows", this.NRows);
        descriptor.AddProperty("nColumns", this.NColumns);

        yield return descriptor;
    }
}
         
protected override IEnumerable<System.Web.UI.ScriptReference> GetScriptReferences()
{
    if (!string.IsNullOrEmpty(this.Filename))
    {
        List<ScriptReference> scripts = new List<ScriptReference>();

        ScriptReference scriptReference1 = new ScriptReference("PreviewScript.js", 
                                           "Microsoft.Web.Preview");
        scripts.Add(scriptReference1);

        ScriptReference scriptReference2 = new ScriptReference("PreviewDragDrop.js", 
                                           "Microsoft.Web.Preview");
        scripts.Add(scriptReference2);

        ScriptReference scriptReference3 = new ScriptReference();
        scriptReference3.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), 
                                "JigsawPuzzleGameControl.Resources.Helpers.js");
        scripts.Add(scriptReference3);

        ScriptReference scriptReference4 = new ScriptReference();
        scriptReference4.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), 
                                "JigsawPuzzleGameControl.Resources.PuzzleGameAjax.js");
        scripts.Add(scriptReference4);

        return scripts;
    }
    else
    {
        return new List<ScriptReference>();
    }
}

拖放

为了在我们的控件中实现拖放功能,我们需要在两个客户端控件中实现两个接口

  • 继承自 Sys.Preview.UI.IDropTarget 的客户端控件:此控件代表放置区域
  • 继承自 Sys.Preview.UI.IDragSource 的客户端控件:此控件代表拖动元素

Sys.Preview.UI.IDropTarget 中,我们需要实现以下方法

  • get_dropTargetElement: 返回放置区域的 HTML 元素
  • canDrop: 检查是否允许放置特定的拖动元素
  • drop: 执行放置操作
  • onDragEnterTarget: 通常与 onDragLeaveTarget 一起使用来实现视觉效果
  • onDragLeaveTarget: 通常与 onDragEnterTarget 一起使用来实现视觉效果

我们还需要在 Initialize 方法中为 MouseDown 事件添加一个事件处理程序,然后调用 Sys.Preview.UI.DragDropManager.startDragDrop

initialize : function()
{
    JigsawPuzzleGameControl.DragPuzzleGameAjaxElement.callBaseMethod(this, 
                                                            "initialize");
    $addHandlers(this.get_element(), 
      { "mousedown" : this._onMouseDown }, this);
},

_onMouseDown : function(evt)
{
    window._event = evt;
    evt.preventDefault();
    
    Sys.Preview.UI.DragDropManager.startDragDrop(this, 
                            this.get_element(), null);
},

Sys.Preview.UI.IDragSource 中,我们需要实现以下方法

  • get_dragDataType: 返回拖动元素的类型 (在 canDrop 中用于检查拖动项是否与放置区域兼容的字符串)
  • getDragData: 返回拖动项的数据并传递给放置区域 (drop 方法)
  • get_dragMode: 返回拖动操作是 Move 还是 Copy
  • onDragStart: 在拖动操作开始时调用
  • onDragEnd: 在拖动操作结束时调用
  • onDrag: 在拖动操作完成时调用

我们还需要注册和注销客户端组件作为放置区域

initialize : function()
{
    JigsawPuzzleGameControl.DropPuzzleGameAjaxElement.callBaseMethod(this, "initialize");
    Sys.Preview.UI.DragDropManager.registerDropTarget(this);
},

dispose : function()
{
    Sys.Preview.UI.DragDropManager.unregisterDropTarget(this);
    JigsawPuzzleGameControl.DropPuzzleGameAjaxElement.callBaseMethod(this, "dispose");
},

游戏

项目分为两个项目

  1. 包含控件使用示例的网站
  2. 包含控件的库项目

在库项目中,有一个名为 PuzzleGameAjax 的类,它继承自 ScriptControl (AJAX 中所有自定义控件的基类)。这个类是我控件的服务器端代码。

在客户端,我们有三个 JavaScript 类

  • PuzzleGameAjax (包含实现游戏的代码)
  • DropPuzzleGameAjaxElement (包含实现放置区域的代码)
  • DragPuzzleGameAjaxElement (包含实现拖动区域的代码)

DropPuzzleGameAjaxElement 实现 Sys.Preview.UI.IDropTarget 接口,DragPuzzleGameAjaxElement 实现 Sys.Preview.UI.IDragSource 接口。这两个接口由 AJAX 框架使用,以通用的方式处理拖放。

关注点

AJAX ASP.NET 是一项非常好的技术,我目前正在使用它来实现非常复杂的行为以改善用户体验。拖放功能绝对是最用户友好的功能,它使软件非常直观易用。通常,在 Web 上,此功能需要大量的 JavaScript 代码,但有了 PreviewDragDrop,一切都变得容易。

历史

  • 2008 年 3 月 30 日 - 首次发布 (老实说很糟糕,但我想要做得更好!)。
  • 2008 年 4 月 6 日 - 我使文章更加完整。
  • 2008 年 4 月 8 日 - 修复了解决方案中的一个问题。
  • 2008 年 4 月 9 日 - 现在可与 Visual Studio 2005/2008 (包括 Express Edition) 配合使用。
  • 2008 年 4 月 22 日 - 添加了在线演示。
© . All rights reserved.