Organisational confusion

I have a containercontrol with some buttons. The buttons are instances of my own button class (canvas plus a nice paint event to draw the button). So far so good. Now, I want to be able to rearrange these buttons at runtime, by drag/drop. Canvas seems to have enough events to do this, but no action event per se, so I need to put the action code in the button’s own MouseDown event, and apparently also the drag-initiation code. Trouble with this is that the drag starts on mousedown, and so the button image (which will be dragged) shows even if I’m just clicking the button to do the button’s action.

To get round this I need the MouseDown event to either do the action, or do the drag-initiation, but never both at once. This will be mediated by a property that gets set elsewhere. This is easy enough, but I only see two ways to do this and each has a disadvantage.

  1. have the property belong to the containercontrol. But as far as I can see this forces each button to have its own MouseUp and MouseExit events, rather than hiding those away in the button class. These two event handlers always have the same code so they could go there. They are both quite short but it’s not their length but that I need to make sure that 30 buttons have these handlers.

  2. have the property be in the button class, which would allow the event handlers to be there too. Downside of this is that then when switching the containercontrol between normal operation mode and configuring-it mode, I’d then have a large number of instances of the property to change. Still, I’m minded to do it this way as I can automate that reasonably easily.

What I’m wondering is whether I’ve simply overlooked some aspect of how classes and instances thereof work.

Buttons should not react on mouse DOWN
The reason is that you could push one down, hold the mouse down, move the mouse OFF the button, and release the mouse button and the button action should not be run.
This is pretty normal behaviour on most desktop platforms

That should change behaviour enough that you can handle drag or click independently

Yeah, that improves them by having mouse do the work, and I’ve mostly got the drag/drop to work but there’s an outstanding issue.

I can drag the buttons around, and if I drop a button on another, the dragged button drops in front of the target and all buttons move up (or down). This works OK and is done inside the DropObject event. I figure out which buttons need redrawing, and Refresh() them.

The issue is that the buttons don’t refresh on-screen until I Refresh() them again in the Action event of another button elsewhere. I know the first set of refreshes work because (a) I checked with the debugger and (b) if I try to drag the original button again, the drag-image shows the image of the button that should be showing there, not the original. Thus, all the rearrangement actions have taken place, just the redrawing doesn’t get done.

Any suggestions here? This is Mohave 2019r3.1.

Use invalidate
Not refresh

You’ll have to manage “z-order” yourself somehow to know which one is “on top” and then drag the correct one etc

Makes no difference. In fact I was originally using invalidate but according to the docs refresh is supposed to do it right now only it doesn’t. I can even see that the tooltip applies as if the move has worked (i.e., indicating that it has worked), but the visible button is wrong.

dunno what it could
the difference between invalidate and refresh is that invalidate simply gets coalesced together so if you call it 50 times eventually you get ONE repaint

Refresh makes it do 50 repaints if you all it 50 times

But the end result will, or should be, be the same on screen

Looks like I can get it to work by using Timer.CallLater(). In the DropObject event handler, I have to leave present the loop which invalidates a set of buttons. That exact loop code is then executed a second time in the CallLater method, and then the buttons get redrawn.

Bit of a hack but WTF. Unclear whether this is a bug or not, I’ll have to see whether Windows gets this too and whether I can make a small project for a feedback.

Careful with CallLater
IF that control or the window gets closed and there is still a scheduled CallLater call then you can have issues
Make sure you cancel all calls when you close things

OK it’s sorted now - without the CallLater. If a button is moved, it needs a new Top and Left. I was trying to get the Paint event to do this, when that code should have been in the DropObject event. Lesson for me there.