使用 Tag 属性 - 第二部分
技巧和窍门
这是本文的两部分中的第二部分。在上篇文章中,我们学习了如何使用 Tag 属性来创建我们自己的微帮助并对其进行自动化。在本文中,我们将更进一步,将 Tag 属性用于其他目的。
随着 Sybase 试图为我们提供更多工具,我曾使用 Tag 属性的项目列表逐年缩减。除了微帮助之外,我最常见的用途是实现工具提示。现在 Sybase 为我们提供了工具提示,而无需我们编写弹出窗口代码。
幸运的是,我不需要长长的项目列表;再多一个就足以向您展示我想要的技术。我想向您展示如何将 Tag 属性用于不止一件事。我们有微帮助,那么其他一些功能,例如自动加粗与控件关联的静态文本,怎么样?
我的意思是,我们可以对其进行设置,以便控件还可以注册 DataWindow 控件中的文本对象,并在控件获得焦点时将其加粗,在控件失去焦点时取消加粗。我们还可以将边框设置为“降低”,当我们的控件获得焦点时,设置为“无边框”,当它失去焦点时。
在我们开始之前,这段代码建立在上个月专栏的代码之上。如果您没有,您应该下载它,或者写信给我 rikbrooks[at]aol.com,我会寄给您。
现在,让这一切生效的魔力在于能够在 DataWindow Tag 上放置多个键/值。这实际上是一个常见需求,因此让我们为此做一个可重用对象。我们可以使用 Datastore 来维护数据,从而获得大量功能。
在我们开始之前,我们先来讲解一下键/值 (Key/Value) 的概念。键/值是对象的对,在本例中是字符串,其中键标识值。INI 文件中的单行是一个很好的例子。您有一个键,例如“DBMS”,还有一个值,例如“Informix”。该行可能是
DBMS=Informix
让我们从几个需求开始
- 我们需要一个对象来保存键和值,以便我们可以将其来回传递给我们的集合。
- 我们需要公开集合中的项目数。
- 我们需要能够将键/值组合添加到我们的集合中。
- 我们必须确定是否允许重复键。
- 我们必须提供一种搜索键并获取值的方法。
还有其他事情我们可以做,特别是如果我们允许重复键。本文是关于 Tag 对象,而不是键/值对象。让我们列出可以添加到键/值中的其他内容,然后我们可以在下一篇文章中介绍它们。
- 我们将需要能够获取任何特定行。
- 我们将需要一个键/值对象,以便我们可以返回一个键/值数组。
- 我们将需要一种将该键/值对象添加到我们的集合中的方法。
- 我们将需要能够返回值的子集,例如第 1-6 行。
- 我们将需要一种方法来获取特定键的所有键/值对(鉴于允许重复键)。
- 我们需要一种方法来通过 first/prev/next/last 功能循环遍历值。
- 我们将需要一种方法来通过 first/prev/next/last 功能循环遍历重复键。
- 我们可以提供一个自定义分隔符。
我们可以提供一种获取值子集的方法。我们可以提供获取特定键所有行的方法,或者一种在对象内对特定键执行 first/prev/next/last 功能的方法。
入门
我不需要在此基础上构建我的库。我们将边做边使用它们,因此我将提醒您,可以通过下载文章附带的代码或写信给我 rikbrooks[at]aol.com 来获取它们。
我们将从我们对象的“主力”,Datastore 开始。我们需要一个 Datastore,其中有两个列,都是字符串。它需要一个外部数据源。我给它们起了名字 as_key char(30) 和 as_value char(100)。我保存的叫做 ds_key_value。
考虑到我们将不得不为程序员提供键/值对。我们过去通过结构来处理这个问题,但多年来,对象是一个更好的选择。让我们创建一个仅用于保存键值的对象。这是一个新的 (Ctrl-N) Pb 对象/自定义类,如图 1 所示。
为什么是自定义类?为什么不是结构?PowerBuilder 仍然支持结构,为什么不使用它呢?
主要原因是,我们真的不知道未来。现在我们可能看不到除了存储两个字符串之外还需要功能的需要,但这可能会改变。有一天我们可能会发现我们想继承它并拥有特定类型的键/值;也许是支持值为二进制对象的那种。你永远不知道会发生什么。让我们继续把它放入对象中。使用对象而不是结构,成本很小,但提供了很大的灵活性。
我称我的对象为 n_cst_key_value
。它有两个实例变量,看起来像这样
N_cst_key_value instance variables private string is_key = "" private string is_value = ""
您可能会注意到我声明了两个值为私有并且初始化了它们。这通常是老程序员在不情愿地得出他们绝对信任不了任何东西的结论时编写的代码。
现在您需要为每个值创建两个函数,总共四个函数。您需要两个用于键,一个用于设置,一个用于获取。这是代码,不需要太多解释。
N_cst_key_value.of_key // FUNCTION n_cst_key_value.of_key // ARGUMENTS none // RETURN VALUE string // DESCRIPTION returns the value of is_key return is_key
这里我们设置键值
N_cst_key_value.of_key(as_your_key) // FUNCTION n_cst_key_value.of_key // ARGUMENTS string as_key // RETURN VALUE string // DESCRIPTION Sets the key value and returns the value // of is_key before the call string ls_retVal ls_retVal = of_key() // Call the OTHER of_key is_key = as_key return ls_retVal
现在您已经看到了我如何处理 of_key,我假设您也可以为 of_value 做同样的事情,对吗?如果不行,那么您可能需要下载代码看看我是怎么做的。本文空间有限。
现在我们需要一个非可视化 (自定义) 对象。它将处理 Datastore。创建一个新的自定义类。
我将我的命名为 n_cst_key_value_collection
。让我们从需求点 1 开始。我们必须允许程序员允许或禁止重复键。我们需要一个实例变量和几个函数来设置和获取它。
N_cst_key_value_collection Instance variables Private Boolean ib_allow_duplicates Private Datastore ids_key_values // Our collection
现在我们必须实例化 Datastore,以便它能为我们工作。我们需要在构造函数中添加一些代码
N_cst_key_value_collection.constructor // FUNCTION n_cst_key_value_collection.constructor // DESCRIPTION instantiates the datastore ids_key_value = create datastore ids_key_value.dataobject = "ds_key_value"
接下来我们需要一个函数来获取值
N_cst_key_value_collection.of_allow_duplicates // RETURN VALUE boolean // DESCRIPTION Returns whether or not duplicates are allowed // in this function. return ib_allow_duplicates
同时,我们需要允许程序员设置我们是否允许重复。这比看起来要复杂一些。问题是,如果已经有重复项,那么我们就不能让程序员拒绝重复项。
N_cst_key_value_collection.of_allow_duplicates // FUNCTION n_cst_key_value.of_allow_duplicates // ARGUMENTS - ab_how // RETURN VALUE boolean // DESCRIPTION Allows or disallows duplicate key // values in the datastore. If there are rows in the // datastore and the programmer is trying to change // to FALSE then we need to check for duplicates. If // we find any we need to return FALSE which means // error boolean lb_retVal, lb_found_duplicate = FALSE // The following line is polymorphic, not recursive. // I'm not calling this same function. I'm calling the // one that doesn't have an argument. lb_retVal = of_allow_duplicates() if ids_key_value.rowCount() > 0 then // We have to see if there are any duplicates. long ll_row, ll_max ll_max = of_count( ) // I'm going to be a little tricky here. // I'm going to sort the datastore then check // for duplicates. It's a lot easier. datastore lds lds = ids_key_value lds.setsort( "as_key") for ll_row = 1 to ll_max - 1 if lds.getItemstring(ll_row, "as_key") = & lds.getItemString(ll_row + 1, "as_key") then lb_found_duplicate = TRUE end if next if lb_found_duplicate and not ab_how then ab_how = TRUE end if ib_allow_duplicates = ab_how return lb_retVal
N_cst_key_value_collection.of_count // FUNCTION n_cst_key_value_collection.of_count // RETURN VALUE long // DESCRIPTION returns the number of items in the collection return ids_key_value.rowCount()
我们的第一个测试
我喜欢做一些事情时进行测试。所以我们现在开始测试。我在我的工具中继承了一个名为w_root
的窗口,但您可以创建一个标准窗口并在应用程序的 open 事件中打开它。我的窗口如图 2 所示。它包含两个用于我的键/值的单行编辑框,一个用于添加它们的按钮(即使我们还没有编写那部分),一个用于计算有多少个,一个用于关闭我们的窗口。最后,我有一个复选框,用于允许或禁止重复键。让我们先实例化一个用于我的键/值集合的实例变量
W_main instance variables
private n_cst_key_value_collection io_key_value
现在我们可以添加一些代码。我们必须在 open 事件中创建它
W_main open or post_open events io_key_value = create n_cst_key_value_collection
现在设置我们的允许重复项还不算太早
W_main.cbx_allow_duplicates.clicked event io_key_value.of_allow_duplicates( this.checked)
我们也可以对计数按钮进行编码
W_main.cb_count.clicked event messagebox("Count", string(io_key_value.of_count()) + " values are in the collection")
现在我们能做的还不多,但我们已经开始了。我们可以运行应用程序并点击计数按钮,看到我们还没有值。
将键/值添加到集合中的功能在 n_cst_key_value_collection
中完成。首先要做的是搜索一个项目。这样我们就可以满足允许或禁止重复项的要求。由于我们使用的是 Datastore,所以这个函数实际上非常简单(注释比函数本身还长)。
N_cst_key_value_collection.of_find_key // FUNCTION n_cst_key_value_collection.of_find_key // ARGUMENTS string as_key // RETURN VALUE long // DESCRIPTION // Looks for as_key. If it finds it then the index is returned. // If it doesn't find it then 0 is returned return ids_key_value.find( "as_key='" + as_key + "'", 0, ids_key_value.rowCount())
现在我们有了搜索功能,就可以实现添加函数了
N_cst_key_value_collection.of_add // FUNCTION n_cst_key_value_collection.of_add // ARGUMENTS // string as_key // string as_value // RETURN VALUE long // DESCRIPTION // Checks to make sure that there are // no duplicate keys if the duplicates are not allowed. // If it passes then it adds the values to the datastore // and returns the number of rows in the datastore if not of_allow_duplicates( ) and of_find_key( as_value) > 0 then return -1 end if long ll_row ll_row = ids_key_value.insertRow(0) // add a row at the end. ids_key_value.setitem( ll_row, "as_key", as_key) ids_key_value.setItem(ll_row, "as_value", as_value) return of_count( )
完成这些之后,我们终于可以创建一个测试了。我们需要将 of_add
的调用添加到窗口的 Add 按钮中。它看起来像这样
W_main.cb_add long ll_rows string ls_key, ls_value ls_key = sle_key.text ls_value = sle_value.text ll_rows = io_key_value.of_add(ls_key , ls_value) messagebox("You now have", string(ll_rows) + " rows")
让我们关闭允许重复项。换句话说,不允许重复,然后添加两个不重复的值和一个重复的第三个。这是测试
- 添加键“First”和值“one”。您应该会看到一个消息框,上面写着“1 rows”。
- 添加键“Second”和值“two”。您应该会看到一个消息框,上面写着“2 rows”。
- 不更改键或值而单击“Add”按钮。您应该会看到一个消息框,上面写着“-1 rows”。
- 单击“Count”按钮。您应该会看到一个消息框,上面写着“2 values are in the collection。”
这将是一个初步测试。我们也可以测试允许重复的功能
- 单击“Allow Duplicates”以允许它们。
- 添加“key1”、“value1”。您应该会看到“1 rows”。
- 再次单击“Add”而不进行更改。您应该会看到“2 rows”。
- 再次单击“Allow Duplicates”,将其关闭。
- 再次单击“Add”。您应该会看到“3 rows”,因为您已经有重复项,并且不允许在已有重复项时拒绝重复项。
现在关闭应用程序并重新打开。我们还有一个测试要做,然后就可以结束了。我们需要立即禁止重复项并尝试插入它们。
- 添加“key2”和“value1”。您应该会看到“1 rows”。
- 不更改任何内容而单击“Add”。您应该会看到“-1 rows”。
- 单击“Count”。您应该会看到“1 values are in the collection。”
- 添加“key2”和“value2”。您应该会看到“2 rows”。
现在我们有了一个功能齐全的对象。让我们添加最后几个函数和测试,然后结束这部分,直到第三部分。我们唯一剩下的就是获取值的功能。这是一个相当简单的函数
N_cst_key_value_collection.of_value // FUNCTION n_cst_key_value_collection.of_value // ARGUMENTS string as_key // RETURN VALUE string // DESCRIPTION // Returns the value at the first as_key. If as_key is not in the // collection this returns an empty string long ll_row ll_row = of_find_key( as_key) if ll_row = 0 then return "" return ids_key_value.getitemstring(ll_row, "as_value")
这个对象本身已经很有用了。下个月我们将使用它来存储将进入 DataWindow 的 Tag
属性的值。