views:

20

answers:

1

Hi, I am looking for the best way to dynamically arrange the location of labels (or any control for that matter) during runtime in a Windows Forms application using VB.net or C# in VS2008.

I have a control that displays messages created by users. This control has a panel docked at the top which holds the header information for each message, called pnlHeader

There are 8 labels: 4 of them display static descriptions (e.g. "To", "From", "Created Date", "Completed Date") And the other 4 display the relevant data for those descriptions. (Passed in during runtime from a MS SQL 2005 DB). Static labels are named: lblCreatedDateLbl and dynamic ones: lblCreatedDate

The application runs on monitors of various size and resolution, therefor I would like for all of the labels to arrange themselves with equal spacing in pnlHeader, based on the current width of the panel.

At first, I had simply created the labels in the Designer and used Anchors (half of the lables were set to (Top, Left) and the other half to (Top, Right)). This solution worked for most scenarios, but did not always provide a consistent solution, so I decided to add the controls in code instead.

I thought it would be easier to work with the labels by creating a panel for each corresponding pair. So pnlCreatedDate would hold lblCreatedDateLbl and lblCreatedDate

I wrote 2 methods: 1 to define each label and add it to the relevant panel: Sub AddLabels and another to determine the width of the panels and set the correct location: Sub SetLoc

Currently, AddLabels runs in the constructor after InitializeComponent() and SetLoc is called after the data has been passed in from the database.

I've tried making minor changes and tweaks to the width and size parameters, enabling and disabling AutoSize, but nothing returns a consistent solution, instead either all the description labels are misplaced, or do not appear at all or completely whack locations come up. If I'm using the wrong approach altogether, could someone please suggest how to best handle this task?

Below are the methods I use. I would extremely appreciate any assistance on how to perform this most efficiently and effectively. I am a newb coder, but would love to learn. So any help would be great and I apologize in advance for such a basic question and long description.

   Private Sub AddLabels()

    'Label Created By:'
    lblCreatedByLbl.Location = New Point(0, 0)
    lblCreatedByLbl.AutoSize = True
    lblCreatedByLbl.Anchor = AnchorStyles.None
    lblCreatedByLbl.ForeColor = Color.FromKnownColor(KnownColor.ControlDark)
    lblCreatedByLbl.Font = New Font(New FontFamily("Microsoft Sans Serif"), 7, FontStyle.Bold, GraphicsUnit.Point)
    lblCreatedByLbl.Text = "By:"

    lblCreatedBy.Location = New Point((lblCreatedByLbl.Location.X + (lblCreatedByLbl.Width)), 0)
    lblCreatedBy.AutoSize = True
    lblCreatedBy.Anchor = AnchorStyles.None
    lblCreatedBy.Text = "Source"
    lblCreatedBy.ForeColor = Color.FromKnownColor(KnownColor.ControlDarkDark)
    lblCreatedBy.Font = New Font(New FontFamily("Microsoft Sans Serif"), 7, FontStyle.Bold, GraphicsUnit.Point)

    pnlCreatedBy.AutoSize = False
    pnlCreatedBy.Controls.Add(lblCreatedByLbl)
    pnlCreatedBy.Controls.Add(lblCreatedBy)
    pnlCreatedBy.Anchor = AnchorStyles.None

    'Label Target'
    lblTargetLbl.Location = New Point(0, 0)
   '... The same idea as above for each label'

End Sub


Private Sub SetLoc()

    pnlCreatedBy.Width = lblCreatedByLbl.Width + lblCreatedBy.Width
    pnlTarget.Width = lblTargetLbl.Width + lblTarget.Width
    pnlCreateDate.Width = lblCreateDateLbl.Width + lblCreateDate.Width
    pnlCompletedDate.Width = lblCompletedDateLbl.Width + lblCompletedDate.Width

    Dim loc As Integer = 0
    Dim x As Integer = (pnlHeader.Width - pnlCreatedBy.Width - pnlTarget.Width - pnlCreateDate.Width - pnlCompletedDate.Width) / 5

    loc += x
    pnlCreatedBy.Location = New Point(loc, 0)
    loc += pnlCreatedBy.Width + x
    pnlTarget.Location = New Point(loc, 0)
    loc += pnlTarget.Width + x
    pnlCreateDate.Location = New Point(loc, 0)
    loc += pnlCreateDate.Width + x
    pnlCompletedDate.Location = New Point(loc, 0)
    loc += pnlCompletedDate.Width + x

    pnlHeader.Controls.Add(pnlCreatedBy)
    pnlHeader.Controls.Add(pnlTarget)
    pnlHeader.Controls.Add(pnlCreateDate)
    pnlHeader.Controls.Add(pnlCompletedDate)

    pnlCreatedBy.BringToFront()
    pnlTarget.BringToFront()
    pnlCreateDate.BringToFront()
    pnlCompletedDate.BringToFront()

End Sub
+1  A: 

Use a TableLayoutPanel in your UI.

You can then Dock the TableLayoutPanel to the center of the Form (thus filling the Window).

Once you have the TableLayoutPanel in place, you can configure the Rows/Columns to either be a fixed size or a percentage of the total.

You then place your Labels (and other UI controls) into the cells in the TabelLayoutPanel with appropriate Anchors/Docks set to where the control belongs in the Cell. Now, as you resize the window, your controls will be resized/moved automatically for you based on your TableLayoutPanel definition.

There's also a decent demo of the TableLayoutPanel in VB.NET at:

MSDN - Advanced Basics: TableLayoutPanels

Justin Niessner
Thank you very much, this does seem like the logical control for the task.In general, would you say it's a bad idea to attempt to manipulate the visual aspect of the UI in code? (As opposed to dealing with it in designer where possible.)
AndalusianCat
@AndalusianCat - Neither is perfect. The Designer is nice, but it can generate some ugly code. My suggestion is to use the Designer to get things close and then go to the code and remove the ugly hard-coded values with something that encourages better resize/flow.
Justin Niessner