How to move a label or any other object in a table layout panel at run time?

jpc0tte
06-30-2008, 09:54 AM
hi guys,
hope someone could help me...here's my problem. Am using Vb.net 2005 to create a dynamic timetable for a school. The software will consist of objects like labels which will represent subjects. The user will create the time table by dragging and dropping the labels at the desired location. Each location will be a cell from a table layout panel. The latter is used for precise drag and drop. Rows will represent days while column: the time periods.

What i have succeeded so far is to make the objects move on the form. When drag on the panel, it hides at the back of it. So what can i do to make the objects move inside the table layout panel's cells as it is possible in design mode????

Help guys, it's a real pain..:(

AtmaWeapon
06-30-2008, 11:22 AM
This is not impossible, but it'd definitely pain on the level that makes it almost easier to just implement your own control rather than use the TableLayoutPanel. Let's discuss the smaller problems.

The first problem is having the ability to drag labels around, which I've discussed in a previous thread (http://www.xtremevbtalk.com/showthread.php?t=297849). Since the TableLayoutPanel doesn't respect the Location property of its children, the only way to make this work would be to respond to a MouseDown event by removing the control from the TableLayoutPanel then adding it to the form at the same location. It's not much harder to implement your own table-based control that maintained internal label-like objects; then your dragging code could be much more unified and easier to work with.

The next problem is figuring out where the Label was and repositioning it based on its location. This is kind of tricky. Your only hope will be to use absolute sizing for each row and column, then use the mouse position as the user drags to calculate which cell is under the mouse. This gets more tricky if you don't use absolute sizing; you'll have to use the GetRowHeights and GetColumnWidths methods to figure out where cells are.

Once that problem's solved, moving a control to another cell is simple: you call the panel's SetColumn and SetCell methods to new values.

I really think a custom control is more likely the way to go, though. You'd end up doing less work and you wouldn't be relying on things that I'm not even sure if they work. That said, here's the very messy test application I wrote to see if any of this was even possible; there's a 2x2 table layout panel in the center of the form with row heights of 50 and column widths of 50. You can drag the label around, and when you stop dragging the program puts the label in the cell that contains the top left corner of the label. It deviates slightly from the above explanation in the interests of making the code a little more elegant. Getting this to work for several labels won't be too bad, and extending it to work with any Control doesn't hurt any worse, since all methods used are actually defined on Control. I still think a custom control might be the best practice, if only because you can actually draw grid lines and let the user see what's going on.

Public Class Form1

Dim _dragging As Boolean = False
Dim _controlPoint As Point

Private Sub Label1_MouseDown(ByVal sender As System.Object, ByVal e As MouseEventArgs) _
Handles Label1.MouseDown
Dim lbl As Label = DirectCast(sender, Label)

' If the label is somehow free-floating we'll lust let it be
If (TableLayoutPanel1.Contains(lbl)) Then

Dim mousePoint As Point = Cursor.Position ' mouse position in screen coordinates
_controlPoint = lbl.PointToClient(mousePoint) ' offset from (0, 0) in label coordinates

' This is the location of the label's (0, 0) in screen coordinates.
Dim screenLocation As Point = New Point(mousePoint.X - _controlPoint.X, _
mousePoint.Y - _controlPoint.Y)

' Remove the label from the panel and make it a child of the form; bring it to
'the front to make sure it's not hidden behind the panel.
TableLayoutPanel1.Controls.Remove(lbl)
Me.Controls.Add(lbl)
lbl.BringToFront()

' Move the label to the position it was in before we removed it from the table, and
' indicate the control is being dragged.
lbl.Location = Me.PointToClient(screenLocation)
_dragging = True
End If
End Sub

Private Sub Label1_MouseUp(ByVal sender As System.Object, ByVal e As MouseEventArgs) _
Handles Label1.MouseUp
' When the mouse button is released, put the label in the right place if needed
If _dragging Then
Dim mouseLocation As Point = Cursor.Position ' mouse location in screen coordinates
Dim formLocation As Point = Me.PointToClient(mouseLocation) ' form coordinates

' This is what the location of the label should be set to; it's in form coordinates
Dim finalLocation As Point = New Point(formLocation.X - _controlPoint.X, _
formLocation.Y - _controlPoint.Y)

' Get the label and set its location
Dim lbl As Label = DirectCast(sender, Label)
lbl.Location = finalLocation

' Remove the label from the form's controls and make it a child of the table layout
' panel. Also, calculate its new position in the table.
Me.Controls.Remove(lbl)
TableLayoutPanel1.Controls.Add(lbl)
TableLayoutPanel1.SetCellPosition(lbl, GetCellCoordinates())

' Indicate we're no longer dragging
_dragging = False
End If
End Sub

Private Sub Label1_MouseMove(ByVal sender As System.Object, ByVal e As MouseEventArgs) _
Handles Label1.MouseMove
' When the mouse moves over a label that is being dragged, move the label
If _dragging Then
Dim mouseLocation As Point = Cursor.Position ' screen coordinates
Dim formLocation As Point = Me.PointToClient(mouseLocation) ' form coordinates

' This is where the label should be moved to
Dim finalLocation As Point = New Point(formLocation.X - _controlPoint.X, _
formLocation.Y - _controlPoint.Y)

' Move the label
Dim lbl As Label = DirectCast(sender, Label)
lbl.Location = finalLocation
End If
End Sub

Private Function GetCellCoordinates() As TableLayoutPanelCellPosition
Dim pos As New TableLayoutPanelCellPosition(0, 0)

' Get the client coordinates of the panel
Dim tlpPoint As Point = TableLayoutPanel1.PointToClient(Cursor.Position)

' Determine the row based on row height of 50 pixels
pos.Row = (tlpPoint.Y \ 50) Mod 2

' Determine the column
pos.Column = (tlpPoint.X \ 50) Mod 2

Return pos
End Function
End Class

AtmaWeapon
07-01-2008, 08:46 AM
I managed to make a decent start of a custom control that does this. There's lots of rough edges and room for improvement but it ought to give you some ideas as to techniques that could be used to implement a more refined control.

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum