Thursday, October 04, 2007

the one and only, the TB_ADDSTRING message !

If you try to use TB_ADDSTRING with the string from the resource then you might had blown your head trying to figure out what the heck is happening, why does it fail:

const int iIndex = ::SendMessage(
IDS_TBSEARCH); // string id from module's resource

iIndex is returned -1 (indicating an error).

Now, I looked at my MSDN from disk and then looked at the online version. Nada. But the old friend Mr. Google saved the day (he always does). I got this page:

The problem with TB_ADDSTRING or the AddString method that is simply
a wrapper to this message is that the first character in the string
resource is used as separator and is replaced with a NULL character.

So your string resource must be something similar to: "|Button text||"
where the pipe character is replaced with '\0'.
This is because the string you pass with TB_ADDSTRING message must
have a double null terminator. You can also use the TB_ADDSTRINGS
message with which you can set all the button texts using a string
like this: "|Button 1|Button 2|Button 3|Button 4||".

This is not well documented or not documented at all, I don't
remember. Anyway keep in mind that the text added to the button with
TB_ADDSTRING is not retrieved using TB_GETBUTTONINFO, but only using
TB_GETBUTTONTEXT, this is why if you use this method instead of using
TB_SETBUTTONINFO, the chevron menu does not display the text of the
menu items

No, it's not documented at all.
Something was fishy from the start though.

When you create the toolbar using the CreateToolbarEx API, you pass an array of TBBUTTON structs. TBBUTTON has its last field either an zero-based index or a pointer to string buffers (the last string needed to be terminated by an extra null to indicate the end of the list).
Zero-based index of the button string, or a pointer to a string buffer that contains text for the button.

But the question is: In the case when you specified in the TB_ADDSTRING a resource string id, what's the zero-based index from TBBUTTON for ?

The problem is that the documentation is incomplete due to what the API tried to achieve. It wanted to provide several ways to add strings but the documention failed to explain exactly.

In toolbar controls you can set buttons text in at least 2 ways:
One way is when you create the toolbar using CreateToolbarEx API.
In each TBBUTTON structure you can put either a straight text buffer or a zero-based index of the button string:

TBBUTTON tbbtn = {0};
tbbtn.iString = _T("Text");


TBBUTTON tbbtn = {0};
tbbtn.iString = 0; // index

The index value is the index from the string list you set to the toolbar control using the TB_ADDSTRING message. The string list can be from 2 sources, either from:

a character array with one or more null-terminated strings:

LPCTSTR szTexts[] = {_T("Text1"), _T("Text2\0") };
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)szTexts);

or from a string in the executable resource:

IDS_TBSEARCH); // string id from module's resource

but in the latter case the resource string needs to be in a special form. Its first character will be used by the Common controls API as string separator, so
IDS_TBSEARCH can look like this: |Text1|Text2||

In the first case, you can use TB_ADDSTRING at a time, to add each string to the control internal list.

::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text1\0"));
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text2\0"));

Another way to set a toolbar button text is using the TB_SETBUTTONINFO message but you are limited to pass only a string buffer.

No comments: