Changing control's z-order

I was looking for a way to change the z-order of UI controls in a window but couldn’t find anything definitive. Here’s an example of how it can be done if anyone is still looking for a solution:

  1. Add three rectangles, maybe coloured white, red & blue, created in that order so the white is at back and blue at front. Make them overlap so you can see the change.
  2. Add a button to change order when pressed
  3. Add the following in the button’s ‘Pressed’ event. This should re-order the three rectangles so the white rectangle is at front. Sorry, I still haven’t worked out how to paste code in here :slight_smile:

var arrayRects() As DesktopRectangle

System.DebugLog(“— order before changes —”)
for each obj as Object in self.Controls
if obj IsA DesktopRectangle then
var strName As String = DesktopRectangle(obj).Name
System.DebugLog(strName)
arrayRects.Add(DesktopRectangle(obj)) // adding this rectangle to the array
end if
next

// removing all rectangles from window
for index as Integer = 0 to arrayRects.LastIndex
arrayRects(index).Close
next

// adding rectangles back to window in new order
self.AddControl(arrayRects(1))
self.AddControl(arrayRects(2))
self.AddControl(arrayRects(0))

System.DebugLog(“— order after changes —”)
for each obj as Object in self.Controls
if obj IsA DesktopRectangle then
var strName As String = DesktopRectangle(obj).Name
System.DebugLog(strName)
end if
next


1 Like

What is your goal? Using a single canvas for everything is much easier.

2 Likes

I think @Sam_Morgan’s goal was just to share his solution for restacking controls in a different order, rather than trying to draw something.

It’s a clever idea :+1:

3 Likes

Select your code,
Paste it,
Select it in the Forum page,
Click in image

That is all; now you can see the result as in Xojo IDE.

var arrayRects() As DesktopRectangle

System.DebugLog(“— order before changes —”)
for each obj as Object in self.Controls
if obj IsA DesktopRectangle then
Var strName As String = DesktopRectangle(obj).Name
System.DebugLog(strName)
arrayRects.Add(DesktopRectangle(obj)) // adding this rectangle to the array
End If
Next

// Removing all rectangles from window
For index As Integer = 0 To arrayRects.LastIndex
arrayRects(index).Close
Next

// Adding rectangles back to window in new order
Self.AddControl(arrayRects(1))
Self.AddControl(arrayRects(2))
Self.AddControl(arrayRects(0))

System.DebugLog(“— order after changes —”)
For Each obj As Object In Self.Controls
If obj IsA DesktopRectangle Then
Var strName As String = DesktopRectangle(obj).Name
System.DebugLog(strName)
End If
Next
3 Likes

Like what Scott said, my goal was to come up with a relatively easy way of re-ordering window controls, not necessary rectangles. I tried this with labels as well, and think it would work with any UI control. Just an idea that’s all.

Thank you.

I’d like to know if other controls retain their state - checkboxes, etc.

DesktopLabel retained its properties. I’ll test with other controls later and let you know.

One solution to complicated control overlay problems is to save the Project in .xojo_project text format, Then rearrange the controls in the .xojo_window file. Also make any other changes, such as exact positioning, at the same time.

I was first forced into doing this with a window with 60 overlapping controls, but this procedure is so fast and accurate, that I now do it routinely whenever windows don’t behave or look exactly how I want.

So…

DesktopUIControl subclasses have a method called DrawInto which will draw the control in its current state onto a graphics context. What you could do is let the user put the layout into a design mode and overlay the editing area with a canvas. When the user clicks on the canvas, you go through the controls and see which (if any) control was clicked on. If one was, you move it off screen and use DrawInto to draw the control into the canvas as the user drags. Once they release the mouse, you put the control in the release position and stop drawing the canvas version. In this way, the dragged control would always be on top of everything else.

You’ll have to track the control, its coordinates and the drag itself, but it is doable.

Here’s an example where this could be used, but I hit a hurdle :slight_smile:

