QtQuick桌面应用开发指导 3)实现UI和功能_B 4)动态管理Note对象_A

3.2 把Page Item和Marker Item绑定

之前我们实现了PagePanel组件, 使用了三个state来切换Page组件的opacity属性; 这一步我们会使用Marker和MarkerPanel组件来实现页面导航;

在原型阶段, MarkerPanel组件十分简单, 没有任何功能; 它使用了Repeater类型来产生三个QML Item以及Marker组件作为delegate;

MarkerPanel应该存储当前激活的marker(标记), 即那个被用户点击的marker; 基于MarkerPanel中激活的marker, PagePanel会更新它的state属性; 我们需要将PagePanel的state属性和MarkerPanel新的属性--持有当前激活marker的属性绑定起来;

在MarkerPanel中定义一个string属性--activeMarker;

// MarkerPanel.qml


1

2

3

4

5

6

7

Item
{

    id:
root

    width:
150;    height: 450

    //
a property of type string to hold

    //
the value of the current active marker

    property
string activeMarker: 
"personal"

//...

我们可以把一个markerid值存储起来, 用来唯一地识别marker item; 这样, activeMarker会持有用户所点击的marker item的markerid的值,

根据model, Repeater元素可以产生三个marker item, 因此我们可以使用一个model来存储markerid值, 然后在Repeater中使用;

// MarkerPanel.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

Item
{

    id:
root

    width:
150;    height: 450

    //
a property of type string to hold

    //
the value of the current active marker

    property
string activeMarker: 
"personal"

    //
a list for holding respective data for a Marker item.

    property
variant markerData: [

        {
markerid: 
"personal" },

        {
markerid: 
"fun" },

        {
markerid: 
"work" }

    ]

    Column
{

        id:
layout

        anchors.fill:
parent

        spacing:
5

        Repeater
{

            //
using the defined list as our model

            model:
markerData

            delegate:
Marker {

                id:
marker

                //
handling the clicked signal of the Marker item,

                //
setting the currentMarker property

                //
of MarkerPanel based on the clicked Marker

                //MouseArea
{

                    //anchors.fill:
parent

                    onClicked:
root.activeMarker = modelData.markerid

                //}

            }

        }

    }

}

上述代码中我们在onClicked signal handler中设置了 activeMarker属性; 这意味着我们已经在Marker组件中定义了一个clicked() signal来通知用户的鼠标点击事件;

// Marker.qml


1

2

3

4

5

6

7

8

9

10

11

Item
{

    id:
root

    width:
50; height: 90

    signal
clicked()

    MouseArea
{

        id:
mouseArea

        anchors.fill:
parent

        //
emitting the clicked() signal Marker item

        onClicked:
root.clicked()

    }

}

目前我们以后有了PagePanel组件使用state属性来管理page, 让MarkPanel组件可以识别激活的marker, 因此, 切换各个page的可见性可以通过改变page的opacity属性来做到;

来看看怎样使用 activeMarker属性来对应地更新PagePanel的state;

在main.qml里面, 已经有了Page item和 MarkerPanel定位好了, 我们会创建以及使用PagePanel item而不是各自使用anchor定位;


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

    //
creating a MarkerPanel item

    MarkerPanel
{

        id:
markerPanel

        width:
50

        anchors.topMargin:
20

        anchors
{

            right:
window.right

            top:
window.top

            bottom:
window.bottom

        }

    }

//...

    //
creating a PagePanel item

    PagePanel
{

        id:
pagePanel

        //
binding the state of PagePanel to the

        //
activeMarker property of MarkerPanel

        state:
markerPanel.activeMarker

        anchors
{

            right:
markerPanel.left

            left:
toolbar.right

            top:
parent.top

            bottom:
parent.bottom

            leftMargin:
1

            rightMargin:
-50

            topMargin:
3

            bottomMargin:
15

        }

    }

上面代码中, 我们可以看到QML的 property binding特性, 可以把state属性和activeMarker属性绑定起来; 这样不论activeMarker通过用户操作获得了什么值, 相同的值会被分配给PagePanel的state属性, 这样就能开关各个page的可见性了;

下一步

给出怎样使用使用图形来强化UI的细节;

3.3 添加graphics(图形)

因为QML的特性, 开发和设计完全可以一起紧密工作, 贯彻整个开发生命期; 如今, 使用graphics让用户体验有了很大的不同, 这也是程序让用户感受到的地方;

QML鼓励在UI实现过程中尽可能地使用graphics; 使用QML让图形设计和开发之间的协作更容易, 设计可以立刻在基本的UI元素上测试graphics; 这帮助设计来理解在开发新的组件时, 程序员会需要什么, 这也让程序的UI更有吸引力而且某种程度上更易维护;

3.3.1 给组件设置背景图片

BorderImage类型推荐使用的情况是: 在你想要把一个图片scale(按比例拉伸), 但它的border(边界)保持不变的时候; 这种类型的一个好的用例(use case)是在QML item上有阴影效果的背景图片; 你的item可能会在某些时刻scale但是需要保持corners(四角)不变;

来看下怎样在组件中将BorderImage设置成背景;

// PagePanel.qml


1

2

3

4

5

6

7

8

9

10

11

//...

    BorderImage
{

        id:
background

        //
filling the entire PagePanel

        anchors.fill:
parent

        source: "images/page.png"

        //
specifying the border margins for each corner,

        //
this info should be given by the designer

        border.left:
68; border.top: 69

        border.right:
40; border.bottom: 80

    }

// Note.qml


1

2

3

4

5

6

7

BorderImage
{

    id:
noteImage

    anchors
{ fill: parent}

    source: "images/personal_note.png"

    border.left:
20; border.top: 20

    border.right:
20; border.bottom: 20

}

Warning: 注意BorderImage类型要以正确的次序在组件里面使用, 因为实现的次序定义了显示的顺序; 具有相同 z值的item显示的次序是按它们被声明的次序决定的; 更多细节参考stack ordering of items-- z property;

当这些item都在MarkerPanel组件中创建的时候, 怎样才是给Marker item设置背景的最佳方案--这个方案已经在MarkerPanel中展现了;

这里有个markerData list, 把它作为model给Repeater来创建Marker item, 当一个marker item被点击的时候, 设置markerid作为activeMarker; 我们可以扩展markerData, 存储一个图像的的url路径, 使用Image类型作为Marker组件的顶层类型;

// Marker.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//
The Image type as top level is convenient

//
as the Marker component simply is a graphical

//
UI with a clicked() signal.

Image
{

    id:
root

    //
declaring the clicked() signal to be used in the MarkerPanel

    signal
clicked()

    //
creating a MouseArea type to intercept the mouse click

    MouseArea
{

        id:
mouseArea

        anchors.fill:
parent

        //
emitting the clicked() signal Marker item

        onClicked:
root.clicked()

    }

}

这样就可以增强MarkerPanel组件;

// MarkerPanel.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

//...

    //
for the markerData, we add the img value pointing to the image url

    property
variant markerData: [

        {
img: 
"images/personalmarker.png",
markerid: 
"personal" },

        {
img: 
"images/funmarker.png",
markerid: 
"fun" },

        {
img: 
"images/workmarker.png",
markerid: 
"work" }

    ]

    Column
{

        id:
layout

        anchors.fill:
parent

        spacing:
5

        Repeater
{

            //
using the defined list as our model

            model:
markerData

            delegate:
Marker {

                id:
marker

                //
binding the source property of Marker to that

                //
of the modelData‘ s img value.

                //
note that the Marker is an Image element

                source:
modelData.img

                //
handling the clicked signal of the Marker item,

                //
setting the currentMarker property

                //
of MarkerPanel based on the clicked Marker

                onClicked:
root.activeMarker = modelData.markerid

            }

        }

    }

上述代码中, 可以看到Marker item的source属性是如何绑定到markerData model的image值的;

我们使用了BorderImage类型来为NoteToolbar组件设置背景, 也作为main.qml的顶层类型;

Note 关于图像的border margins, 以及图像的如何anchor和align(对齐), 要和graphics设计讨论清楚;

MarkerPanel组件看起来是这样的:

然后来看看怎样在原型阶段使用graphics按照设计来增强toolbar;

3.3.2 创建Tool组件

基于代码重用考虑, 定义一个新组件给toolbar中的New Note和Clear All工具使用; 这是为什么我们已经实现了一个Tool组件, 使用Image类型作为顶层类型, 处理鼠标点击事件;

Image类型经常用作UI元素自身, 不论是静态的或是动画图像; 它会按像素布局, 可以很好地按照设计需求来定义;

// Tool.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

//
Use Image as the top level type

Image
{

    id:
root

    //
defining the clicked signal

    signal
clicked()

    //
using a MouseArea type to capture

    //
the mouse click of the user

    MouseArea
{

        anchors.fill:
parent

        //
emitting the clicked() signal of the root item

        onClicked:
root.clicked()

    }

}

现在用Tool组件来创建toolbar; 我们从原型阶段修改代码, 用Tool item代替Rectangle元素;

//main.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

//...

    //
toolbar background

    Rectangle
{

        anchors.fill:
toolbar

        color: "white"

        opacity:
0.15

        radius:
16

        border
{ color: 
"#600";
width: 4 }

    }

    //
using a Column element to layout

    //
the Tool items vertically

    Column
//
sidebar toolbar

        id:
toolbar

        spacing:
16

        anchors
{

            top:
window.top

            left:
window.left

            bottom:
window.bottom

            topMargin:
50

            bottomMargin:
50

            leftMargin:
8

        }

        //
new note tool

        Tool
{

            id:
newNoteTool

            source: "images/add.png"

        }

        //
clear page tool

        Tool
{

            id:
clearAllTool

            source: "images/clear.png"

        }

    }

现在我们给我们的组件设置了所有的graphics, 程序应该有了更吸引人的外观和更多定义好的UI了;

下一步

下一章详细介绍如何动态地创建和管理Note item以及如何在本地数据库存储它们;

---3End---

CHAPTER4 动态管理Note对象

我们目前看到的QML是一个非常强大的声明性(declarative)语言, 和JavaScript组合使用让它更强大; QML不仅提供了 inline JavaScript, 而且还可以把整个JavaScript库导入到文件中;

NoteApp的核心功能是可以让用户去创建, 修改和删除note, 但是程序应该也要自动存储note, 无需提示;

这一章会指导如何使用JavaScript来给QML代码添加逻辑, 实现本地存储--Qt Quick Local Storage

本章主要主题:

- 使用JavaScript实现动态对象管理的功能;

- 如何使用 Qt Quick Database API 进行本地数据存储;

4.1 创建和管理Note Item

用户应该随时可以创建和删除note, 这意味着我们的代码应该可以动态地创建和删除Note item; 有多种方式创建和管理QML对象; 事实上, 我们已经使用一种--Repeater类型; 创建一个QML对象意味着在创建组件的实例之前, 组件必须要被创建和加载起来;

QML对象可以通过 createObject(Item parent, object properties) JavaScript方法在组件上创建; 更多细节参考 Dynamic Object Management in QML http://qt-project.org/doc/qt-5/qtqml-javascript-dynamicobjectcreation.html  

4.1.1 Note对象的动态创建

我们知道一个Note item是属于Page组件的, 它负责note对象的创建, 也会从数据库中读取笔记; 

如前面所说, 首先加载Note组件:

// Page.qml


1

2

3

4

5

6

//...

    //
loading the Note Component

    Component
{

        id:
noteComponent

        Note
{ }

    }

现在我们来定义一个Javascript方法, 创建QML Note对象; 创建QML对象的时候, 必须保证一个参数是这个对象的parent; 在Page组件中持有一个Note item容器(container)是个管理note对象的好主意, 这样我们可以在数据库中保存这些note;

// Page.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

//...

    //
creating an Item element that will be used as a note container

    Item
{ id: container }

    //
a Javascript helper function for creating QML Note objects

    function newNoteObject(args)
{

        //
calling the createObject() function on noteComponent item

        //
and the container item will be the parent of the new

        //
object and args as the set of arguments

        var note
= noteComponent.createObject(container, args)

        if(note
== 
null)

            console.log("note
object failed to be created!"
)

    }

在前面显示的代码中, 我们看到一个新的 note item对象是怎样在 newNoteObject()方法中创建的; 新建的 note对象隶属于container item;

现在我们要在toolbar上的new note tool被按下的时候调用这个方法, toolbar在main.qml中; 由于PagePanel组件知道当前可见的page item, 我们可以在PagePanel中创建一个新的属性来存储那个page;

// PagePanel.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

//...

    //
this property holds the current visible page

    property
Page currentPage: personalpage

    //
creating the list of states

    states:
[

        //
creating a state item with its corresponding name

        State
{

            name: "personal"

            PropertyChanges
{

                target:
personalpage

                opacity:1.0

                restoreEntryValues: true

            }

            PropertyChanges
{

                target:
root

                currentPage:
personalpage

                explicit: true

            }

        },

        State
{

            name: "fun"

            PropertyChanges
{

                target:
funpage

                opacity:1.0

                restoreEntryValues: true

            }

            PropertyChanges
{

                target:
root

                currentPage:
funpage

                explicit: true

            }

        },

        State
{

            name: "work"

            PropertyChanges
{

                target:
workpage

                opacity:1.0

                restoreEntryValues: true

            }

            PropertyChanges
{

                target:
root

                currentPage:
workpage

                explicit: true

            }

        }

    ]

我们修改了三个state来给currentPage属性设置合适的值;

在main.qml中, 看看在new note tool被点击的时候怎样调用方法来创建新的note对象;

// main.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//
using a Column element to layout the Tool items vertically

Column
{

    id:
toolbar

    spacing:
16

    anchors
{

        top:
window.top; left: window.left; bottom: window.bottom

        topMargin:
50; bottomMargin: 50; leftMargin: 8

    }

    //
new note tool, also known as the plus icon

    Tool
{

        id:
newNoteTool

        source: "images/add.png"

        //
using the currentPage property of PagePanel and

        //
calling newNoteObject() function without any arguments.

        onClicked:
pagePanel.currentPage.newNoteObject()

    }

4.1.2 删除Note对象

删除Note对象是个更直接的过程, 因为QML item类型提供了一个JavaScipt方法--destroy() http://qt-project.org/doc/qt-5/qtqml-javascript-dynamicobjectcreation.html#deleting-objects-dynamically 
; 因为我们已经有一个container item的children是Note item, 我们可以简单地对children逐个地调用 destroy;

在Page组件上, 定义一个方法来为我们执行操作:

// Page.qml


1

2

3

4

5

6

7

//...

    //
a JavaScript helper function for iterating through the children elements of the

    //
container item and calls destroy() for deleting them

    function clear()
{

        for(var i=0;
i<container.children.length; ++i)

            container.children[i].destroy()

    }

在main.qml文件中, 我们在clear tool被按下时调用 clear()方法:


1

2

3

4

5

6

7

//...

        //
the clear tool

        Tool
{

            id:
clearAllTool

            source: "images/clear.png"

            onClicked:
pagePanel.currentPage.clear()

        }

为了让用户可以独立地删除每一个note, 我们在NoteToolbar组件中为Note组件添加了一个tool; 可以使用签名实现的Tool组件:

// Note.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//...

    //
creating a NoteToolbar that will be anchored to its parent

    NoteToolbar
{

        id:
toolbar

        height:
40

        anchors
{ top: root.top; left: root.left; right: root.right }

        //
using the drag property alias to set the drag.target to our Note item.

        drag.target:
root

        //
creating the delete  tool for deleting the note item

        Tool
{

            id:
deleteItem

            source: "images/delete.png"

            onClicked:
root.destroy()

        }

    }

下一步

关于如何在本地数据库存储note item的详细步骤;

4.2 从数据库存储和读取数据

目前我们实现了NoteApp的功能: 实时地创建和管理note item;

这里我们会了解在本地数据库存储note的详细实现; QML提供了一个简单的 Qt Quick Local Storage API, 使用SQLite数据库来实现我们想要的功能;

首选的方式是在NoteApp程序启动的时候从数据库读取note, 然后在程序关闭的是保存它们; 用户不会收到提示;

4.2.1 定义数据库

NoteApp的数据库很简单; 它只有一个table--note table, 包含我们所保存的note的信息;

看一下Table的定义, 让我们了解下Note组件的哪些属性或哪些新的数据应该被引入:

x和y是每个QML item都有的几何属性; 从Note item获得这些值很简单; 这些值会用来粗糙你note在page中的位置; noteText是note的实际文字, 我们可以从Note组件中的Text元素中获取它们, 我们应该定义一个alias(别名_)--noteText; noteId和markerId是每个note item都该有的标识符; noteId是一个唯一的标识符, 数据库需要用到, markerId用来标识note item属于哪一个page; 因此我们会在Note组件里面添加两个新的属性;

// Note.qml


1

2

3

4

5

6

7

8

9

10

11

12

Item
{

    id:
root

    width:
200; height: 200

    property
string markerId;

    property
int  noteId;

    property
alias noteText: editArea.text

//...

    //
creating a TextEdit

    TextEdit
{

        id:
editArea

//...

考虑到Page组件负责创建Note item, 它应该也知道Note和哪个markerId相关联; 当一个新的Note item创建出来(不是从数据库读取), Page应该设置好Note的markerId属性;

使用一个JavaScript的 helper方法:

// Page.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Item
{

    id:
root

    //
this property is held for helping store

    //
the note items in the database

    property
string markerId

    //
this Javascript helper function is used to create,

    //
Note items not loaded from database so that it will set

    //
the markerId property of the note.

    function newNote()
{

        //
calling the newNoteObject and passing the a set of

        //
arguments where the markerId is set.

        newNoteObject(
"markerId":
root.markerId } )

    }

//...

先前在main.qml中, 我们使用了 newNoteObject()方法, 但就如前面解释的, 我们需要用newNote()方法代替它来达到目的;

现在每个Page组件有个markerId属性, 可以在Note item被创建的时候设置markerId; 我们必须保证page的markerId属性在Page item在PagePanel组件中创建的时候就被设置了;

// PagePanel.qml


1

2

3

4

5

//...

//
creating three Page items that are anchored to fill the parent

Page
{ id: personalpage; anchors.fill: parent; markerId: 
"personal" }

Page
{ id: funpage; anchors.fill: parent; markerId: 
"fun" }

Page
{ id: workpage; anchors.fill: parent; markerId: 
"work" }

目前我们保证的:

- 从对应的关系数据库[relational database http://en.wikipedia.org/wiki/Relational_database ]得到的note和page之间的关系是正确的;

- 每个Note item有一个唯一的ID, ID属于page, 可以识别marker ID;

- 这些属性值要被正确设置;

下面来读取和存储笔记;

---TBC---

时间: 2024-08-26 05:38:24

QtQuick桌面应用开发指导 3)实现UI和功能_B 4)动态管理Note对象_A的相关文章

QtQuick桌面应用程序开发指导 3)达到UI而功能_B 4)动态管理Note物_A

3.2 把Page Item和Marker Item绑定 之前我们实现了PagePanel组件, 使用了三个state来切换Page组件的opacity属性; 这一步我们会使用Marker和MarkerPanel组件来实现页面导航; 在原型阶段, MarkerPanel组件十分简单, 没有不论什么功能; 它使用了Repeater类型来产生三个QML Item以及Marker组件作为delegate; MarkerPanel应该存储当前激活的marker(标记), 即那个被用户点击的marker;

QtQuick桌面应用开发指导 1)关于教程 2)原型和设计 3)实现UI和功能_A

