Is there any way to get and set the .Location of an UserControl, which is positioned on a Container (e.g. a Panel) relative to the "most-parental" Form?
I know that there is the possibility of calculating the offset of the Panel itself and adding it to the .Location of the UserControl.
But in my case the number of parent-levels is unknown and can differ from case to case.
So once the UserControl could be placed on a Panel which is directly on the Form. But there is also the possibility that the UserControl is placed on a 2nd Panel which is on the 1st Panel which is on the Form.
What if you take your idea of calculating the offset of the Panel and calculate the offset recursively back to the Form? i.e. I have a Textbox1 within a Panel2 within a Panel1. Panel1 is located at .Left 266, Panel2 is at .Left 77 within Panel1.
Private Function GetLeftOffset(ByVal UserControl As Control) As Int32
Dim intLeftOffset As Int32 = 0
If Not TypeOf UserControl.Parent Is Form Then
intLeftOffset = UserControl.Parent.Left
intLeftOffset += GetLeftOffset(UserControl.Parent)
End If
Return intLeftOffset
End Function
Now if I GetLeftOffset(Me.TextBox1), it returns an Offset of 343 (266 + 77).
I use this function to place a context menu near a control like a textbox or button. You can set x and y to zero to return the location of a control itself.
'--- Return the screen location of a control with an offset
Private Function Offset(ByRef controlObj As Control, ByVal x As Integer, ByVal y As Integer) As Point
Dim pt As Point
Dim parentObj As Control = controlObj.Parent
Do While parentObj IsNot controlObj.FindForm
x += parentObj.Location.X
y += parentObj.Location.Y
parentObj = parentObj.Parent
Loop
pt = PointToScreen(controlObj.Location)
pt.Offset(x, y)
Return pt
End Function
Try:
Dim pnt As Point
pnt = UserControl.PointToScreen(New Point(0, 0))
pnt = Me.PointToClient(pnt)
This calculates the location relative to your Form. Change Me to any control, if you like
Now, if you want to set the location eg (100, 100), relative to your Form
pnt = Me.PointToScreen(New Point(100, 100))
pnt = UserControl.Parent.PointToClient(pnt)
UserControl.Location = pnt
Remember that, if the new location is outside the parent area the control will not be visible.
Related
I have a app which is loading between 32^2 to 32768 8x8 px pictureboxes. All pictureboxes are on screen so I need to load them all and can't just load some.
As it stands, my program won't even run. Is there a better way to load that many pictureboxes?
I would like to share with you my project, but I don't know how to.............
Thanks though!
You'd likely run into a MemoryOverflowException with this design. From the sound of it, you're probably trying to render a map of some sort if that's the case then this answer is for you (otherwise just ignore it).
At a high level you should only create the number of PictureBox controls that can fit on the screen at any given time. You can calculate this with the following function:
Private Function CalculateSizeToFitParent(ByVal parent As Control, ByVal childSize As Size) As Size
Return New Size(parent.Width \ childSize.Width, parent.Height \ childSize.Height)
End Sub
You would implement it as such to create a PictureBox to fill up the visible area of the current Form:
Dim pictureBoxSize As Size = New Size(8, 8)
Dim visibleArea(pictureBoxSize.Width - 1, pictureBoxSize.Height - 1) As PictureBox
Dim numberOfPictureBoxes As Size = CalculateSizeToFitParent(Me, pictureBoxSize)
For x As Integer = 0 To numberOfPictureBoxes.Width - 1
For y As Integer = 0 To numberOfPictureBoxes.Height - 1
visibleArea(x, y) = New PictureBox() With {
.Location = New Point(x * pictureBoxSize.Width, y * pictureBoxSize.Height)
.Size = pictureBoxSize
}
Me.Controls.Add(visibleArea(x, y))
Next
Next
The next part is two-fold:
You need to keep track of where the top-left corner of the current visible are is
You will need to reload the images in the respective visual area of the map.
This assumes that you have a 2D array that stores your images. And please note that you don't recreate the PictureBox controls but rather you just reload the image of the existing control:
Private _currentLocation As Point = New Point(0, 0) ' If you're starting somewhere else change it here
Public Property CurrentLocation As Point
Get
Return _currentLocation
End Get
Set(ByVal value As Point)
If (value <> _currentLocation) Then
_currentLocation = value
Me.OnCurrentLocationChanged()
End If
End Set
End Property
Protected Overridable Sub OnCurrentLocationChanged()
RaiseEvent CurrentLocationChanged(Me, EventArgs.Empty)
End Sub
Public Event CurrentLocationChanged(ByVal sender As Object, ByVal e As EventArgs)
Private Sub MyForm_CurrentLocationChanged(ByVal sender As Object, ByVal e As EventArgs) Handles Me.CurrentLocationChanged
If (visibleArea Is Nothing) Then
Throw New Exception("The visible area has not been generated yet.")
End If
If (_currentLocation Is Nothing) Then
Throw New Exception("The CurrentLocation cannot be null.")
End If
Dim widthUpperBounds As Integer = My2DArrayOfImageLocations.GetUpperBounds(0) - 1
Dim heightUpperBounds As Integer = My2DArrayOfImageLocations.GetUpperBounds(1) - 1
For x As Integer = 0 To visibleArea.GetUpperBounds(0) - 1
For y As Integer = 0 To visibleArea.GetUpperBounds(1) - 1
If (x + _currentLocation.Width > widthUpperBounds OrElse y + _currentLocation.Height) Then
'This "block" is outside the view area (display a blank tile?)
Else
visibleArea(x, y).Load(My2DArrayOfImageLocations(x + _currentLocation.Width, y + _currentLocation.Height))
End If
Next
Next
End Sub
Now whenever you reset the CurrentLocation property (however you'd do that, e.g. arrow keys, asdw, etc.) it will redraw the visible area of the map.
Update
Please note that I "free-typed" this example and you may need to tweak it a bit. After some more thought, you'll probably also need to call the Refresh method of the PictureBox when you load in the image (I didn't test).
I have a Form called formmain. When I open another Form, formmain is minimized.
My problem is that when I restore formmain, the Location and Size of the controls on it have changed completely.
I am using the following method when the Form is resized:
Public Sub ResizeForm(ObjForm As Form, DesignerHeight As Integer, DesignerWidth As Integer)
'#Region "Code for Resizing and Font Change According to Resolution"
'Specify Here the Resolution Y component in which this form is designed
'For Example if the Form is Designed at 800 * 600 Resolution then DesignerHeight=600
Dim i_StandardHeight As Integer = DesignerHeight
'Specify Here the Resolution X component in which this form is designed
'For Example if the Form is Designed at 800 * 600 Resolution then DesignerWidth=800
Dim i_StandardWidth As Integer = DesignerWidth
Dim i_PresentHeight As Integer = Screen.PrimaryScreen.Bounds.Height
'Present Resolution Height
Dim i_PresentWidth As Integer = Screen.PrimaryScreen.Bounds.Width
'Presnet Resolution Width
f_HeightRatio = CSng(CSng(i_PresentHeight) / CSng(i_StandardHeight))
f_WidthRatio = CSng(CSng(i_PresentWidth) / CSng(i_StandardWidth))
ObjForm.AutoScaleMode = AutoScaleMode.None
'Make the Autoscale Mode=None
ObjForm.Scale(New SizeF(f_WidthRatio, f_HeightRatio))
For Each c As Control In ObjForm.Controls
If c.HasChildren Then
ResizeControlStore(c)
Else
c.Font = New Font(c.Font.FontFamily, c.Font.Size * f_HeightRatio, c.Font.Style, c.Font.Unit, CByte(0))
End If
Next
ObjForm.Font = New Font(ObjForm.Font.FontFamily, ObjForm.Font.Size * f_HeightRatio, ObjForm.Font.Style, ObjForm.Font.Unit, CByte(0))
End Sub
I am trying to resize/ re-position controls based on the size of the form. This is the class i am using :
Public Class Resizer
'----------------------------------------------------------
' ControlInfo
' Structure of original state of all processed controls
'----------------------------------------------------------
Private Structure ControlInfo
Public name As String
Public parentName As String
Public leftOffsetPercent As Double
Public topOffsetPercent As Double
Public heightPercent As Double
Public originalHeight As Integer
Public originalWidth As Integer
Public widthPercent As Double
Public originalFontSize As Single
End Structure
'-------------------------------------------------------------------------
' ctrlDict
' Dictionary of (control name, control info) for all processed controls
'-------------------------------------------------------------------------
Private ctrlDict As Dictionary(Of String, ControlInfo) = New Dictionary(Of String, ControlInfo)
'----------------------------------------------------------------------------------------
' FindAllControls
' Recursive function to process all controls contained in the initially passed
' control container and store it in the Control dictionary
'----------------------------------------------------------------------------------------
Public Sub FindAllControls(thisCtrl As Control)
'-- If the current control has a parent, store all original relative position
'-- and size information in the dictionary.
'-- Recursively call FindAllControls for each control contained in the
'-- current Control
For Each ctl As Control In thisCtrl.Controls
Try
If Not IsNothing(ctl.Parent) Then
Dim parentHeight = ctl.Parent.Height
Dim parentWidth = ctl.Parent.Width
Dim c As New ControlInfo
c.name = ctl.Name
c.parentName = ctl.Parent.Name
c.topOffsetPercent = Convert.ToDouble(ctl.Top) / Convert.ToDouble(parentHeight)
c.leftOffsetPercent = Convert.ToDouble(ctl.Left) / Convert.ToDouble(parentWidth)
c.heightPercent = Convert.ToDouble(ctl.Height) / Convert.ToDouble(parentHeight)
c.widthPercent = Convert.ToDouble(ctl.Width) / Convert.ToDouble(parentWidth)
c.originalFontSize = ctl.Font.Size
c.originalHeight = ctl.Height
c.originalWidth = ctl.Width
ctrlDict.Add(c.name, c)
End If
Catch ex As Exception
Debug.Print(ex.Message)
End Try
If ctl.Controls.Count > 0 Then
FindAllControls(ctl)
End If
Next '-- For Each
End Sub
'----------------------------------------------------------------------------------------
' ResizeAllControls
' Recursive function to resize and reposition all controls contained in the Control
' dictionary
'----------------------------------------------------------------------------------------
Public Sub ResizeAllControls(thisCtrl As Control, Optional wform As Form = Nothing)
Dim fontRatioW As Single
Dim fontRatioH As Single
Dim fontRatio As Single
Dim f As Font
If IsNothing(wform) = False Then wform.Opacity = 0
'-- Resize and reposition all controls in the passed control
For Each ctl As Control In thisCtrl.Controls
Try
If Not IsNothing(ctl.Parent) Then
Dim parentHeight = ctl.Parent.Height
Dim parentWidth = ctl.Parent.Width
Dim c As New ControlInfo
Dim ret As Boolean = False
Try
'-- Get the current control's info from the control info dictionary
ret = ctrlDict.TryGetValue(ctl.Name, c)
'-- If found, adjust the current control based on control relative
'-- size and position information stored in the dictionary
If (ret) Then
'-- Size
ctl.Width = Int(parentWidth * c.widthPercent)
ctl.Height = Int(parentHeight * c.heightPercent)
'-- Position
ctl.Top = Int(parentHeight * c.topOffsetPercent)
ctl.Left = Int(parentWidth * c.leftOffsetPercent)
'-- Font
f = ctl.Font
fontRatioW = ctl.Width / c.originalWidth
fontRatioH = ctl.Height / c.originalHeight
fontRatio = (fontRatioW +
fontRatioH) / 2 '-- average change in control Height and Width
ctl.Font = New Font(f.FontFamily,
c.originalFontSize * fontRatio, f.Style)
End If
Catch
End Try
End If
Catch ex As Exception
End Try
'-- Recursive call for controls contained in the current control
If ctl.Controls.Count > 0 Then
ResizeAllControls(ctl)
End If
Next '-- For Each
If IsNothing(wform) = False Then wform.Opacity = 1
End Sub
End Class
The problem with this code is that
1) It flickers a lot while resizing the controls or moving them around.
2) Some labels and buttons are moved around to random positions on the form and,
3) the size of the background image is not responsive to the size of the form (Not much knowledge on how to execute this.)
Any sort of help is appreciated.
Instead of manually doing this, look into properties on the form such as "Dock", "Anchor".
Examples:
Setting dock to top (or bottom) will allow a module to always cover
the entire top (or bottom) of the form, keeping the height the same.
Setting dock to left (or right) will allow a module to always cover the entire left (or right) of the form, keeping the height the
same.
Setting the anchor property to all of Top, Left, Right, Down will allow the control to expand as the form grows, but keep it's x/y top-left position.
Setting the anchor property to Top, Left, Right will allow the control to expand to the right as the form grows, but keep it's x/y top-left position
Setting the anchor property to Bottom, Right will allow the control to stay in a fixed position relative to the bottom corner of the form.
I have a number of buttons between 5-20 and it's variable each time the form loads based on the user settings. I am trying to fit all these buttons on my form no matter what the size of the form is. The buttons are generated during runtime. I would like the first button to be 20 points from the top bar (at any size) and the rest of the buttons simply to fit in the form. This is what I have now but I have to maximize the form to view them all and also while I'm expanding the form the space between the buttons decreases and they overlap with each other whereas they should keep a relative distance. Any ideas?
Dim iButtonWidth, iButtonHeight, iVerticalSpace As Integer
If UserButtons.Count > 0 Then
iButtonHeight = Me.Size.Height - (Me.Size.Height * 0.85)
iButtonWidth = Me.Size.Width - (Me.Size.Width * 0.5)
iVerticalSpace = iButtonHeight / 3
For Each btn In UserButtons
btn.Size = New System.Drawing.Size(iButtonWidth, iButtonHeight)
btn.Location = New Point(20, 20 + btn.TabIndex * iVerticalSpace * 3)
Next
End If
Here's a quick example using the TableLayoutPanel to play with:
Public Class Form1
Private UserButtons As New List(Of Button)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Static R As New Random
Dim NumButtons As Integer = R.Next(5, 21) ' "a number of buttons between 5-20 and it's variable each time"
UserButtons.Clear()
For i As Integer = 1 To NumButtons
Dim btn As New Button()
btn.Text = i.ToString("00")
btn.Dock = DockStyle.Fill ' Optional: See how you like it with this on vs. off
UserButtons.Add(btn)
Next
DisplayButtons()
End Sub
Private Sub DisplayButtons()
TableLayoutPanel1.SuspendLayout()
TableLayoutPanel1.Controls.Clear()
TableLayoutPanel1.ColumnStyles.Clear()
TableLayoutPanel1.ColumnCount = 5 ' Fixed Number of Columns
For i As Integer = 1 To TableLayoutPanel1.ColumnCount
TableLayoutPanel1.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 911)) ' the size doesn't matter here, as long as they are all the same
Next
' Variable Number of Rows:
Dim RowsRequired As Integer = ((UserButtons.Count - 1) \ TableLayoutPanel1.ColumnCount) + 1 ' Integer Division
TableLayoutPanel1.RowStyles.Clear()
TableLayoutPanel1.RowCount = RowsRequired
For i As Integer = 1 To TableLayoutPanel1.RowCount
TableLayoutPanel1.RowStyles.Add(New RowStyle(SizeType.Percent, 911)) ' the size doesn't matter here, as long as they are all the same
Next
TableLayoutPanel1.Controls.AddRange(UserButtons.ToArray)
TableLayoutPanel1.ResumeLayout()
End Sub
End Class
First of all what kind of container are the buttons in? You should be able to set the container's AutoScroll property to true - then when controls within it spill out of the visible bounds you will get a scroll bar.
Then also what you could do is draw each button in more of a table with a certain number next to each other before dropping down to the next line (instead of just 1 button on each line). If that is an option that works for you then you could get more buttons within the visible space.
I happen to have an example to do the same thing with picture boxes (and text boxes under each picture box). Hope this helps:
Dim point As New Point(0, 0)
'create 11 picture and text boxes-you can make this number the number your user selects.
Dim box(11) As PictureBox
Dim text(11) As TextBox
Dim i As UInt16
For i = 0 To 11 'or whatever your number is
box(i) = New PictureBox
box(i).Width = 250 'your button width
box(i).Height = 170 'your button height
box(i).BorderStyle = BorderStyle.FixedSingle
box(i).Location = point
layoutsPanel.Controls.Add(box(i)) 'my container is a panel
text(i) = New TextBox
text(i).Height = 50
text(i).Width = 250
point.Y += box(i).Height
text(i).Location = (point)
layoutsPanel.Controls.Add(text(i))
point.Y -= box(i).Height 'reset Y for next picture box
'Put 4 picture boxes in a row, then move down to next row
If i Mod 4 = 3 Then
point.X = 0
point.Y += box(i).Height + text(i).Height + 4
Else
point.X += box(i).Width + 4
End If
Next
I have a Custom Control for viewing images with zooming facility but without scrollBar, and its working condition is good. Actually The Custom Control is a Panel with a Picutrbox on it. I also using a TrackBar for zooming in/out the image. It is also working better.
But I am not fully satisfied, even it is covering the purpose of my App because I need zooming PictureBox based on center point. Now it is anchoring Top Left.
Another One is when Zooming out the image, the image goes to zero size at TrackBar's Zero. Even I limited zoom level to the panel size and working good, my unsatisfaction taking place here also, as it is not responding at zero level of TrackBar. Here I need, the original size of image loaded in picture box have to go for 100% of TrackBar and when image reached at Custom controle size have to go for 0% of TrackBar. Then I will fullfilled.
I figuring my code here.........
My Custom Control is a User Control Inheriting from Panel.
code for cutom control :
Public Class ImageViewer
Inherits Panel
Dim AutoScaleDimensions As SizeF
Dim AutoScaleMode As AutoScaleMode
Protected Overrides Sub DefWndProc(ByRef m As Message)
If m.Msg <> 131 Then
MyBase.DefWndProc(m)
End If
End Sub
End Class
On Form1, I Placed my Custom Control- ImageViewer1 and also placed PicutreBox1 with in ImageViewer1, Palced Button1 and TrackBar1 on Form1
Changed Properties as follows
ImageViewer1 - AutoScroll=True
PicutreBox1 - SizeMode=Zoom
TrackBar1- Maximum=100
My Declared Variables are
Dim imgName As String
Private SliderCenter As Integer = 50
Private originalImg As Bitmap
Code for Button1.Click
Try
Dim inputImg As FileDialog = New OpenFileDialog()
inputImg.Filter = "Image File (*.Jpg;*.Bmp;*.Png;*.Gif;*.Tiff;*.Tif;*.PDF)|*.Jpg;*.Bmp;*.Png;*.Gif;*.Tiff;*.Tif;*.PDF"
If inputImg.ShowDialog() = DialogResult.OK Then
imgName = inputImg.FileName
originalImg = New Bitmap(inputImg.FileName)
Dim newImg As New Bitmap(imgName)
PictureBox1.Image = DirectCast(newImg, Image)
End If
inputImg = Nothing
Catch ae As System.ArgumentException
imgName = ""
MessageBox.Show(ae.Message.ToString)
Catch ex As Exception
MessageBox.Show(ex.Message.ToString)
End Try
Code for TrackBar1.ValueChanged
If originalImg IsNot Nothing Then
If TrackBar1.Value > 0 Then
Dim scale As Double = TrackBar1.Value
Dim height As Integer = Convert.ToInt32((scale / SliderCenter) * originalImg.Height)
Dim width As Integer = Convert.ToInt32((scale / SliderCenter) * originalImg.Width)
PictureBox1.Size = New Size(width, height)
If PictureBox1.Width <= ImageViewer1.Width Then
PictureBox1.Size = New Size(PictureBox1.Width + (ImageViewer1.Width - PictureBox1.Width), ImageViewer1.Height)
End If
If PictureBox1.Height <= ImageViewer1.Height Then
PictureBox1.Size = New Size(ImageViewer1.Width, PictureBox1.Height + (ImageViewer1.Height - PictureBox1.Height))
End If
End If
End If
Please help me in your kind ........ Thank You.