I have 3 DesktopCanvas items in a window. Using the paint event I drew a different playing card picture in each

The canvas items are created in order from left to right and named CanvasA, Canvas2 & Canvas3. Using the MouseDown, MouseDrag and MouseUp events I drag a playing-card by clicking the mouse on the canvas item and dragging it around the screen. If I move the CanvasA playing-card it moves behind the other two cards because it was created first and is at the back of display according to Z-Order. So, thinking I was clever, I moved CanvasA to the front of the display by making it last on the Z-Order (did that by CanvasA.close then adding it back on the window by self.AddControl(CanvasB) ).

Everything works well, EXCEPT:

The first MouseDown event is triggered, however once the CanvasA is closed the MouseDrag event is not triggered unless I do a second mouse down click. It appears that once I close the canvas its mouse events are suspended so it no longer detects that the mouse is down until it’s triggered again by another click.

Hmmmm… any ideas?? :person_shrugging:

and just to practice pasting code, here’s the code:

MouseDown event

if objMovingCard = nil or objMovingCard <> me then
  BringCardToFront(me)
  me.Left = me.Left + x - (me.Width / 2)
  me.Top = me.Top + y - (me.Height / 2)
end if
Return True

MouseDrag event

if objMovingCard = me then
  me.Left = me.Left + x - (me.Width / 2)
  me.Top = me.Top + y - (me.Height / 2)
end if

MouseUp event

objMovingCard = nil

BringCardToFront method

objMovingCard = objCard
objCard.Close
self.AddControl(objCard)

Eric, see above. I tried it with a DesktopCanvas and it seems to retain its properties. I did encounter a problem though, described above.

This one?

Hi Beatrix,
Thank you but I’m a Xojo beginner and not sure what you mean. Can you show me an example on how a single canvas would work with something like the playing cards example?

This topic has been discussed several times in the forum. I’ve looked at the examples but I wasn’t able to find anything.

Basically you still define your cards in classes. But the output is done all to one canvas.

Edit: have a look at the game examples.

2 Likes

You can have a look at the example project I posted here. You’d create a class that represents the cards and draws their face (just like the included Rectangle and Oval). To modify the Z order, you’d change their position in the array.

1 Like

Found a bug when you only change the order on a Window and save to xojo project. The Window code is not saved. (The project must be saved first)
I think this doesn’t apply to binary saving but didn’t test.

#78206 - Changing only the Order Forward/Backward on a Window does not update the code

I think I found a solution (for this specific example) that isn’t too complicated. Hopefully someone gets some use out of it.

This example displays 3 DesktopCanvas items as playing-cards. The goal is to have the ability to select a card and move it around the window. When moving the selected card, it should display above the other cards (which doesn’t happen if we don’t change the z-order).

A mouse click & release over a card selects it, another mouse click & release over the same card unselects it. All three canvases use identical MouseDown and MouseMove events.

MouseDown event

// check if this canvas is selected - select canavs with first click, unselect with second click
if canvasSelectedCard = me then
  
  // already selected so unselect it
  canvasSelectedCard = nil
  
else
  
  // select this canvas
  canvasSelectedCard = me
  
  // centre canvas where mouse pointer is clicked
  me.Left = me.Left + x - (me.Width / 2) 
  me.Top = me.Top + y - (me.Height / 2)
  
  // bring this canvas to top of z-order
  me.Close
  self.AddControl(me)
  
end if

Return True

MouseMove event

me.MouseCursor = System.Cursors.FingerPointer

// check if this canvas is selected
if canvasSelectedCard = me then
  
  // centre canvas where mouse pointer is
  me.Left = me.Left + x - (me.Width / 2)
  me.Top = me.Top + y - (me.Height / 2)
  
end if

Results:
Moving the Ace Card

Moving the 2-Card

Moving the 3-Card

I love Xojo… :blush:

2 Likes

Thank you for your help. I had a look and found it too complex for me for now… I found a work-around anyway. I’ll keep learning. Again thanks for your thoughts.

Thanks Anthony.