从压缩包编程 Gambas/表单排列
GridView 和 TableView 通常在包含它们的窗口调整大小后进行拉伸和收缩。表单(即窗口)知道如何调整大小并排列其包含的控件。即使在表单打开时,任何可以排列和扩展的控件都会被扩展。
创建一个带有一个 TableView 的小型表单。
将表单的 *arrangement* 属性设置为 Vertical。将 tableview 的 *expand* 属性设置为 True。运行程序 (f5)。更改窗口大小,并注意 tableview 的大小如何随之变化。
将表单的 *padding* 属性调整为 8。再次运行程序 (F5)。TableView 和表单边缘之间的空间增加了。表单内部的边距即为填充。表格单元格也具有填充。
在 tableview 下方添加一个按钮。再次运行程序。将按钮移动到 tableview 的一侧。再次运行程序。将 arrangement 属性更改为 Horizontal 并再次尝试。将其更改回 Vertical。
删除按钮。添加一个 HBox。在 HBox 中添加一个按钮。(或者,右键单击按钮并选择“*嵌入到容器中*”,然后右键单击容器(它是一个面板)并*更改为...* HBox。)
HBox 中的控件水平排列。
弹簧将按钮尽可能地推到右边。尝试使用和不使用弹簧。
如果你使按钮变宽或变窄,它将保持你在 HBox 中给定的宽度。
按钮垂直扩展以从上到下填充 HBox。更改 HBox 的高度并观察。HBox 扩展其控件以填充其高度。在面板中,按钮保持相同的高度,但你不能使用弹簧。
*tv1* 是一个 TableView。它的 *expand* 属性设置为 True。
底部是一个 HBox。从左到右,里面是一个标签 *labC*、一个粗体标签 *labAverage*、一个弹簧和一个名为 *bQuit* 的退出按钮,其文本属性为“退出”。
每输入一个数字,就会重新计算平均值。空白单元格会被跳过。
*LabC* 显示计数,*labAverage* 显示平均值。
Public Names As New String[]
Public Scores As New Float[]
Public Sub bQuit_Click()
Quit
End
Public Sub Form_Open()
Names = ["Mereka AIKE", "Ernest AIRI", "John AME", "Stanley ANTHONY", "Natasha AUA", "Veronica AUFA", "John Taylor BUNA", "Romrick CLEMENT", "Philomena GAVIA", "Richard GHAM"]
tv1.Rows.Count = Names.count
Scores.Resize(Names.Count)
tv1.Columns.Count = 2
tv1.Columns[1].Alignment = Align.Right
End
Public Sub CalculateAverage()
Dim i, n As Integer
Dim t As Float
For i = 0 To Scores.Max
If Scores[i] = -1 Or IsNull(tv1[i, 1].text) Then Continue 'skip new but unfilled-in lines
n += 1 'number of scores
t += Scores[i] 'total
Next
If n = 0 Then Return
labC.Text = "N= " & n
labAverage.Text = "Avg= " & Format(t / n, "#0.00")
End
Public Sub tv1_Click()
If tv1.Column = 1 Then tv1.Edit 'numbers column is editable; Enter goes down
End
Public Sub tv1_Activate() 'double-clicked a cell
If tv1.Column = 0 Then tv1.Edit 'edit a name
End
Public Sub tv1_Save(Row As Integer, Column As Integer, Value As String)
tv1[Row, Column].text = Value
If Column = 1 Then
Scores[Row] = Value
CalculateAverage
Else
Names[Row] = Value
Endif
End
Public Sub tv1_Data(Row As Integer, Column As Integer)
If Column = 0 Then tv1[row, 0].text = Names[Row]
If Row Mod 2 = 0 Then tv1[Row, Column].Background = &hDDDDFF 'light blue
tv1.Columns[0].Width = -1 'Automatically set width based on contents
End
Public Sub tv1_Insert()
tv1.Rows.Count += 1
Scores.Add(-1)
Names.Add("")
tv1.MoveTo(tv1.Rows.max, 0)
tv1.Edit
End
一开始就创建了两个公共数组。*Names[]* 是一个学生姓名的列表。*Scores[]* 是他们的成绩列表。它们匹配:第一个分数对应第一个姓名,第二个分数对应第二个姓名,依此类推。
*tv1_Data(Row As Integer, Column As Integer)* 事件在每次需要重绘单元格时触发。它为你提供了 Row 和 Column。可以把它看作是绘制单元格。
*_DATA* 事件有一个特殊考虑因素:它不会绘制不需要绘制的单元格。这对于显示大量行非常有用。如果有 100,000 行而你只显示了 15 行,那么只有这 15 行上的单元格才会触发 *_DATA* 事件,而不是所有的十万行。如果你使用 *_DATA* 事件将数字放入单元格,请注意!可能只有这 100,000 个单元格中的 15 个单元格中有数据。在这里没有问题,因为我们自己输入数字,并且每次完成输入一个新数字后,它都会在 *_SAVE* 事件中被放入单元格中。(*tv1[Row, Column].text = Valu**e**)。但是,当我们从数据库中使用 **_***DATA* 事件放置值时,我们只会将数据放入我们看到的单元格中。然后我们必须记住对内部保存的数据进行计算,而不是对单元格的显示内容进行计算。为了养成良好的习惯,我使用 *DATA[ ]* 数组来保存分数,并在计算平均值时使用它。归根结底是:*如果你确定所有数据都在单元格中,请使用它们;如果不是,请使用你确信有数据的地方。*
以下几行创建了 TableView 的上下文菜单,包含四个条目
Public Sub tv1_Menu()
Dim mn, su As Menu 'main menu and submenu
mn = New Menu(Me) 'brackets contain the parent, the main window
su = New Menu(mn) As "MenuCopyTable" 'submenu of mn; alias is MenuCopyTable
su.Text = "Copy table..." 'first submenu's text
su = New Menu(mn) As "MenuCopyNames"
su.Text = "Copy names..." 'second submenu's text
su = New Menu(mn) As "MenuDeleteRow"
su.Text = "Delete Row" 'third submenu's text
su = New Menu(mn) As "MenuRefresh"
su.Text = "Refresh" 'fourth submenu's text
mn.Popup
End
Public Sub MenuDeleteRow_Click()
Names.Remove(tv1.Row)
Scores.Remove(tv1.Row)
tv1.Rows.Remove(tv1.Row)
End
Public Sub MenuCopyTable_Click() 'clicked the Copy Table menu item
Dim z As String
For i As Integer = 0 To Names.Max
If Scores[i] = -1 Then Continue
z = If(IsNull(z), "", z & gb.NewLine) & Names[i] & gb.Tab & Scores[i]
Next
Clipboard.Copy(z)
Message("Table copied")
End
Public Sub MenuCopyNames_Click() 'clicked the Copy Names menu item
Dim z As String
For i As Integer = 0 To Names.Max
If IsNull(Names[i]) Then Continue
z = If(IsNull(z), "", z & gb.NewLine) & Names[i]
Next
Clipboard.Copy(z)
Message("Names copied")
End
Public Sub MenuRefresh_Click()
tv1.Clear 'clear the data
tv1.Rows.Count = Names.Count 'reset number of rows to match Names[ ]
For i As Integer = 0 To Names.Max
tv1[i, 0].Text = Names[i]
tv1[i, 1].Text = Scores[i]
If i Mod 2 = 0 Then
tv1[i, 0].Background = &hFFDDFF
tv1[i, 1].Background = &hFFDDFF
Endif
Next
End
*_Menu()* 事件属于 *tv1*,即 TableView。当右键单击该对象(以显示菜单)时,该事件就会触发。*_Click()* 事件属于 *TableviewMenu*。那是什么?它是菜单 *su* 所知的别名。别名让人想起穿着黑色风衣的秘密人物,但它只是它所知的名称。
**mn** 和 **su** 只在弹出菜单的持续时间内存在,因为它们在 *_Menu()* 事件中。一旦你单击弹出菜单上的任何项目,该子程序就会结束,*mn* 和 *su* 就会消失。幸运的是,我们已经说过 *su* 也被称为 *TableviewMenu*。菜单本身,当它使用 *New* 创建时,就有了那个名字。因此,菜单收到的任何点击都由 *TableviewMenu_Click()* 处理。
菜单(无论是主菜单、子菜单还是菜单项)都有多个事件、方法和属性。有关详细信息,请查看 Gambas 帮助
你可以告诉菜单关闭、隐藏、弹出、显示或删除。
你可以告诉程序在单击菜单时、隐藏后或显示之前执行操作。通常,在单击菜单时,菜单就会开始工作。
一些属性是布尔值(例如 *enabled*、*checked* 和 *visible*),另一些属性是字符串(例如 *text*、*name*)、图片(*picture*)或变体(即任何类型,*tag*)。
</syntaxhighlight>