Release1.0 http://qt-project.org/wiki/developer-guides Qt Quick Application Developer Guide for Desktop 这个教程的目的是让你熟悉使用QtQuick构建QML程序的最佳编程实践方法; 先决条件: 对QML有相当的理解, 相关阅读: <qtquick/qtquick-applicationdevelopers.html>; 本教程会涉及QML开发最佳实践的各个方面, 以及在典型的桌面环境下部署应

QtQuick桌面应用开发指导 7)创建应用 8)扩展

CHAPTER7 部署NotApp应用 现在我们要让程序在典型的桌面环境中可用, 可部署; 如第一章所描述, 我们在QtCreator中使用QtQuick UI项目开发NoteApp程序; 这意味着qmlscene用来加载main.qml, 随之让NoteApp运行; 首先, 让NoteApp可用的最简单方案是创建一个package(包)将所有qml文件, qmlscense和一个可以运行qmlscense加载main.qml的简单脚本bundle(捆扎)起来; 你需要参考每一个桌面平台, 了解

QtQuick桌面应用开发指导 4)动态管理Note对象_B 5)外观加强 6)更多改进

4.2.2 Stateless(状态无关的)JavaScript库 为了让开发轻松点, 使用一个JavaScript接口来和数据库交互是个好主意, 它在QML中提供了方便的方法; 在QtCreator中创建一个新的JavaScript文件 noteDB.js, 保证选择了 State Library选项; 这样使得noteDB.js用起来像一个库, 提供了stateless的helper方法; 这样,在每个QML组件 import noteDB.js以及使用它的时候, 就只有一份实例被加载和使用

