-1

I'm developping a modular dashboard in Visual Basic that starts as an empty container than i can add modules to populate it.

The parent form is an MDI container and every module is a child MDI form that have fixed size.

I'd like to snap all of that child form to a grid both when the user creates a new one and when moves one of it inside the container (like they are magnetized to that grid).

How can i do that? Thanks

mferre
  • 182
  • 1
  • 14
  • 1
    The simple option is to call the `LayoutMdi` method of the parent form. If you want the forms to snap to a grid as they are dragged then that's much more complex and far too broad to be asked as a single question here. – jmcilhinney Aug 06 '20 at 11:06
  • yeah, i know that but in this case when i move one of that forms they can be positioned in every point in the space, not only snapped to an imaginary grid. – mferre Aug 06 '20 at 11:10

2 Answers2

2

If I well understand you need something like this:

    Private Class ImaginaryGrid

    ' You can change Columns and Rows as you want
    Shared Rows As Integer = 3
    Shared Cols As Integer = 3

    Private Shared Function SnapToGrid(target As Form) As Point

        Dim AllW As Integer = Screen.PrimaryScreen.WorkingArea.Width
        Dim AllH As Integer = Screen.PrimaryScreen.WorkingArea.Height

        Dim parent = target.MdiParent
        If parent IsNot Nothing Then
            AllW = target.MdiParent.ClientSize.Width
            AllH = target.MdiParent.ClientSize.Height
        End If

        Dim currentPoint As Point = target.Location

        Dim stepW As Integer = CInt(AllW / Cols)
        Dim stepH As Integer = CInt(AllH / Rows)

        Dim targetCol As Integer = CInt(currentPoint.X \ stepW)
        Dim targetRow As Integer = CInt(currentPoint.Y \ stepH)

        Dim newX As Integer = targetCol * stepW
        Dim newY As Integer = targetRow * stepH

        target.Location = New Point(newX, newY)
        target.Width = stepW
        target.Height = stepH

    End Function

    Shared Sub AttachFormToStayInGrid(frm As Form)
        AddHandler frm.ResizeEnd, Sub()
                                      SnapToGrid(frm)
                                  End Sub
        SnapToGrid(frm)
    End Sub

End Class

Usage:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    ImaginaryGrid.AttachFormToStayInGrid(Me)
End Sub

Take changes (if needed) as comments below shows:

G3nt_M3caj
  • 2,497
  • 1
  • 14
  • 16
  • 1
    Great start. For this to be applied to the child area of an MdiParent, you'd need to use [MdiClient](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.mdiclient?view=netcore-3.1) instead of the screen. In the Load() event of the parent form, you could get the MdiClient with: `client = Me.Controls.OfType(Of MdiClient).FirstOrDefault`. Then you can use `client.ClientRectangle` to determine the "working area" of the mdi children area. – Idle_Mind Aug 06 '20 at 14:22
  • @Idle_Mind yeah, let to develop this together. Take changes in code then I have to edit with new changes I’m trying to add now (for busy cells). I’m waiting for your changes. – G3nt_M3caj Aug 06 '20 at 14:31
  • oh it's crazy! it works very well! just another question.. if i'd like to maintain the original size of the child forms, how can i proceed? i guess i have to calculate the number of column and rows according to the screen size and the size of forms and then don't modify the size (target.Width = stepW, target.Height = stepH)? – mferre Aug 06 '20 at 15:25
  • 1
    Columns and Rows is inside of class. As is shared you can change those by code. To have original Form Size just comment two lines “target.Width = stepW” and “target.Height = stepH” – G3nt_M3caj Aug 06 '20 at 15:29
1

Here's a beefed up version based on the same idea as G3nt_M3caj's suggestion:

enter image description here

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ImaginaryGrid.Client = Me.Controls.OfType(Of MdiClient).FirstOrDefault
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim frm As New Form
        ImaginaryGrid.AttachFormToStayInGrid(frm)
    End Sub

    Private Class ImaginaryGrid

        Public Shared WithEvents Client As MdiClient

        Public Shared FixedChildSize As New Size(250, 150)

        Private Shared Function SnapToGrid(target As Form) As Rectangle
            Dim colX As Integer = target.Location.X / FixedChildSize.Width
            Dim colY As Integer = target.Location.Y / FixedChildSize.Height
            Dim newX As Integer = colX * FixedChildSize.Width
            Dim newY As Integer = colY * FixedChildSize.Height

            Return New Rectangle(New Point(newX, newY), FixedChildSize)
        End Function

        Shared Sub AttachFormToStayInGrid(frm As Form)
            frm.Size = FixedChildSize
            frm.FormBorderStyle = FormBorderStyle.FixedSingle
            frm.MdiParent = Client.Parent
            frm.Show()
            SnapChild(frm)

            AddHandler frm.ResizeEnd, Sub()
                                          SnapChild(frm)
                                      End Sub
            AddHandler frm.LocationChanged, Sub()
                                                If frm.WindowState = FormWindowState.Normal Then
                                                    snapRectangle = SnapToGrid(frm)
                                                    Client.Refresh()
                                                End If
                                            End Sub
        End Sub

        Private Shared Sub SnapChild(ByVal frm As Form)
            If frm.WindowState = FormWindowState.Normal Then
                Dim rc As Rectangle = SnapToGrid(frm)
                frm.Bounds = rc
                snapRectangle = Nothing
                Client.Refresh()
            End If
        End Sub

        Private Shared snapRectangle? As Rectangle

        Private Shared Sub Client_Paint(sender As Object, e As PaintEventArgs) Handles Client.Paint
            If snapRectangle.HasValue Then
                e.Graphics.DrawRectangle(Pens.Black, snapRectangle)
            End If
        End Sub

    End Class

End Class
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • It's a great starting point, thanks. I'm trying now to modify it to allow various fixed sized forms (sizes are proportional to screen size for example multiple of 640 for width and multiple of 360 for heigth) and I don't know how to do it. I also need to avoid forms to overlap others. – mferre Aug 11 '20 at 09:58