I have a parent window that has a panel on the left hand side into which some buttons are placed. There is also a splitter added, so that the user can adjust the size of this panel. When a user presses one of the buttons, a form should appear on the right hand size - defaulting to the width of the remaining area of the window. In order to work out how wide this child form needs to be, I have taken the client width and subtracted the width of the panel and the splitter from this, however it is always slightly too big. I can simply subtract an additional 4 from the calculation to get it to work - but this feels unstable to me, as I don't know where those 4 pixels have come form! How do I calculate this correctly. My Code is below.
Dim xPos As Integer = Me.Panel1.Width + Me.Splitter1.Width
Dim yPos As Integer = 0
Dim childFormWidth As Integer = Me.ClientSize.Width - xPos
Dim childFormHeight As Integer = 200
myChildForm.Show()
myChildForm.Location = New Point(xPos, yPos)
myChildForm.Size = New Size(ChildFormWidth, myHeight)
Thanks Paul.
Related
I have a user control in VB.Net VS2019. It is used to display a description, a value and units. It generally works if the description is not too large.
The controls in the user control are all labels.
In the resize event it sizes the descriptions width to 66% of the overall width, 22% for the value, and whatever is left over for the units.
Then it set the description's left to 0, the value's left to the width of the description (plus a little). For the unit's position, it adds the left position of the value plus its width and a little.
But when the program runs the controls overlap each other and do not take the entire space of the UserControl.
For example the UserControl is 236 wide, but it looks like everything is squished to about 1/2 or 2/3s.
I have set Anchor to none and docking to none.
I perform these calculations in the 'Resize' event as shown below. The code was ported from an old VB6 program.
Is the 'Auto' size property taking precedence over the width I specify?
Private Sub UserControl1_Resize(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Resize
Dim lblUnits_Size As Integer
Label1.Height = MyBase.Height
Label2.Height = MyBase.Height
lblUnits.Height = MyBase.Height
Label1.Width = CInt(MyBase.Width * 0.6167)
Label2.Width = CInt(MyBase.Width * 0.22)
' I want the width of the units to be whatever is left over
lblUnits_Size = MyBase.Width - (Label1.Width + Label2.Width) - 6
If lblUnits_Size <= 0 Then lblUnits_Size = 1
lblUnits.Width = lblUnits_Size ' was -> TwipsToPixelsX(lbUnits_Size)
Label1.Left = 0
Label2.Left = (Label1.Left + Label1.Width) + 3
lblUnits.Left = (Label2.Left + Label2.Width) + 3
End Sub
I found it, the resize code needs to be called after the description changes. I guess ole VB6 did this automatically or someway the resize's were called after.
A snippet of my code:
Dim G As Graphics
Dim BBG As Graphics
Dim BB As Bitmap
Dim R As Rectangle
..................................................................
picMainScreen.Visible = True
G = picMainScreen.CreateGraphics
BB = New Bitmap(picMainScreen.Width, picMainScreen.Height)
For x = 0 To 256 * 3 - 1 Step 24
For y = 0 To 240 * 3 - 1 Step 24
R = New Rectangle(New Point(x, y), New Point(24, 24))
G.DrawRectangle(Pens.Black, R)
Next
Next
In this snippet of code, picMainScreen was a PictureBox that was originally not supposed to be visible.
Then through some conditions, picMainScreen was SUPPOSED to TURN Visible.
And THEN, the code draws all the rectangles onto the picture.
However, that isn't the case: the rectangles are first drawn onto the picture, and THEN the picture becomes visible.
Why does this happen? And what's the remedy?
Your rectangle instantiation is interesting since you are using two parameters of type Point.
R = New Rectangle(New Point(x, y), New Point(24, 24))
The Microsoft Docs show these parameter types;
Rectangle(Point, Size)
Initializes a new instance of the Rectangle class with the specified location and size.
https://learn.microsoft.com/en-us/dotnet/api/system.drawing.rectangle.-ctor?view=netframework-4.7.2
I'm not sure if the parameter types are the problem; however you are mixing drawing graphics with a picture control and they might not cohabitate in the same space well.
You could try calling DoEvents() after making the picture control visible to force it to be displayed first.
I'm writing a program that will create and load pictureboxs at run time. The problem is that they will not show up or display anything. Not sure what i'm doing wrong. i have checked and the full path for the images are correct.
Sub DrawScreen()
'Call g.DrawImage to draw whatever you want in here
Dim layers(Me.ListBox1.Items.Count) As PictureBox
For i = 0 To ListBox1.Items.Count - 1
'Create New Layer as picturebox
layers(i) = New PictureBox
layers(i).Parent = Me.picWatch
layers(i).BackColor = Color.Transparent
layers(i).Visible = True
Select Case ListBox1.Items(i)
Case "image"
'Debug.Print(ListofLayers(i).Full_Path)
layers(i).Image = Image.FromFile(ListofLayers(i).Full_Path)
layers(i).Top = ListofLayers(i).X
picWatch.Controls.Add(layers(i))
Case "shape"
'Dim g As Graphics
'g.DrawRectangle()
Case "text"
Dim g As Graphics = layers(i).CreateGraphics
g.DrawString(ListofLayers(i).Text, New Font("Arial", 12), Brushes.White, ListofLayers(i).X, ListofLayers(i).Y)
Case Else
Debug.Print(ListBox1.Items(i))
End Select
Next
Me.Refresh()
End Sub
You never add the picture boxes to the form. Call Me.Controls.Add():
Me.Controls.Add(layers(i))
And as already pointed out by LarsTech:
You seem to have a typo here:
layers(i).Top = ListofLayers(i).X
X is the coordinate for the control's horizontal position and is the same as .Left.
Y however is the coordinate for the control's vertical position, which is the same as .Top.
Using CreateGraphics is a bad idea. For starters, what you draw with it will get removed when the control is redrawn. And since you're not disposing it you'll also have memory leaks.
Subscribe to the Paint event for each picture box instead and do all drawing in there.
Finally, just a little note: Array declarations in VB.NET does not specify how many items there are to be in the array, but to what index the array should end at. And since arrays are zero-based, this:
Dim layers(Me.ListBox1.Items.Count) As PictureBox
...is equal to this:
Dim layers(0 To Me.ListBox1.Items.Count) As PictureBox
Thus the array will contain ListBox1.Items.Count plus one since what's inside the parentheses merely specify the lower and/or upper bound.
To create an array with the "correct" amount of items you should always specify the size minus one:
Dim layers(Me.ListBox1.Items.Count - 1) As PictureBox
So I have a picture box with an image. I have coordinates to draw certain boxes (as in multiple rectangles) on the image (from a device).
Dim rectPoint_Start As Point =
New Point((newStartPoint_X * pbZoneImage.Width / resWidth),
newStartPoint_Y * pbZoneImage.Height / resHeight)
Dim rectPoint_End As Point =
New Point((newEndPoint_X * pbZoneImage.Width / resWidth),
newEndPoint_Y * pbZoneImage.Height / resHeight)
Dim rectangleHeight As Integer = (rectPoint_End.Y - rectPoint_Start.Y)
Dim rectangleWidth As Integer = (rectPoint_End.X - rectPoint_Start.X)
'localize
camRect1 = New Rectangle(rectPoint_Start.X, rectPoint_Start.Y, _
rectangleWidth, rectangleHeight)
If camRect1 <> Nothing Then g.DrawRectangle(Pens.Blue, camRect1)
The rect coordinates are coming in scaled with a resolution, so you see me there changing it to a relative point value for the pbZoneImage picture box. The problem I am having is that the coordinates I'm receiving (from the device) are assuming the (0,0) point is in the bottom left, where the PB draws these boxes starting from the top left. Is there a way to change the orientation of the starting point being drawn in the g.drawRectangle()? Or another method.
What I have tried so far is to add (pbzoneImage.bottom) - ((newEndPoint_X * pbZoneImage.Width ....) in the rectPoint_End and rectPoint_Start points. It doesn't seem to work and gives me negative values.
Update:
So this is what I ended up doing to fix it. I ended up drawing out a coordinate system and doing some old school algebra based on some of the answers I received. (Reminder, pbZoneImage is a picture box, startpoint x and y along with endpoint x and y are the values from the device)
Dim rectangleHeight As Integer = Math.Floor((newEndPoint_Y - newStartPoint_Y) * pbZoneImage.Height)
Dim rectangleWidth As Integer = Math.Floor((newEndPoint_X - newStartPoint_X) * pbZoneImage.Height)
'flip rectangle
Dim rectPoint_Start As New Point((newStartPoint_X * pbZoneImage.Width), _
pbZoneImage.Height - (newStartPoint_Y * pbZoneImage.Height) - rectangleHeight)
Dim rectPoint_End As Point = New Point((newEndPoint_X * pbZoneImage.Width), _
pbZoneImage.Height - (newEndPoint_Y * pbZoneImage.Height) - rectangleHeight)
'localize
camRect1 = New Rectangle(rectPoint_Start.X, rectPoint_Start.Y, _
rectangleWidth, rectangleHeight)
Upon receiving newStartPoint_Y and newEndPoint_Y values from the device, do the following :
newStartPoint_Y = resHeight - newStartPoint_Y
newEndPoint_Y = resHeight - newEndPoint_Y
assuming your device gives you coordinates on a zero-based reference (where the point at the lower left corner has the coordinate (0,0) which would fit with the zero-based coordinates of the top left corner of a PictureBox)
If I understand well, newStartPoint_X/Y and newEndPoint_X/Y are coordinates directly from the device ?
I suggest you to take the habit to use either CInt() when using Point with integer X and Y values, or better : CSng() with PointF, since you're scaling coordinates anyway.
I believe you are forgetting to also adjust for the rectangle height. If the rectangle origin is the lower left and its position was mapped to the resized image coordinate system based upon image lower left origin then you must adjust for the resized image height and the mapped rectangle height. Sketch it out on paper to get the relationships right.
I've got a list of System.Drawing.RectangleF objects that all overlap the same RectangleF object. In my picture below, the 3 overlapping rectangles would be the pink, yellow, and red rectangles. My main rectangle in question is the light blue rectangle.
Second Image:
I know that with RectangleF objects I can use the Intersect() method that will return me another RectangleF object representing the overlap. But as far as I can tell, this only really works when comparing two rectangles.
My question is: How could I determine the TOTAL area/percentage (i.e. the combined total overlap of the red, yellow, and pink rectangles when compared to the light blue rectangle - but it would need to be smart enough to not count the area in which the red and yellow overlaps twice, and same for the pink and yellow)?
NOTE: The green lines represent the area I'm looking for, just the total area of the blue rectangle that is not visible.
UPDATE: I've added a 2nd image to further demonstrate what I'm looking for. In the second image, the presence of the burgundy rectangle should have no affect on the total percent covered because that area is already covered by the yellow and green rectangles.
OK I think I found a solution using a Region that seems to be working for both of my example images above:
Private Function TotalCoveredAreaPercent(ByVal oRectToCheck As RectangleF, ByVal oOverlappingRects As List(Of RectangleF)) As Double
Dim oRegion As New Region(oRectToCheck)
Dim dTotalVisibleArea As Double = 0
Dim dTotalCoveredArea As Double = 0
'now we need to exclude the intersection of our
'overlapping rectangles with our main rectangle:
For Each oOverlappingRect As RectangleF In oOverlappingRects
oRegion.Exclude(RectangleF.Intersect(oRectToCheck, oOverlappingRect))
Next
'now we have access to the non-overlapping
'rectangles that make up the visible area of our main rectangle:
Dim oVisibleRects As RectangleF()
oVisibleRects = oRegion.GetRegionScans(New Drawing2D.Matrix())
'add the area of the visible rectangles together
'to find the total visible area of our main rectangle:
For Each oVisibleRect As RectangleF In oVisibleRects
dTotalVisibleArea += AreaOf(oVisibleRect)
Next
Dim dPercentVisible As Double = dTotalVisibleArea / AreaOf(oRectToCheck) * 100
'percent covered is 100 - the visible percentage:
Return (100 - dPercentVisible)
End Function
This seems to be working pretty well, and is quite simple.
Here is my algorithm. The key point is that we are subtracting out overlaps of overlaps.
Dim baseRect As New RectangleF(10, 10, 20, 20)
Dim otherRectList As New List(Of RectangleF)
otherRectList.Add(New RectangleF(5, 5, 10, 10))
otherRectList.Add(New RectangleF(20, 20, 10, 10))
otherRectList.Add(New RectangleF(10, 5, 10, 10))
Dim overlapRectList As New List(Of RectangleF)
For Each otherRect As RectangleF In otherRectList
If RectangleF.Intersect(otherRect, baseRect) <> RectangleF.Empty Then
overlapRectList.Add(RectangleF.Intersect(otherRect, baseRect))
End If
Next
Dim totalArea As Single = 0
For Each overlapRect As RectangleF In overlapRectList
totalArea += overlapRect.Width * overlapRect.Height
Next
'Subtract out any overlaps that overlap each other
For i = 0 To overlapRectList.Count - 2
For j = i+1 To overlapRectList.Count - 1
If i <> j Then
If RectangleF.Intersect(overlapRectList(i), overlapRectList(j)) <> RectangleF.Empty Then
Dim dupeRect As RectangleF = RectangleF.Intersect(overlapRectList(i), overlapRectList(j))
totalArea -= dupeRect.Width * dupeRect.Height
End If
End If
Next
Next
I amended the code to take into account tcarvin's note. However, I have not plotted out the results on graph paper to see if this is fully correct. I will look at it as soon as I have additional time. Also note that I have not included any code to handle a situation with less than 2 intersections.