级别: 中级 Nicholas Chase (ibmquestions@nicholaschase.com), 自由撰稿人, Backstop Media
2007 年 5 月 14 日 众所周知,数独(Sudoku)正在全球盛行。因为人们可以轻易地在计算机或纸上玩这个数字游戏,再加上使用 XPath 能很容易地在表单中分析数据,所以利用 XForms 创建能玩数独游戏的表单是可行的。本系列的两篇文章 将介绍如何创建游戏客户机,该客户机能向服务器请求开始新游戏,检测合法和非法操作和游戏结束,保存当前游戏状态。同时还将介绍如何为用户生成新游戏。第 2 部分将探究载入和保存游戏。
阅读本文需对 XForms 基础有一定的了解。要复习该内容,请参阅 参考资料 部分的链接内容。代码是为 Mozilla Firefox XForms 扩展编写的,并且在该平台上已通过测试,不过其思想适用于任何实现。本文的代码是使用 Mozilla 和 Xforms 扩展编写的,因此要实现本文的代码,需要一个支持 Xforms 的浏览器和一个支持 PHP 的服务器。要生成新的数独迷题,还需要安装 Python 和 Python Sudoku 程序。
回顾
在 第 1 部分 中,我们创建了基本的游戏,用户能够使用简单的下拉菜单玩数独游戏。只有当用户成功解答出了迷题时,Submit(提交)按钮才会出现。目前的 Xforms 文档结构如清单 1 所示。
清单 1. 当前页面
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:s="http://www.example.com/sudoku"
xmlns:b="http://www.example.com/board">
<head>
<title>Sudoku</title>
<xforms:model>
<xforms:instance id="content">
<s:game>
<s:row><s:box ro="yes">6</s:box><s:box></s:box>
<s:box></s:box><s:box></s:box><s:box></s:box>
<s:box ro="yes">9</s:box><s:box></s:box><s:box></s:box>
<s:box ro="yes">1</s:box></s:row>
...
<s:square><s:box>0</s:box><s:box>0</s:box><s:box>0</s:box>
<s:box>0</s:box><s:box>0</s:box><s:box>0</s:box><s:box>0
</s:box>
<s:box>0</s:box><s:box>0</s:box></s:square>
...
<s:submitButtonElement>Submit</s:submitButtonElement>
<s:correctRows>no</s:correctRows>
</s:game>
</xforms:instance>
<xforms:bind nodeset="//s:row//s:box[@ro='yes']" readonly="true()" />
<xforms:bind nodeset="//s:submitButtonElement"
relevant="/s:game/s:correctRows = 27" />
<xforms:bind nodeset="//s:correctRows" calculate=
"count(/s:game/s:row[s:box = '1'][s:box = '2'][s:box = '3']
[s:box = '4'][s:box = '5'][s:box = '6'][s:box = '7'][s:box = '8']
[s:box = '9']) +
count(/s:game[s:row[s:box[1]='1']][s:row[s:box[1]='2']]
[s:row[s:box[1]='3']][s:row[s:box[1]='4']][s:row[s:box[1]='5']]
[s:row[s:box[1]='6']][s:row[s:box[1]='7']][s:row[s:box[1]='8']]
[s:row[s:box[1]='9']]) +
...
count(/s:game/s:square[s:box = '1'][s:box = '2']
[s:box = '3'][s:box = '4'][s:box = '5'][s:box = '6']
[s:box = '7'][s:box = '8'][s:box = '9'])" />
<xforms:instance id="templates">
<b:template>
<b:entry><b:sendvalue>0</b:sendvalue>
<b:display></b:display></b:entry>
<b:entry><b:sendvalue>1</b:sendvalue>
<b:display>1</b:display></b:entry>
<b:entry><b:sendvalue>2</b:sendvalue>
<b:display>2</b:display></b:entry>
...
</b:template>
</xforms:instance>
<xforms:submission id="submitgame" action="" method="post"/>
</xforms:model>
<style type="text/css">
div > * {display: inline;}
*:read-only { color: red }
</style>
</head>
<body>
<img src="images/showlayout.gif" style="float:left;height: 64px; width: 64px;" />
<h1 align="center">Sudoku</h1>
<br clear="left" />
<div>
<xforms:repeat id="gamerow" nodeset="instance('content')/s:row">
<xforms:repeat id="gamebox" nodeset="s:box">
<span class="test"><xforms:select1 ref=".">
<xforms:itemset nodeset="instance('templates')/b:entry">
<xforms:label ref="b:display"/>
<xforms:value ref="b:sendvalue"/>
</xforms:itemset>
</xforms:select1></span>
</xforms:repeat>
</xforms:repeat>
</div>
Correct Rows: <xforms:output ref="//s:correctRows" /><br />
<xforms:trigger style="display:block">
<xforms:label>Check it!</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:setvalue ref="/s:game/s:square[1]/s:box[1]"
value="/s:game/s:row[1]/s:box[1]" />
<xforms:setvalue ref="/s:game/s:square[1]/s:box[2]"
value="/s:game/s:row[1]/s:box[2]" />
...
</xforms:action>
</xforms:trigger>
<xforms:submit ref="/s:game/s:submitButtonElement"
submission="submitgame">
<xforms:label>Submit</xforms:label>
</xforms:submit>
</body>
</html> |
回顾一下,实例为表格中的每一行都提供了一个 row 元素,另外还包含一个 square 元素用于为计算表格中的 3x3 方块模拟行数据。(要复习规则请参阅 第 1 部分。)将 correctRows 元素与一个计算绑定在一起,它可以计算出成功解答出的行数、列数和方格数。当总数达到 27 时 Submit 按钮就会出现。
要创建表格,需要循环遍历每一个 row 元素,通过遍历 templates 实例中的每个 entry 元素为每个 box 元素创建一个下拉列表。如果 box 元素具有一个值,这个值是默认选定的。同样,如果原始实例提供了数值,那么表格上的方框将是只读属性的。
面板如图 1 所示。
图 1. 当前的游戏
现在让我们继续创建数独游戏。
增强界面
玩了一段时间以后,可能会感觉到使用下拉菜单太累了,而直接填入正确的数字会更轻松一点。要达到这个目的,不仅需要重新构造表单,同时还需要避免输入无效值。
先从去除下拉菜单开始(参见清单 2)。
清单 2. 使用文本框替代下拉菜单
...
<xforms:model>
<xforms:instance id="content">
<s:game xmlns:s="http://www.example.com/sudoku">
<s:row><s:box s:ro="yes">6</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box s:ro="yes">9</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box s:ro="yes">1</s:box></s:row>
...
<s:square><s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box><s:box>.
</s:box>
<s:box>.</s:box><s:box>.</s:box></s:square>
...
<s:submitButtonElement>Submit</s:submitButtonElement>
<s:correctRows>no</s:correctRows>
</s:game>
</xforms:instance>
...
<style type="text/css">
@namespace xforms url(http://www.w3.org/2002/xforms);
div > * {display: inline;}
*:read-only { color: red }
xforms|input .xf-value, .input-value {width: 30px;
background-color: white;}
</style>
</head>
<body>
<img src="images/showlayout.gif" style="float:left;height: 64px; width: 64px;" />
<h1 align="center">Sudoku</h1>
<br clear="left" />
<div>
<xforms:repeat id="gamerow" nodeset="instance('content')/s:row">
<xforms:repeat id="gamebox" nodeset="s:box">
<span>
<xforms:input ref="." />
</span>
</xforms:repeat>
</xforms:repeat>
</div>
...
|
首先注意到代码的底部,select1 元素被一个简单的 input 元素替代了,该元素把 box 元素的内容作为它的值。通常来说,该元素值设定的宽度比我们需要的宽度要宽很多,所以还需使用级联样式表来设置该元素值的宽度。注意,在这之前必须先声明 xforms 的名称空间别名和名称空间。
最后,还要修改现行的数据。之前,我们使用 0 表示用户需要填入的方框,但因为现在是直接在方框中输入当前值(不用像原来一样使用标签),我们把它改成用句点(. )表示,这样也显示地更加清楚。最后显示的结果如图 2 所示。
图 2. 使用文本输入
现在,还有一个问题就是不能防止用户输入无效的值,例如 “42” 或者 “bleh”。幸运的是,我们可以改进一下。
强制限制
Xforms 内建有模式确认(schema validation),因此我们可以轻松地限制用户能输入文本框的值。比如说,我们可以指定输入必须为整数(如清单 3 所示)。
清单 3. 限制输入为整数
...
<xforms:instance id="content">
<s:game xmlns:s="http://www.example.com/sudoku"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<s:row><s:box xsi:type="xsd:int"
s:ro="yes">6</s:box><s:box xsi:type="xsd:int"
>.</s:box><s:box xsi:type="xsd:int" >.</s:box>
<s:box xsi:type="xsd:int" >.</s:box><s:box
xsi:type="xsd:int" >.</s:box><s:box xsi:type="xsd:int"
s:ro="yes">9</s:box><s:box xsi:type="xsd:int" >.</s:box>
<s:box xsi:type="xsd:int" >.</s:box><s:box xsi:type="xsd:int"
s:ro="yes">1</s:box></s:row>
...
</s:game>
</xforms:instance>
...
<div>
<xforms:repeat id="gamerow" nodeset="instance('content')/s:row">
<xforms:repeat id="gamebox" nodeset="s:box">
<span>
<xforms:input ref=".">
<xforms:action ev:event="xforms-invalid">
<xforms:setvalue ref=".">.</xforms:setvalue>
<xforms:message level="modal">Please
choose an integer from 1 to
9.</xforms:message>
</xforms:action>
</xforms:input>
</span>
</xforms:repeat>
</xforms:repeat>
</div>
...
|
首先,将实例与 XML Schema 名称空间连接起来,并指定 box 元素必须为整数。但是如果表单对不符合类型的数据置之不理的话,这样的指定是没有意义的。
要使表单有所举动,可以指定当输入元素遇到 xforms-invalid 事件时表单采取的动作。当事件发生时,表单将把数值重新设置为句点并显示提示信息。
可以添加一个非整数值测试一下,如图 3 所示。
图 3. 输入无效的值
此时,当您测试时您可能会发现消息框实际上出现了两次。这是因为您把值设置成了非整数,而非整数本身就是无效的输入。
幸运的是,还有另一种选择。我们可以创建一个模式,让它指定所有允许输入的值(如清单 4 所示)。
清单 4. 模式
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com/sudoku"
xmlns="http://www.example.com/sudoku">
<xsd:element name="game" type="GameType"/>
<xsd:element name="row" type="RowType"/>
<xsd:element name="square" type="RowType"/>
<xsd:element name="box" type="BoxType"/>
<xsd:element name="submitButtonElement" type="ButtonType" />
<xsd:element name="correctRows" type="CorrectRowsType" />
<xsd:simpleType name="ButtonType">
<xsd:restriction base="xsd:string" />
</xsd:simpleType>
<xsd:simpleType name="CorrectRowsType">
<xsd:restriction base="xsd:int" />
</xsd:simpleType>
<xsd:simpleType name='BoxType'>
<xsd:restriction base="xsd:string">
<xsd:enumeration value = "."/>
<xsd:enumeration value = "1"/>
<xsd:enumeration value = "2"/>
<xsd:enumeration value = "3"/>
<xsd:enumeration value = "4"/>
<xsd:enumeration value = "5"/>
<xsd:enumeration value = "6"/>
<xsd:enumeration value = "7"/>
<xsd:enumeration value = "8"/>
<xsd:enumeration value = "9"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="RowType">
<xsd:sequence>
<xsd:element ref="box" minOccurs="9" maxOccurs="9" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="GameType">
<xsd:sequence>
<xsd:element ref="row" minOccurs = "9" maxOccurs="9" />
<xsd:element ref="square" minOccurs = "9" maxOccurs="9" />
<xsd:element ref="submitButtonElement" minOccurs="1"
maxOccurs="1" />
<xsd:element ref="correctRows" minOccurs="1" maxOccurs="1" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema> |
我们明确地指定了 box 元素只能为 1-9 的整数或者为句点。然后把这个模式保存为文件 sudoku.xsd,再从表单中引用它(如清单 5 所示)。
清单 5. 引用模式文件
...
<head>
<title>Sudoku</title>
<xforms:model schema="./sudoku.xsd">
<xforms:instance id="content">
<s:game xmlns:s="http://www.example.com/sudoku"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<s:row><s:box s:ro="yes">6</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box s:ro="yes">9</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box s:ro="yes">1</s:box></s:row>
... |
这时再运行表单,我们仍然会看到警示框,不过它只会出现一次,因为根据模式新值是允许输入的。
保存当前游戏
现在已经调整了界面,接下来我们再讨论一下游戏的管理。
第一步是允许用户保存当前游戏。在传统的 HTML 表单中,可以使用复杂的脚本接收数据并保存数据,然后更多的脚本再从中取回。幸运的是,在 Xforms中有更简单的方法
Xforms 允许使用 HTTP 的 “PUT” 方法把实例数据保存到特定的位置。如果 Web 服务器经过正确配置,可以把实例保存在服务器上,但是把数据保存在本地磁盘上是更常用的方法(如清单 6 如示)。
清单 6. 保存到本地文件
...
<xforms:submission id="submitgame" action="" method="post"/>
<xforms:submission id="savegame" action="file:///c:/sudoku.txt"
replace="instance" instance="content" method="put"/>
</xforms:model>
...
<xforms:submit ref="/s:game/s:submitButtonElement" submission="submitgame">
<xforms:label>Submit</xforms:label>
</xforms:submit>
<xforms:submit submission="savegame">
<xforms:label>Save current game</xforms:label>
</xforms:submit>
</body>
</html>
|
此处我们创建了一个新的 submission 元素,它指定了 content 实例并通知表单使用返回的数据替代实例,而不是页面。然而在本例中,并没有任何返回的数据,因为它还指定了 PUT 方法和存放文件的位置。这样的话当您单击该按钮,它便会把实例数据保存在指定位置下的 XML 文件中。
如果需要保存和载入表单,可以使用这个新按钮(如图 4 所示)。
图 4. 保存按钮
更改一些值并单击 Save(保存)按钮。在 C 盘根目录下将会出现一个 XML 文件,其中包含了刚才保存的实例文档。接下来看看如何将其取回。
载入保存的游戏
一旦我们保存了一个游戏,载入它非常简单。我们要做的就是让表单发送一个 HTTP 请求,然后把数据放入实例中去。这可以通过再创建一个 submission 元素来实现(如清单 7 所示)。
清单 7. 载入数据
...
<xforms:submission id="savegame" action="file:///c:/sudoku.txt"
replace="instance" instance="content" method="put"/>
<xforms:submission id="loadgame" action="file:///c:/sudoku.txt"
replace="instance" instance="content" method="get"/>
</xforms:model>
<style type="text/css">
...
<xforms:submit submission="savegame">
<xforms:label>Save current game</xforms:label>
</xforms:submit>
<xforms:submit submission="loadgame">
<xforms:label>Load saved game</xforms:label>
</xforms:submit>
</body>
</html> |
在这种情况下,我们通过发送 GET 请求取回数据,浏览器就是使用它请求新 Web 页面的。然后再用取回的数据替代 content 实例中的内容。要查看是否起作用,先保存然后再载入表单,然后改变一些值。保存游戏,然后重新载入表单使游戏回到初始状态。单击 Load saved game(载入保存游戏) 按钮,可以看到我们改变的值又出现了。
提交游戏
现在这个游戏已经非常吸引人了,但不能总是玩这一个游戏吧?当然不会了,我们应该使用户能够向服务器提交当前游戏并获得一个新游戏。稍后您将看到真正的 submission 元素,但是在这之前我们需要调整一下浏览器的设置。
出于安全性考虑,Xforms 只能在最初下载的服务器中载入和保存数据。将表单上传到请求数据的服务器可以解决部分问题。但是问题并没有全部解决。
难点在于事实上我们把数据保存在本地文件中,而不是初始服务器上。要解决这个问题,我们需要在 Xforms 的 “信任服务器” 列表中添加服务器。
为此,选择 Tools->Options 并确保复选框中选择了 “Allow XForms to access other domains”。下一步,单击 Allowed Sites 按钮。添加您所需的服务器名称并选中 Load and Save 复选框。(参见图 5)单击 Add 按钮然后关闭对话框。重启浏览器使修改生效。
图 5. 受信任站点
现在可以开始创建真正的 submission 元素了。从根本上说,我们使用了一个 convertGame.php PHP 脚本用于获得新游戏。稍后我们将看到该脚本。现在还是先更新一下 submission 元素。(如清单 8 所示)。
清单 8. 完整的 submission 元素
...
</xforms:instance>
<xforms:submission id="submitgame"
action="http://www.backstopmedia.com/sudoku/convertGame.php"
replace="instance" instance="content" method="post"/>
<xforms:submission id="savegame" action="file:///c:/sudoku.txt"
replace="instance" instance="content" method="put"/>
... |
和之前一样,我们指定只替代实例而不是整个页面,以及需要替代的实例。同时还添加了将为我们提供新游戏的脚本的 URL。
获取新游戏
除非您是一个数学能手(或者有太多空闲的时间),否则您是不可能独自生成这些迷题的。幸运的是,您也没必要这样做。
Python Sudoku 是一个开放源码的应用程序,能够生成和解答数独迷题。(查看 参考资料 获得下载 URL) 它用于生成迷题,可供纸上解答或供其它应用程序使用。不过我们还需要进行一些调整(以及一个支持 Python 脚本的主机)使它能直接向表单输出迷题。
我们将在线下生成许多的迷题,然后随机地把它们返回给表单。要创建这些迷题,解压 Python Sudoku 压缩包并在刚解压的目录下执行如下命令(参见清单 9)。
清单 9. 创建新的迷题
>>>>>python pysdk.py -c game11.sdk
Creating sudoku... success!
1 _ _ _ _ _ 5 _ _
_ _ _ 6 _ _ 9 _ _
_ _ _ 3 4 _ _ _ _
_ _ _ _ _ _ _ 1 _
_ 8 _ _ _ 2 _ _ 9
7 9 2 _ _ 5 _ _ _
_ _ 8 _ 3 _ _ 6 _
_ _ _ _ _ _ _ _ _
_ 2 4 1 _ 8 _ 3 _ |
在本例中,我们创建了一个 game11.sdk 文件。创建一系列这样的迷题并把它们保存为 game1.sdk、game2.sdk、game3.sdk 等等。
这些文件都使用相同的格式(如清单 10 所示)。
清单 10. 生成的游戏
# boardsize 3 x 3
1 0 0 0 0 0 5 0 0
0 0 0 6 0 0 9 0 0
0 0 0 3 4 0 0 0 0
0 0 0 0 0 0 0 1 0
0 8 0 0 0 2 0 0 9
7 9 2 0 0 5 0 0 0
0 0 8 0 3 0 0 6 0
0 0 0 0 0 0 0 0 0
0 2 4 1 0 8 0 3 0 |
生成游戏后,把它们移到能执行 PHP 的服务器上面去。这时再创建一个 convertGame.php PHP 脚本,用于随机地选取文件并读取数据,然后创建一个返回给表单的实例。
该脚本如清单 11. 所示。
清单 11. convertGame.php 脚本
<?php
$nextGame = rand(1, 10);
$lines = file('game'.$nextGame.'.sdk');
echo '<s:game xmlns:s="http://www.example.com/sudoku">';
foreach ($lines as $line_num => $line) {
$line = str_replace(" ", "", $line);
$line = str_replace("\n", "", $line);
if ($line_num > 0 && $line != '') {
echo "<s:row>";
for ($x = 0; $x < 9; $x++){
$thisBox = substr($line, $x, 1);
if ($thisBox == "0") {
echo "<s:box>.</s:box>";
} else {
echo "<s:box s:ro='yes'>".$thisBox."</s:box>";
}
}
echo "</s:row>";
}
}
echo " <s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:square><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box><s:box>.</s:box><s:box>.</s:box>
<s:box>.</s:box></s:square>
<s:submitButtonElement>Submit</s:submitButtonElement>
<s:correctRows>no</s:correctRows>";
echo '</s:game>';
?> |
要开始,需要选择下一个游戏。这里,我们随机地选择 1-10(包含 1 和 10)的整数。然后可以使用这个整数创建用于返回游戏的文件名。然后,使用 file() 函数解压文件中各行中的一组数据。
然后便可以开始输出实例,从根元素 game 打开标记开始。对于每一行,首先解压所有的空格和行末的换行符。完成这步这后,我们能够分辨各行之间的不同,哪些行真正包含了数据而哪些行没有。对这样的每一行,打开一个新的 row 元素然后遍历其中的的每个数字并创建一个 box 元素。如果数字为 0,则把它替换成句点,如果不为 0,则设置为只读属性(ro)。遍历到每一行的末尾,则关闭 row 元素。
如果您是在游戏文件中处理数据,那么将需要一些额外的数据,例如 square 元素、submitButtonElement 元素和 correctRows 元素。最后,关闭 game 元素。
如果您把表单、模式和 convertGame.php 脚本装载在服务器中,并在浏览器中调用它们,可以对其进行测试。(为避免在每次提交之前都必须把迷题解答出来,隐藏在 Submit 按钮之后的 bind 元素被注释掉了。)重复点击 Submit 按钮查看迷题的变化。
就这么简单!
结束语
在 本系列的两篇文章 中,我们创建了一个模拟数独游戏的 Xforms 表单。在 第 1 部分 中,我们创建了基本的表单,使用户能够使用下拉菜单玩数独游戏。表单计算迷题是否被解答出来,并根据相应情况做出反应。在第 2 部分中,我们把表单修改成使用文本输入,并且添加了模式约束使用户不能输入无效数据。同时使用户能够在本地保存现有游戏,并能将保存的游戏载入到当前实例。最后,我们探讨了创建新游戏以及如何将它们提供给表单的方法。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 第 2 部分示例代码 | xforms2_source.zip | 7KB | HTTP |
|---|
参考资料 学习
获得产品和技术
讨论
关于作者  | |  | Nicholas Chase 曾经参与多家公司的网站开发,包括 Lucent Technologies、Sun Microsystems、Oracle 和 Tampa Bay Buccaneers。Nick 曾经做过高中物理教师、低放射性废弃设备管理员、在线科幻杂志的编辑、多媒体工程师、Oracle 教员以及一家交互通信公司的首席技术官。他出版了多部著作,包括 XML Primer Plus(Sam's)。 |
对本文的评价
|