I have one form and I want it to be fullscreen, but taskbar should be still visible. And I want it to have one Panel on it, whose borders are 10px away from form borders
I tried hundreds of combinations, and I simply can't achieve this.
here's my code
Public Class Form1
Sub New()
InitializeComponent()
WindowState = FormWindowState.Maximized
Size = New Size(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height)
Dim p1 As New Panel()
p1.Location = New Point(10, 10)
p1.Size = New Size(ClientSize.Width - 20, ClientSize.Height - 20)
p1.BackColor = Color.Blue
Controls.Add(p1)
End Sub
End Class
what I want: http://i.imgur.com/4BxoBeh.png
what I get: http://i.imgur.com/QynIdaU.png
I would take an entirely different approach where there is no need to calculate anything:
WindowState = FormWindowState.Maximized
Size = New Size(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height)
Padding = New Padding(10)
Dim p1 As New Panel()
p1.Location = New Point(0, 0)
p1.Dock = DockStyle.Fill
p1.BackColor = Color.Blue
Controls.Add(p1)
Your calculation is correct for a form that takes entire screen but is not maximized, which you can see by unmaximizing it as soon as it appears. The reason is that you are observing the form sizes from the constructor which is a bit too early (namely, even though you are setting WindowState = FormWindowState.Maximized before everything else, ClientSize still has values corresponding to non-maximized window because the window has not yet been created and shown). If you move your original code to e.g. a Form.Load handler it will give the opposite result - looking correct when the form is maximized and incorrect if not.
The padding approach works as expected in all cases.
Related
My program automates a process on a website using selenium, HTTP requests, and an HTML parser known as HtmlAgilityPack.
There are parts of this process that require user input that can not be automatically filled out by hard-coded values. Allowing the user control over the website to fill in these inputs would be a mistake. It would be difficult to know when they are done inputting it and they would likely touch something they shouldn't which would cause errors when control of the website is handed back over to the program.
Instead, I download the page source of the website using selenium and parse the inputs out of the HTML with HtmlAgilityPack. Then controls matching those inputs are dynamically generated and added to a FlowLayoutPanel. The FlowLayoutPanel works correctly the first time. It displays all added controls. When the user is done they click on a button and the process continues.
Sometimes the process encounters errors that cause it to loop back to where the user needs to enter input again. The exact code that worked before is then run in the same subroutine again. It all runs without error and as expected. I have manually checked at runtime that the controls are added to the FlowLayoutPanel, that they have a size bigger than 0, 0, and that they are a visible color. Yet only one control ever shows up in the FlowLayoutPanel no matter how many should be visible.
The FlowLayoutPanel has it's AutoScroll property set to true and it has adequate space to add more controls. As I said this works fine the first time around just not the second. I've been stuck on this for a while and would appreciate some help. The code is going to be posted below for you to look at.
The code I am going to post has several FlowLayoutPanels. One that is on the Form permanently that was created in the designer and the others are generated dynamically to group up the controls.
The issue is with the permanent one. It shows one control as I said. This control is the first dynamically generated flow layout panel and all of it's children. It doesn't display any of the other FlowLayoutPanel controls that are added to it. Just whichever one is added first.
For those of you wondering about the line FLPOutOfStock.ImprovedClear() in the code bellow, The .Improved clear is an extension method I wrote. When Clearing the FlowLayoutPanel with the normal method, .Clear(), the controls get removed but they don't get disposed of. My method disposes of all the controls in the panel and then calls the Clear() method on the panel.
I realize that the Clear() method isn't necessary after they've been disposed of it's just a backup since nothing ever seems to work. I wanted to make sure the panel was fully reset.
Here is the code:
Private Sub UIOutOfStockState_Load()
Try
cmdOutOfStockDeleteAll.Enabled = False
cmdOutOfStockContinue.Enabled = False
FLPOutOfStock.ImprovedClear() 'The flowlayoutpanel that does not display all controls
With New WebDriverWait(ChromeDriver, TimeSpan.FromSeconds(20)).Until(Function(driver) CBool((CType(driver, IJavaScriptExecutor)).ExecuteScript("return jQuery.active == 0")))
End With
LBLOutOfStockErrors.Text = "Notice. The following items are out of stock. Please see below for product-specific availability dates. If you have any questions, please contact Customer Service at (800) 843-2020. All direct to patient orders will ship complete. The entire order will ship when out-of-stock product becomes available."
Dim OutOfStockDoc As New HtmlAgilityPack.HtmlDocument
OutOfStockDoc.LoadHtml(ChromeDriver.PageSource)
Dim OutOfStockProductNodes As HtmlAgilityPack.HtmlNodeCollection = OutOfStockDoc.DocumentNode.SelectNodes("//div[contains(#id,'id_detail_item_')]")
FLPOutOfStock.SuspendLayout()
If OutOfStockProductNodes IsNot Nothing Then
For Each OutOfStockProductNode As HtmlAgilityPack.HtmlNode In OutOfStockProductNodes
Dim FLPOutOfStockProduct As New FlowLayoutPanel
FLPOutOfStockProduct.SuspendLayout()
FLPOutOfStockProduct.FlowDirection = FlowDirection.LeftToRight
FLPOutOfStockProduct.AutoSize = False
FLPOutOfStockProduct.Size = New Size(420, 160)
FLPOutOfStock.Controls.Add(FLPOutOfStockProduct)
Dim WBProductText As New WebBrowser
WBProductText.Size = New Size(400, 120)
WBProductText.AllowNavigation = False
WBProductText.AllowWebBrowserDrop = False
WBProductText.IsWebBrowserContextMenuEnabled = False
WBProductText.ScriptErrorsSuppressed = True
WBProductText.ScrollBarsEnabled = True
WBProductText.Margin = New Padding(0, 0, 0, 0)
WBProductText.Padding = New Padding(0, 0, 0, 0)
WBProductText.Navigate("about:blank")
Dim ProductTextHtmlDoc As New HtmlAgilityPack.HtmlDocument
ProductTextHtmlDoc.LoadHtml(OutOfStockProductNode.OuterHtml)
ProductTextHtmlDoc.DocumentNode.SelectSingleNode("/descendant::a[#id='patient-outOfStock-delete-item']").Remove()
WBProductText.DocumentText = ProductTextHtmlDoc.DocumentNode.OuterHtml
FLPOutOfStockProduct.Controls.Add(WBProductText)
FLPOutOfStockProduct.SetFlowBreak(WBProductText, True)
Dim SpaceReducer0 As New Panel
SpaceReducer0.Size = New Size(0, 0)
FLPOutOfStockProduct.Controls.Add(SpaceReducer0)
Dim cmdProductDeleteButton As New Button
cmdProductDeleteButton.Text = "Delete"
cmdProductDeleteButton.BackColor = Color.White
cmdProductDeleteButton.ForeColor = Color.Black
cmdProductDeleteButton.Margin = New Padding(0, 0, 0, 0)
cmdProductDeleteButton.Padding = New Padding(0, 0, 0, 0)
cmdProductDeleteButton.Size = New Size(400, 20)
Dim OnClickAttributeValueWithQuoteEscaping As String = OutOfStockProductNode.SelectSingleNode("/descendant::a[#id='patient-outOfStock-delete-item']").GetAttributeValue("onclick", "")
cmdProductDeleteButton.Tag = "//a[#id='patient-outOfStock-delete-item' and (#onclick=""" & OnClickAttributeValueWithQuoteEscaping & """)]"
AddHandler cmdProductDeleteButton.Click, AddressOf cmdProductDeleteButton_Click
FLPOutOfStockProduct.Controls.Add(cmdProductDeleteButton)
FLPOutOfStockProduct.SetFlowBreak(cmdProductDeleteButton, True)
Dim SpaceReducer1 As New Panel
SpaceReducer1.Size = New Size(0, 0)
FLPOutOfStockProduct.Controls.Add(SpaceReducer1)
FLPOutOfStockProduct.ResumeLayout()
Next
FLPOutOfStock.ResumeLayout()
FLPOutOfStock.Refresh()
End If
cmdOutOfStockDeleteAll.Enabled = True
cmdOutOfStockContinue.Enabled = True
Catch Ex As Exception
cmdOutOfStockDeleteAll.Enabled = True
cmdOutOfStockContinue.Enabled = True
End Try
End Sub
So I’ve been asked to show what is in the improved clear method. Here it is:
<Extension()>
Public Function ImprovedClear(ByRef Control as Control)
For Each controlchild as control in control.controls
Control.Dispose()
Next
Control.Controls.Clear()
Return Nothing
End Function
I'm trying to code it so that i can create a picture box from a method in a class. However when my picture box is drawn it doesn't display any image, it only shows a white square of the specified dimensions in the specified location.
Here is the code which i am using to create said picture box:
Public Sub DrawEnemy(ByRef formInstance)
Dim enemypic As New PictureBox
enemypic.Image = Image.FromFile("C:\fboi1\Enemy.Png")
enemypic.Width = 64
enemypic.Height = 64
enemypic.Location = New Point(Me.EnemyPosX, EnemyPosY)
enemypic.Visible = True
formInstance.Controls.Add(enemypic)
End Sub
And here is where i am calling the method from:
Dim Enemy1 As New computerControlled(1, 1)
Enemy1.DrawEnemy(Me)
Please add the following code in your DrawEnemy() method:
enemypic.SizeMode = PictureBoxSizeMode.StretchImage
When i drag the console window suddenly the white box turns into the image i wanted.
Aha! This means the code is not causing the form to be repainted. We can trigger that by calling the Invalidate() function.
Public Sub DrawEnemy(formInstance As Form)
Dim enemypic As New PictureBox
enemypic.Image = Image.FromFile("C:\fboi1\Enemy.Png")
enemypic.Width = 64
enemypic.Height = 64
enemypic.Location = New Point(Me.EnemyPosX, EnemyPosY)
enemypic.Visible = True
formInstance.Controls.Add(enemypic)
formInstance.Invalidate()
End Sub
If you're calling this several times in a loop, you would instead handle this after the loop, where you also block repainting (to prevent flickering) until the loop is finished.
form.SuspendLayout()
For Each enemy In ...
'...
DrawEnemy(form)
Next
form.Invalidate()
form.ResumeLayout()
It's also possible you only need to Invalidate() the picturebox.
I've got a WinForms Panel control which holds a large number of child controls. Each child is left docked, causing the horizontal width of the contents to grow. The containing Panel has its AutoScroll property set so that you can get to all the contents.
I'm running into a problem when the total width of the contents gets too large. Once you've hit this maximum width, additional content elements are placed on top of existing contents instead of being placed to the right. But, if I resize the Panel after it has done its initial layout, it corrects itself by expanding its logical width and placing each content element in the correct location. How do I get it to layout correctly before the user resizes the window?
Here's a simple example:
Form1.vb
Public Class Form1
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
For i As Integer = 1 To 200
Dim gb As New GroupBox
gb.Text = "Box " & i.ToString
gb.Width = 250
gb.Dock = DockStyle.Left
Panel1.Controls.Add(gb)
gb.BringToFront()
Next
End Sub
End Class
Form1.Designer.vb
Partial Class Form1
Inherits System.Windows.Forms.Form
Private Sub InitializeComponent()
Me.Panel1 = New System.Windows.Forms.Panel()
Me.SuspendLayout()
'
'Panel1
'
Me.Panel1.AutoScroll = True
Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill
Me.Panel1.Location = New System.Drawing.Point(0, 0)
Me.Panel1.Name = "Panel1"
Me.Panel1.Size = New System.Drawing.Size(284, 262)
Me.Panel1.TabIndex = 0
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(284, 262)
Me.Controls.Add(Me.Panel1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
Friend WithEvents Panel1 As System.Windows.Forms.Panel
End Class
This is what the window looks like when it first comes up, scrolled nearly to the end so you can see the problem area. Notice that Box 183 to 199 are missing because they are placed on top of each other. This is not right.
This is what the window looks like after you manually resize it, scrolled nearly to the end. The panel fixed itself in response to the resize; the total logical width of the panel was automatically extended enough to hold all the contents. This is what I want it to look like when it first comes up.
I've tried manually setting the location of each box, and I've tried calling PerformLayout() and several other functions. Nothing seems to work. So far I haven't found the magic combination to get the good layout. Does anyone know how to fix this?
Edit:
Here's a screenshot that might make the issue more obvious. I adjusted the box widths and the number of boxes to show the problem better. See how the last box overlaps box 656? Every box from 657 to 700 has the same incorrect location. Turning off docking and setting the location myself doesn't help.
Looks like a bug with the scrolling information. If you call PerformLayout when the Panel is scrolled all the way to the right, it correctly places the controls in the proper place. That requires some code in the OnShown method:
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
Panel1.AutoScroll = True
Panel1.SuspendLayout()
For i As Integer = 1 To 200
Dim gb As New GroupBox
gb.Text = "Box " & i.ToString
gb.Width = 250
gb.Dock = DockStyle.Left
Panel1.Controls.Add(gb)
gb.BringToFront()
Next
Panel1.ResumeLayout(False)
End Sub
Protected Overrides Sub OnShown(e As EventArgs)
MyBase.OnShown(e)
Panel1.AutoScrollPosition = New Point(Panel1.HorizontalScroll.Maximum - _
Panel1.HorizontalScroll.LargeChange, 0)
Panel1.PerformLayout()
Panel1.AutoScrollPosition = Point.Empty
End Sub
Of course, having over 200 container controls on the form is never recommended.
AutoScroll is not AutoPositionMyChildren. From MSDN:
When adding controls programmatically to a form, use the AutoScrollPosition property to position the control either inside or outside of the current viewable scroll area.
If you looped thru the controls, to print their location, you's see at some point (probably around 130) that Location.Y becomes fixed at 32767 probably some default unscrolled max. This is also the point they start stacking because they in fact have the same initial location. Some of the code you have makes up for that but it isnt quite right. Once you scroll it, the panel fixes the coords on the child controls.
First, I would suggest that you set Panel1.AutoScrollMinSize to something like {480, 0} so that the HScroll bar appears at design time; this allows you to calc a good height for the boxes which wont cause a VScroll as you add controls.
Dim gb As GroupBox
' only 150 because problem is when (i * width) > 32k
For i As Integer = 0 To 150
gb = New GroupBox
gb.Name = i.ToString ' added
gb.Text = "Box " & i.ToString
gb.Width = 250
' no docking so set the height
gb.Height = Panel1.Bounds.Height - 30 ' trying to avoid the VSCroll
' set location explicitly
gb.Location = NewCtlLocation(Panel1.Controls.Count,
Panel1.AutoScrollPosition.X)
' Dock and Anchor mess up the AutoScroll
'gb.Dock = DockStyle.Left
Panel1.Controls.Add(gb)
' allow panel to update its scroll positions
Panel1.ScrollControlIntoView(gb)
' not needed; seems to offset something with Dock
' changing ZOrder may not always be desirable
'gb.BringToFront()
' debug illumination
Console.WriteLine("{0} {1} {2}", i.ToString,
Panel1.AutoScrollPosition.X.ToString,
gb.Location.X.ToString)
Next
'Go back to start
Panel1.ScrollControlIntoView(Panel1.Controls("0"))
Location helper so you can tweak gutters or margins (dock replacement):
Friend Function NewCtlLocation(ByVal n As Integer,
ByVal ScrollPosX As Integer) As Point
Const TopMargin As Integer = 5
Const LeftMargin As Integer = 5
Return New Point((n * 250) + ScrollPosX, 0)
End Function
Notes:
I have a vertical scroller which repeatedly adds up to 120 user controls which works well but it does not need/use ScrollControlIntoView and they never stack up like yours do. I suspect maybe because they are smaller. There is also at least a second or two before the next one can be added, which may matter. But, good to know.
It might be possible to use the ControlAdded event of the panel to do something, but it would likely amount to ScrollControlIntoView. Doing it once at the end only doesnt work, so using it as they are added is allowing something to get updated as you go.
With the right fiddling, you might be able to get Dock to work, but I suspect it may be part of the problem such as Height and Left set this way dont update the panel's internal scroll map.
Your boxes actually look narrower than 250 - is autosize on?
Suspend/Resume Layout hurt rather than help - they prevent the control from doing anything about the virtual area being populated. It should happen fast enough that no one will see anything. Results:
Works on My SystemTM
I am looking for a way to design things differently in my project. Instead of using TabControls I wish to use Buttons (Instead of pressing the tabs on the top I would like to press the Buttons on the left-side). These buttons when pressed they have their own Panel where each has their own respective content.
Select Case tabAdmin.SelectedIndex
Case 0
If txtCode_Patient.Text = "" Then
txtCode_Patient.Focus()
Else
cmdAdminister.Focus()
End If
Case 1
If txtD_Patient.Text = "" Then
txtD_Patient.Focus()
Else
cmdRefresh.Focus()
End If
Case 2
If txtI_Patient.Text = "" Then
txtI_Patient.Focus()
Else
cmdI_CUser.Focus()
End If
Case 3
If txtStat_CS.Text = "" Then
txtStat_CS.Focus()
Else
cmdStat_Refresh.Focus()
End If
End Select
The code above is similar to what my project acts and it works with TabControls. I want to do a similar thing but this time, like I said before, pressing Buttons on the left-side. How can I do a similar thing ?
UPDATE:
I found a way for this one but now my concern is how do I make it look like one of its default button 3D look-alike?
Public Class Tab
Inherits TabControl
Private Property DoubledBuffered As Boolean
Sub New()
SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint, True)
DoubledBuffered = True
SizeMode = TabSizeMode.Fixed
ItemSize = New Size(30, 110)
End Sub
Protected Overrides Sub CreateHandle()
MyBase.CreateHandle()
Alignment = TabAlignment.Left
End Sub
Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
Dim B As New Bitmap(Width, Height)
Dim G As Graphics = Graphics.FromImage(B)
G.Clear(Color.AliceBlue)
For i = 0 To TabCount - 1
Dim TabRectangle As Rectangle = GetTabRect(i)
If i = SelectedIndex Then
'//Selected
G.FillRectangle(Brushes.DarkSlateGray, TabRectangle)
Else
'//Not Selected
G.FillRectangle(Brushes.AntiqueWhite, TabRectangle)
End If
G.DrawString(TabPages(i).Text, Font, Brushes.Black, TabRectangle, New StringFormat With {.Alignment = StringAlignment.Center, .LineAlignment = StringAlignment.Center})
Next
e.Graphics.DrawImage(B.Clone, 0, 0)
G.Dispose() : B.Dispose()
MyBase.OnPaint(e)
End Sub
End Class
Use the CheckBox control instead of Button, but set Appearance = Button, that way it looks exactly like a button can remains in the "pressed" state when clicked.
To shift between content, put each of your sub-forms into their own UserControl instances, then host them within a Panel control, then switch the .Visibility property of each sub-form according to which CheckBox was clicked.
There is an Outlook-style side bar available on Code Project. It has a VB version as well as C# and although it's knocking on a bit now, you could always adapt this to look a bit nicer. I have used it in the past and it worked pretty well as I recall.
You can set the selected tab via tabAdmin.SelectedIndex = 0 (or 1, 2, etc, but remember it is 0 based)
You may also set the tab by the tab's name via tabAdmin.SelectedTab = TabName
Use a common click event handler for the buttons. Store the relevant tabindex in the Tag property of the buttons. Then tabAdmin.SelectedIndex equals the tag of the clicked button cast as integer.
I imagine this is probably an easy to answer question but for some reason I can't get it to work
Sub New(ByVal Sess As AudioSessionControl2)
S_Session = Sess
'Create the panel and position it.
S_Panel.BackColor = Color.AliceBlue
S_Panel.Width = 200
S_Panel.Height = 40
Dim Position As New Point(6, 19)
If G_AppSessions.Count > 0 Then
Position = Point.Add(G_AppSessions.Item(G_AppSessions.Count - 1).SessionPanel.Location, New Point(0, 45))
End If
S_Panel.Location = Position
'Create a label which has the name of the process
Dim S_PName As New Label
S_PName.Text = "Test"
S_PName.Dock = DockStyle.Left
S_Panel.Controls.Add(S_PName)
'Create a button to change volume
Dim S_Save As New Button()
S_Save.Text = "Save"
AddHandler S_Save.Click, AddressOf Save_Click
S_Save.Parent = S_Panel
S_Panel.Controls.Add(S_Save)
S_Volume.Parent = S_Panel
S_PName.Parent = S_Panel
MainForm.Controls.Add(S_Panel)
S_Panel.Parent = MainForm.gb_Applications
End Sub
The problem is that, the label will show because its docked, but the button won't. It will only show if its docked as well, and thats just not what I want. This is part of a class for creating a dynamic UI, where I can create a number of this class to create a bunch of panels for various things.
I don't see anywhere where you are setting the label or button position. You probably have them both at 0,0 and the label is on top of the button, obscuring it. Did you try setting the position of both the controls, making sure they don't overlap?