第 5 章 **** 用户互操作:提示和选择
背景 ****
提示通常包含一个描述性信息,伴随一个停止以让用户理解所给的信息并输入数据。数据可以通过多种方式被输入,如通过命令行、对话框或AutoCAD编辑窗口。给出的提示要遵循一定的格式,格式要与一般的AutoCAD提示相一致,这一点是非常重要的。例如,关键字要用“/”号分隔并放在方括号“[]”中,缺省值要放在“<>”内。对于一个AutoCAD用户来说,坚持统一的格式将会减少信息理解错误的产生。
当用户在AutoCAD命令行中选择一个实体时,实体是使用选择机制被选择的。这种机制包括一个提示,用来让用户知道选择什么并怎样选择(如,窗口或单一实体),然后是一个停顿。
试一下诸如PINE这种命令来看一下提示的显示,PEDIT来看一下使用单一实体或多线来进行选择。
**练习 **
**Prompts : **
**提示: **
在本章中,我们将提示输入雇员名字、职位、薪水和部门来创建一个雇员块索引对象。如果输入的部门不存在,我们将提示输入部门经理的名字来创建一个新的部门。在我们继续之前,让我们试着重用以前的代码。
为了进行选择,我们将提示用户在一个窗口中进行选择或选择一个实体,而我们只显示选择集中的雇员对象。
在前面的章节中,我们创建了一个名叫“Earnest Shackleton”的雇员,名字被存储为“EmployeeBlock”块定义(块表记录)中的MText。如果我们多次插入这个块,那么我们看到的都是同一个雇员的名字。我们怎样才能自定义这个块以使每次插入这个块的时候显示不同雇员的名字?这就要使用块属性的功能了。属性是存储在每一个块索引实例中的文本,并被作为实例的一部分来被显示。属性从存储在块表记录中的属性定义中继承相关的属性。
**属性: **
让我们来把MText实体类型改变为属性定义。在CreateEmployeeDefinition()函数中,把下面的代码替换
‘ 文本:
Dim text As MText = New MText()
text.Contents = "Earnest Shackleton"
text.Location = center
为
'属性定义
Dim text As AttributeDefinition = New AttributeDefinition(center, "NoName", "Name:", "Enter Name", db.Textstyle)
text.ColorIndex = 2
试着使用TEST命令来测试一下CreateEmployeeDefinition()函数:
1<commandmethod("test")> _
2
3Public Function Test()
4
5CreateEmployeeDefinition()
6
7End Function
8
9你现在应该可以使用INSERT命令来插入EmployeeBlock块并对每一个实例确定一个雇员名。
10
11当你插入Employee块时,请注意一下块插入的位置。它是正好被放置在所选点还是有些偏移?试试怎样修复它。(提示:检查块定义中的圆心)
12
13**_修改CreateEmployee ()_ ** **_以重用_ **
14
15**1)** 让我们来修改CreateEmployee()函数,以让它可以接收名字、薪水、部门和职位并返回创建的雇员块索引的ObjectId。函数的形式如下(你可以改变参数顺序)
16
17Public Function CreateEmployee(ByVal name As String, ByVal division As String, ByVal salary As Double, ByVal pos As Point3d) as ObjectId
18
19**2)** 移除上面函数中的CommandMethod属性”CREATE”,这样它就不再是用来创建雇员的命令。
20
21**3)** 修改函数的代码,这样就可以正确地设置块索引的名字、职位、部门和薪水和它的扩展字典。
22
23 * 替换
24
25
26
27Dim br As New BlockReference(New Point3d(10, 10, 0), CreateEmployeeDefinition())
28
29为
30
31Dim br As New BlockReference(pos, CreateEmployeeDefinition())
32
33 * 替换
34
35
36
37xRec.Data = New ResultBuffer( _
38
39New TypedValue(DxfCode.Text, "Earnest Shackleton"), _
40
41New TypedValue(DxfCode.Real, 72000), _
42
43New TypedValue(DxfCode.Text, "Sales"))
44
45为
46
47xRec.Data = New ResultBuffer( _
48
49New TypedValue(DxfCode.Text, name), _
50
51New TypedValue(DxfCode.Real, salary), _
52
53New TypedValue(DxfCode.Text, division))
54
55**4)** 因为我们把雇员的名字从MText替换成块的属性定义,因此我们要创建一个相应的属性索引来显示雇员的名字。属性索引将使用属性定义的属性。
56
57 * 替换:
58
59
60
61btr.AppendEntity(br) '加入索引到模型空间
62
63trans.AddNewlyCreatedDBObject(br, True) '让事务处理知道
64
65为
66
67Dim attRef As AttributeReference = New AttributeReference()
68
69'遍历雇员块来查找属性定义
70
71Dim empBtr As BlockTableRecord = trans.GetObject(bt("EmployeeBlock"), OpenMode.ForRead)
72
73Dim id As ObjectId
74
75For Each id In empBtr
76
77Dim ent As Entity = trans.GetObject(id, OpenMode.ForRead, False) '打开当前的对象!
78
79If TypeOf ent Is AttributeDefinition Then '
80
81'设置属性为属性索引中的属性定义
82
83Dim attDef As AttributeDefinition = CType(ent, AttributeDefinition)
84
85attRef.SetPropertiesFrom(attDef)
86
87attRef.Position = New Point3d(attDef.Position.X + br.Position.X, _
88
89attDef.Position.Y + br.Position.Y, _
90
91attDef.Position.Z + br.Position.Z)
92
93attRef.Height = attDef.Height
94
95attRef.Rotation = attDef.Rotation
96
97attRef.Tag = attDef.Tag
98
99attRef.TextString = name
100
101End If
102
103Next
104
105btr.AppendEntity(br) '把索引加入模型空间
106
107'把属性索引加入到块索引
108
109br.AttributeCollection.AppendAttribute(attRef)
110
111'让事务处理知道
112
113trans.AddNewlyCreatedDBObject(attRef, True)
114
115trans.AddNewlyCreatedDBObject(br, True)
116
117研究一下上面的代码,看看是怎样把属性定义中除显示用的文本字符串外的属性复制到属性索引的。属性被加入到块索引的属性集合中。这就是你怎样来为每一个实例自定义雇员名字。
118
119**5)** 不要忘记返回雇员块索引的ObjectId,但要在提交事务处理之后才能返回:
120
121trans.Commit()
122
123Return br.ObjectId
124
1256) 测试CreateEmployee。
126
127加入一个Test命令来测试CreateEmployee:
128
129<commandmethod("test")> _
130
131Public Function Test()
132
133CreateEmployee("Earnest Shackleton", "Sales", 10000, New Point3d(10, 10, 0))
134
135End Function
136
137**_修改CreateDivision()_ ** **_以重用_ ** **_:_ ** **__ **
138
139让我们来修改CreateDivision ()函数,以让它可以接收部门名字、经理名字并返回创建的部门经理扩展记录的ObjectId。如果部门经理已经存在,则不改变经理的名字。
140
1411) 如果你先前在CreateEmployeeDefinition()中调用了CreateDivision(),请把它注释掉,因为我们在这里不需要创建一个部门
142
143**2)** 改变CreateDivision()的形式让它接收部门和经理的名字并返回一个ObjectId。
144
145Public Function CreateDivision(ByVal division As String, ByVal manager As String) As ObjectId
146
1473) 修改上面函数的代码创建部门的名字和经理:
148
149 * 替换:
150
151
152
153divDict = trans.GetObject(acmeDict.GetAt("Sales"), OpenMode.ForWrite)
154
155为:
156
157divDict = trans.GetObject(acmeDict.GetAt(division), OpenMode.ForWrite)
158
159 * 替换:
160
161
162
163acmeDict.SetAt("Sales", divDict)
164
165为:
166
167acmeDict.SetAt(division, divDict)
168
169 * 替换:
170
171
172
173mgrXRec.Data = New ResultBuffer(New TypedValue(DxfCode.Text, "Randolph P. Brokwell"))
174
175为
176
177mgrXRec.Data = New ResultBuffer(New TypedValue(DxfCode.Text, manager))
178
179不要忘了返回部门经理这个扩展记录的ObjectId,但要在提交事务处理后才返回。
180
181trans . Commit ()
182
183'返回部门经理这个扩展记录的ObjectId
184
185Return mgrXRec.ObjectId
186
187现在把在中CreateEmployeeDefinition调用的CreateDivision函数给注释掉。
188
1894) 现在通过使用TEST命令来测试调用CreateDivision函数。使用ArxDbg工具来检查条目是否已被加入到“ACME_DIVISION”下的命名对象字典。
190
191CreateDivision("Sales", "Randolph P. Brokwell")
192
193**_使用CREATE命令来创建雇员_ ** **_:_ **
194
195我们将加入一个名为CREATE的新命令,此命令用来提示输入雇员的详细资料来创建雇员块索引。让我们来看一下这个命令是怎样使用的。
196
1971) 让我们加入一个名为CREATE的新命令,并声明几个常用的变量和一个try-finally块。
198
199<commandmethod("create")> _
200
201Public Sub CreateEmployee()
202
203Dim db = HostApplicationServices.WorkingDatabase
204
205Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
206
207Dim trans As Transaction = db.TransactionManager.StartTransaction()
208
209Try
210
211trans.Commit()
212
213Finally
214
215trans.Dispose()
216
217End Try
218
219End Sub
220
2212) 让我们来为雇员定义可以用作为提示缺省值的常数。注意,布尔值gotPosition是用来判断用户是否已输入 **职位** 。
222
223. 雇员名 - 类型 :String -缺省值 “Earnest Shackleton”
224
225. 雇员所在部门名 - 类型:String -缺省值“Sales”
226
227. 薪水 -类型:Double (non-negative and not zero) -缺省值10000
228
229. **职位** -类型:Point3d -缺省值(0,0,0)
230
231把这些常数加入到try语句后面:
232
233Dim empName As New String("Earnest Shackleton")
234
235Dim divName As New String("Sales")
236
237Dim salary As New Double() : salary = 10000
238
239Dim position As New Point3d(0, 0, 0)
240
241'布尔值用来判断用户是否已输入 **职位**
242
243Dim gotPosition As New Boolean() : gotPosition = False ~~~~
244
2453) 现在让我们提示用户输入值。我们先使用PromptXXXOptions类来初始化要显示的提示字符串。
246
247'提示输入每个雇员的详细资料
248
249Dim prName As PromptStringOptions = New PromptStringOptions("Enter Employee Name <" & empName & ">")
250
251Dim prDiv As PromptStringOptions = New PromptStringOptions("Enter Employee Division <" & divName & ">")
252
253Dim prSal As PromptDoubleOptions = New PromptDoubleOptions("Enter Employee Salary <" & salary & ">")
254
255Dim prPos As PromptPointOptions = New PromptPointOptions("Enter Employee Position or")
256
257注意,提示字符串用尖括号来显示变量的值。这是AutoCAD用来提示用户这个值为缺省值。
258
2594) 当提示用户输入 **职位** 时,我们也提供了一个关键字列表选项,如名字、部门和薪水。如果用户想要在选择一个点的时候改变为其它值,他可以选择那个关键字。
260
261一个命令提示的例子如下:
262
263Command: CREATE
264
265Enter Employee Position or [Name/Division/Salary]:
266
267要创建一个雇员,用户会选择一个点而其它的值被设置为缺省值。如果用户要改变其它的值,如名字,他可以输入”N”或全名”Name”,然后输入名字:
268
269Command: CREATE
270
271Enter Employee Position or [Name/Division/Salary]:N
272
273Enter Employee Name <earnest shackleton="">:
274
275如果用户想要再次选择缺省的名字,他可以按回车键。
276
277让我们创建用于 **职位** 提示的关键字列表:
278
279'加入用于 **职位** 提示的关键字
280
281prPos.Keywords.Add("Name")
282
283prPos.Keywords.Add("Division")
284
285prPos.Keywords.Add("Salary")
286
287'设置提示的限制条件
288
289prPos.AllowNone = False '不允许没有值
290
2915) 现在让我们声明PromptXXXResult变量来获取提示的结果:
292
293'prompt results
294
295Dim prNameRes As PromptResult
296
297Dim prDivRes As PromptResult
298
299Dim prSalRes As PromptDoubleResult
300
301Dim prPosRes As PromptPointResult
302
3036) 直到用户成功输入一个点后,循环才结束。如果输入错误的话,我们会提示用户并退出函数:
304
305判断用户是否输入了关键字,我们通过检查promptresult的状态来进行:
306
307'循环用来获取雇员的详细资料。当 **职位** 被输入后,循环终止。
308
309While (Not (gotPosition))
310
311'提示输入 **职位**
312
313prPosRes = ed.GetPoint(prPos)
314
315'取得一个点
316
317If prPosRes.Status = PromptStatus.OK Then
318
319gotPosition = True
320
321position = prPosRes.Value
322
323ElseIf prPosRes.Status = PromptStatus.Keyword Then '获取一个关键字
324
325'输入了Name关键字
326
327If prPosRes.StringResult = "Name" Then
328
329'获取雇员名字
330
331prName.AllowSpaces = True
332
333prNameRes = ed.GetString(prName)
334
335If prNameRes.Status <> PromptStatus.OK Then
336
337Return
338
339End If
340
341'如果获取雇员名字成功
342
343If prNameRes.StringResult <> "" Then
344
345empName = prNameRes.StringResult
346
347End If
348
349End If
350
351Else
352
353'获取 **职位** 时发生错误
354
355ed.WriteMessage("***Error in getting a point, exiting!!***" + vbCrLf)
356
357Return
358
359End If '如果获取一个点
360
361End While
362
3637) 上面的代码只提示输入名字,请加入提示输入薪水和部门的代码。
364
3658) 完成提示输入后,我们将使用获得的值来创建雇员。
366
367'创建雇员
368
369CreateEmployee(empName, divName, salary, position)
370
3719) 现在来检查部门经理是否已存在。我们通过检查NOD中部门的扩展记录中的经理名字来进行。如果检查到的是一个空字符串,那么我们会提示用户输入经理的名字。注意,通过修改CreateDivision()函数,获取经理的名字变得简单了。
372
373Dim manager As String = New String("")
374
375'创建部门
376
377'给经理传入一个空字符串来检查它是否已存在
378
379Dim depMgrXRec As Xrecord
380
381Dim xRecId As ObjectId
382
383xRecId = CreateDivision(divName, manager)
384
385'打开部门经理扩展记录
386
387depMgrXRec = trans.GetObject(xRecId, OpenMode.ForRead)
388
389Dim val As TypedValue
390
391For Each val In depMgrXRec.Data
392
393Dim str As String
394
395str = val.Value
396
397If str = "" Then
398
399'经理没有被设置,现在设置它
400
401'先提示输入经理的名字
402
403ed.WriteMessage(vbCrLf)
404
405Dim prManagerName As PromptStringOptions = New PromptStringOptions("No manager set for the division! Enter Manager Name")
406
407prManagerName.AllowSpaces = True
408
409Dim prManagerNameRes As PromptResult = ed.GetString(prManagerName)
410
411If prManagerNameRes.Status <> PromptStatus.OK Then
412
413Return
414
415End If
416
417'设置经理的名字
418
419depMgrXRec.Data = New ResultBuffer(New TypedValue(DxfCode.Text, prManagerNameRes.StringResult))
420
421End If
422
423Next
424
42510) 测试CREATE命令
426
427**_选择集:_ **
428
429现在让我们来创建一个命令,当用户在图形中选择一个雇员对象时,它会显示雇员的详细资料。
430
431我们会使用上一章中创建的ListEmployee()函数在命令行中输出雇员的详细资料。
432
433下面是你必须遵循的步骤:
434
435 1. 调用“LISTEMPLOYEES”命令
436
437
438 2. 调用Editor的GetSelection()函数来选择实体
439
440
441
442Dim res As PromptSelectionResult = ed.GetSelection(Opts, filter)
443
444 3. 上面的filter用来过滤选择集中的块索引。你可以创建如下的过滤列表:
445
446
447
448Dim filList() As TypedValue = {New TypedValue(DxfCode.Start, "INSERT")}
449
450Dim filter As SelectionFilter = New SelectionFilter(filList)
451
452 4. 从选择集中获取ObjectId数组:
453
454
455
456'如果选择失败则什么也不做
457
458If Not res.Status = PromptStatus.OK Then Return
459
460Dim SS As Autodesk.AutoCAD.EditorInput.SelectionSet = res.Value
461
462Dim idArray As ObjectId() = SS.GetObjectIds()
463
4645\. 最后,把选择集中的每个ObjectId输入到ListEmployee()函数来获取一个雇员详细资料的字符串数组。把雇员的详细资料输出到命令行。例如:
465
466'获取saEmployeeList 数组中的所有雇员
467
468For Each employeeId In idArray
469
470ListEmployee(employeeId, saEmployeeList)
471
472'把雇员的详细资料输出到命令行
473
474Dim employeeDetail As String
475
476For Each employeeDetail In saEmployeeList
477
478ed.WriteMessage(employeeDetail)
479
480Next
481
482ed.WriteMessage("----------------------" + vbCrLf)
483
484Next</earnest></commandmethod("create")></commandmethod("test")></commandmethod("test")>