nw.js桌面软件开发系列 第0.1节 HTML5和桌面软件开发的碰撞

第0.1节 HTML5和桌面软件开发的碰撞 当我们谈论桌面软件开发技术的时候,你会想到什么?如果不对技术本身进行更为深入的探讨,在我的世界里,有这么多技术概念可以被罗列出来(请原谅我本质上是一个Windows程序员的事实). 操作系统 API.操作系统发展到今日,几乎桌面应用的所有功能,都是基于系统API构建的.调用API和语言及技术无关,哪怕是使用汇编.例如(代码来源于网络,本地重新编译): ;我的第一个win32汇编程序 ;一个经典的hello world !程序 ;>>>>&

python3+PyQt5+pycharm桌面GUI开发环境搭建

python3+PyQt5+pycharm桌面GUI开发环境搭建 一.安装python PyQt5所支持的python版本是不低于3.5版本 python3.5以上的版本安装:https://www.python.org/downloads/windows/ 二.安装PyQt5 pip install PyQt5 pip install PyQt5-tools 三.配置pycharm 官网下载安装pycharm:https://www.jetbrains.com/pycharm/ 1,点击:Fi

.Net Core 3 骚操作 之 用 Windows 桌面应用开发 Asp.Net Core 网站

前言 曾经在开发 Asp.Net 网站时就在想,为什么一定要把网站挂到 IIS 上?网站项目的 Main 函数哪儿去了?后来才知道这个 Main 函数在 w3wp.exe 里,这也是 IIS 的主进程.Asp.Net 网站的命门被 IIS 捏着,我无力改变.有时需要临时搭建一个简单的 Web 服务器,去网上一通度娘,发现了 MyWebServer.HFS (HTTP File Server) 等简单实用的 Web 服务器,而且它们还是桌面应用.我就在想,什么时候能用 .Net 来开发一个桌面 W

Jetty开发指导:Maven和Jetty

使用Maven Apache Maven是一种软件项目管理和综合工具.基于项目对象模型(POM)的概念,Maven能从核心信息管理一个项目的构建.报告和文档. 他是用于构建一个web应用项目的理想工具,这些项目能用jetty-maven-plugin轻松的运行web应用,从而节省开发时间.你也能用Maven构建.测试和运行一个嵌入Jetty的项目. 首先我们将看一个很简单嵌入Jetty的HelloWorld Java应用,然后看一个简单的webapp怎么使用jetty-maven-plugin加

桌面程序开发入门(WinForm with C#)

1.使用Visual Studio 2013创建新项目 2.创建一个主窗体和4个子窗体 3.创建一个数据库.一个表.一个存储过程 4.在配置文件里添加数据库连接字符串 5.真正的编码工作. 第一步:创建新项目,一张图片抵一万字!看图说话,呵呵.. 第二步:创建4个子窗体,名称分别是formCommandProc.cs.formDataAdapterProc.cs.formXMLCreate.cs.formXMLRead.cs. 三.创建一个数据库.一个表.一个存储过程.打开MS SQL Serv