C - Win32 - managing custom buttons.

Started by
20 comments, last by BrickInTheWall 13 years, 11 months ago
Hi guys, I'm in a bit of a pickle here on how to continue. I'm writing a Win32 application and want to program everyting C-style and without using resources. It's working well so far. I have an owner drawn button (BS_OWNDERDRAW style)...drawing it works fine. These buttons will represent points in a coordinate system that the user can drag around. Also, I want the user to be able to add new points(buttons) whenever and wherever he wants. This is all no problem, but I'm really not sure how I should store all of these buttons. Also, every new button would need an individual ID wouldn't it? Otherwise if two buttons had the same ID and I were to intercept a "drag" action, I'd be moving both buttons eventhough only one is clicked. I was thinking of maybe using a linked list. I could try to generate ID's by incrementing a variable each time the user creates a point. If I have 10 points-> 1,2,3,4,5,6,7,8,9,10 ...if the user deletes no. 5-> 1,2,3,4,5 (formerly 6), 6 (formerly 7) etc... Any advice is appreciated. Cheers!
Fate fell short this time your smile fades in the summer.
Advertisement
I'm not sure how you structure your controls and such, and by that I mean, whether the buttons sit directly inside the window, or if they sit inside a parent control, but a more ideal solution I think would be to use what's built into the Windows API already. That is, EnumChildWindows and EnumChildProc. If you use these on the parent control, you could generate a list quickly or perform actions on the buttons themselves.

Thanks, thats definately a good piece of help when handling messages that apply to all buttons at once. But my buttons positions don't depend on how big the window is for example. If a button needs to be redrawn, I'll get a WM_DRAWITEM message but still need to figure out which button it is (i.e. get it's ID) and then associate this ID with the corresponding point structure that stores info about the point button (it's color etc.). I have an idea, but it seems kind of overkill. I'm worried about ID ambiguity, for example that I might get into problems because a newly created buttons gets an ID (number) that maybe some static text box might already have. I'm not sure if thats bad, but I don't want that happening. I thought I'd have an array of 100 fields only for pointers to Point structures on the heap. Therefore the button ID's can start at 400 for example and go to 500, and I know this, so I can give everything else that needs an ID something outside of this range. I keep a variable that stores the number of points currently created.

If I create a point: malloc(sizeof(Point)) etc ...put a point on the heap, initialize it with the data specified by the user (e.g. color, visible etc.).
pointContainer[numOfPoints] = pointerToTheNewPoint. Then create the window...
CreateWindowEx(0, "button", "", WS_CHILD | WS_VISIBLE (or not) | BS_OWNERDRAW, x, y, width, height, hWnd, (HMENU)numOfPoints), ....);

Then when I get a WM_OWNERDRAW message, I can just use the ID to draw the appropriate button: drawButton(hDC, pointContainer[ID]....);

The only problem I see in this is that when I delete a button, I have to set all the ID's of the buttons behind the one that was deleted, so that they are in increasing order...and also decrement numOfPoints...that way I know that when I am using the ID I get sent, on the correct button in the array.
If I wanted to give a window that is already in use a new ID, would this require destroying it first and then recreating it with the new ID?
Fate fell short this time your smile fades in the summer.
This might be a stupid idea (I tinker in C).
Have a simple array. I guess you won't have a million elements. Maximum 5000.
So I would have an item_type item[5000]; with a used flag.

Start to fill item. If you delete an item, mark it unused. If you add a new item, search for the first unused item in the array and place the new item there. Just like in a multi-bullet-shooter bullet-vector (I guess you get it).

As for ID:s, maybe the ID simply could be the position in the array.

But I'm not a real programmer. I'm just tinkering and producing ugly code.
(And I hope that helps)
Believe it or not but I had actually considered what you are suggesting since it's a method I like using when for example generating a random order of elements and need to fill all slots, and I think it's actually quite effitient. Though I'm no pro, I think this could work quite well.
Fate fell short this time your smile fades in the summer.
Just use a vector or malloc if you're working in c only. But instead of deleting elements and having to shift to fill the gap, you swap the element you want to delete with the element at the end, then erase the element at the end. You can't use this if you care about the order of the elements, but otherwise it's extremely efficient.
Quote:Original post by BrickInTheWall
... still need to figure out which button it is (i.e. get it's ID)


If you have the HWND for the button you can use GetWindowLong to retrieve the id.

e.g.

DWORD id = GetWindowLong(hwnd, GWL_ID);

Quote:Original post by BrickInTheWall
... and then associate this ID with the corresponding point structure that stores info about the point button (it's color etc.).


You can use the id as the index into a lookup table or as the index key for some other kind of searchable container - list, queue, etc.

Quote:Original post by BrickInTheWall
I'm worried about ID ambiguity, for example that I might get into problems because a newly created buttons gets an ID (number) that maybe some static text box might already have. I'm not sure if thats bad, but I don't want that happening.


It sounds to me like you need to rethink how you've arranged the interface. Do you really need to create new buttons? Maybe you can get by with re-instancing buttons or toggling the onscreen visibility of buttons. You don't need to create a new button if you simply want to change the text displayed on the buttons. Iirc, SetWindowText should do the job.

Quote:Original post by BrickInTheWall
If I wanted to give a window that is already in use a new ID, would this require destroying it first and then recreating it with the new ID?


No. See SetWindowLong.


"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
I've chosen a method similar to szecs suggested. I have an array of pointers to point objects on the heap and if I delete one (using free) I just set that pointer to NULL, and when I create a new button, it goes in the first space of the array that is NULL. Also as suggested, I'm using the ID to access the array. Everything there works fine.
What I'm currently fighting with it trying to set the cursor to IDC_HAND when the user "mouseovers" on any of the buttons.
The button ID's can go from 800 to 900...here is my WM_SETCURSOR case:

int id = GetDlgCtrlID((HWND)wParam);if (id >= 800 && id < 900)      SetCursor(LoadCursor(GetModuleHandle(NULL), IDC_HAND));else      return DefWindowProc(hWnd, msg, wParam, lParam);

I'm not sure if calling DefWindowProc is ok here but it seemed logical to me since I was getting weird cursor behavior before.
I debugged the if (...) case gets called exactly when I want it to, but for some reason the cursor doesn't change. It flickers a bit (I think...it just looks like it but I can't see the cursor I want). Is something resetting the cursor? Or is it not being set at all? I've done a little research and have read that setting the window class cursor to NULL would help, though I'm not sure if this is true for WM_SETCURSOR cases generated by mouse overs on a windows child windows (my button controls).
Anyone have any experience with this?
Thanks for the help so far. I thought I'd ask here instead of starting a new thread.
Fate fell short this time your smile fades in the summer.
I recommend starting a new thread. It will get attention that this thread might not.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
According to MSDN the cursor is changed, but will be changed back (to the type, with which you registered the window class) as soon as you move the mouse.

To change permanently:
SetCursor(cursor);SetClassLong(hWnd,GCL_HCURSOR, cursor);  
and change back manually when you have to, of course

This topic is closed to new replies.

Advertisement