Resize form on listview height - vb.net

I have form with only docked.full listview on it. Listview show computer drives so it's content is changeable on runtime. On listview column headers are allways visible.
I would like my form to resize with listview regarding of filled items.
This is my code:
Dim rc As Rectangle = lvDriveInfo.Items(0).GetBounds(ItemBoundsPortion.Entire)
Me.Height = (rc.Height * lvDriveInfo.Items.Count) +
SystemInformation.CaptionHeight +
SystemInformation.BorderSize.Height
But something misses here or is incorrect.
How to get exact height of listview with headers regarding on items.count and properly set height of form with this value?

ListView metrics are rather convoluted, what you are trying to do is most definitely not easy. The one thing you overlooked is the space required by the column headers. That however is not enough, ListView also requires a bit of extra elbow room at the bottom to avoid displaying the scrollbar.
The most straight-forward code that works on my machine is:
Private Sub ResizeView()
If ListView1.Items.Count = 0 Then Exit Sub
Dim last = ListView1.Items(ListView1.Items.Count - 1)
Me.ClientSize = New Size(Me.ClientSize.Width, _
ListView1.Top + last.Bounds.Bottom + 4)
End Sub
The +4 is the rub, I can't give you a warranty that this will work on every Windows version at every video DPI. It is independent of the item height so that's encouraging. But testing required to be sure. If you can guarantee that there will never be too many items in the list then you can avoid the problem by setting the list view's Scrollable property to False.

Try the following code
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
lvDriveInfo.BorderStyle = BorderStyle.None
lvDriveInfo.Dock = DockStyle.Fill
With lvDriveInfo
.View = View.Details
.GridLines = True
.Columns.Add("Drive")
End With
SetFormHeight()
End Sub
Private Sub SetFormHeight()
lvDriveInfo.Items.Clear()
For Each Drive In IO.DriveInfo.GetDrives
lvDriveInfo.Items.Add(Drive.Name)
Next
Dim ListViewHeaderHeight As Integer = lvDriveInfo.Items(0).Bounds.Top
Dim ListViewRowHeight As Integer = lvDriveInfo.Items(0).Bounds.Height
Dim ListViewRowsCount As Integer = lvDriveInfo.Items.Count
Dim NewHeight As Integer = ListViewHeaderHeight + (ListViewRowHeight * ListViewRowsCount)
Me.ClientSize = New Size(Me.ClientSize.Width, NewHeight)
End Sub

Related

VB: How can I make buttons over a picture box transparent?

in my program I have a picture box with a specific width x and height y. Then the user can enter an elementsize (e.g. 10) and then the programm will create buttons with the width and height of the elementsize over the picture box. The thing is that these buttons need to be transparent so only their border is visible. That means that through the buttons you can see the content of the picture box.
So this is my code for creating these buttons when I click on my start button:
Imports System.Math
Public Class Form1
Dim x As Double
Dim y As Double
Dim elsize As Integer
Dim numberofbuttons As Integer
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
x = 400
y = 200
elsize = 20
numberofbuttons = Round(x / elsize) * Round(y / elsize)
Dim i As Integer
Dim j As Integer
For j = 1 To Round(y / elsize)
For i = 1 To Round(x / elsize)
Dim Btn As New Button
Btn.Width = elsize
Btn.Height = elsize
Btn.Location = New Point(elsize * (i - 1), elsize * (j - 1))
Btn.BackColor = Color.Transparent
PictureBox1.Controls.Add(Btn)
Next
Next
End Sub
End Class
I use
Btn.BringToFront()
to put these buttons in front of the picture box and I wanted to use
Btn.BackColor = Drawing.Color.Transparent
to make them transparent but this won't work. Has anybody any ideas how to do this? Also I wanted to put these buttons in the coordinate system of the picture box and not of the form1. I thought that this is possible through
Btn.Parent = PictureBox1
but the buttons always use the coordinate system of the form.
Thanks in advance for your help.
This:
Me.Controls.Add(Btn)
is setting the Parent of the Button to be the form. You don't Add to one parent and assign a different parent explicitly. Do one or the other. In short, get rid of that Add call and you should be good to go. Alternatively, get rid of the Parent assignmnent and Add to the Controls collection of the PictureBox instead. I'd suggest the latter.
Either way, DO NOT display the Button until you have configured it, i.e. set the Parent or call Controls.Add last.
EDIT: I tested this code and it worked as expected:
Dim btn As New Button
With btn
.Size = mySize
.Location = myLocation
.BackColor = Color.Transparent
.FlatStyle = FlatStyle.Flat
PictureBox1.Controls.Add(btn)
End With

programmatically select and highlight a row of a ListView in VB.NET

I want to do something seemingly simple - programmatically select and highlight a row of a ListView in VB.NET.
VB.NET: How to dynamically select a list view item?
tells me what should to be all that is needed, but it isn't. The row is selected, but not highlighted.
http://vbcity.com/forums/t/28260.aspx
tells me about the "HideSelection" property and the .Focus() method (also referenced at Select programmatically a row of a Listview), which sounded hopeful, but the best I can get is the faint highlight mentioned, I want the full monty. I tried a Noddy example with just a ListView, in Details mode, FullRowSelection = true, HideSelection = False, one columnheader defined and then
ListView1.Items.Add("Red")
ListView1.Items.Add("Orange")
ListView1.Items.Add("Yellow")
ListView1.Items.Add("Green")
ListView1.Items(2).Selected = True
I get this
but I want this
I can simulate highlighting by adding these lines
ListView1.SelectedItems(0).BackColor = Color.CornflowerBlue
ListView1.SelectedItems(0).ForeColor = Color.White
but then how can I be sure to undo the artificial highlight if the row can be implicitly as well as explicitly deselected? Do I have to think of all the possible cases? That's too much work for what should be a simple operation. Plus, since I want to color-code my rows, there is an additional challenge that when I undo the highlight color, I have to figure out what color is appropriate at that point. Is there a better, simpler way?
You can access the Graphics object used to draw each item, and draw them yourself.
Make a new project with a Button and ListView. Paste the following code:
Form_Load to use multiple subitems
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.ListView1.OwnerDraw = True ' or else can't handle DrawItem event
ListView1.Columns.Add("ColumnHeader1")
ListView1.Columns.Add("ColumnHeader2")
ListView1.Columns.Add("ColumnHeader3")
Me.ListView1.Items.Add("Red")
Me.ListView1.Items.Add("Orange")
Me.ListView1.Items.Add("Yellow")
Me.ListView1.Items.Add("Green")
ListView1.Items(0).SubItems.Add("Strawberry")
ListView1.Items(0).SubItems.Add("Apple")
ListView1.Items(1).SubItems.Add("Pepper")
ListView1.Items(1).SubItems.Add("Apricot")
ListView1.Items(2).SubItems.Add("Plum")
ListView1.Items(2).SubItems.Add("Banana")
ListView1.Items(3).SubItems.Add("Apple")
ListView1.Items(3).SubItems.Add("Lime")
End Sub
Three handlers for the ListView's drawing related events. Code copied from this answer
Private Sub listView1_DrawColumnHeader(sender As Object, e As DrawListViewColumnHeaderEventArgs) Handles ListView1.DrawColumnHeader
e.DrawDefault = True
End Sub
Private Sub listView1_DrawSubItem(sender As Object, e As DrawListViewSubItemEventArgs) Handles ListView1.DrawSubItem
Const TEXT_OFFSET As Integer = 1
' I don't know why the text is located at 1px to the right. Maybe it's only for me.
Dim listView As ListView = DirectCast(sender, ListView)
' Check if e.Item is selected and the ListView has a focus.
If Not listView.Focused AndAlso e.Item.Selected Then
Dim rowBounds As Rectangle = e.SubItem.Bounds
Dim labelBounds As Rectangle = e.Item.GetBounds(ItemBoundsPortion.Label)
Dim leftMargin As Integer = labelBounds.Left - TEXT_OFFSET
Dim bounds As New Rectangle(rowBounds.Left + leftMargin, rowBounds.Top, If(e.ColumnIndex = 0, labelBounds.Width, (rowBounds.Width - leftMargin - TEXT_OFFSET)), rowBounds.Height)
Dim align As TextFormatFlags
Select Case listView.Columns(e.ColumnIndex).TextAlign
Case HorizontalAlignment.Right
align = TextFormatFlags.Right
Exit Select
Case HorizontalAlignment.Center
align = TextFormatFlags.HorizontalCenter
Exit Select
Case Else
align = TextFormatFlags.Left
Exit Select
End Select
TextRenderer.DrawText(e.Graphics, e.SubItem.Text, listView.Font, bounds, SystemColors.HighlightText, align Or TextFormatFlags.SingleLine Or TextFormatFlags.GlyphOverhangPadding Or TextFormatFlags.VerticalCenter Or TextFormatFlags.WordEllipsis)
Else
e.DrawDefault = True
End If
End Sub
Private Sub listView1_DrawItem(sender As Object, e As DrawListViewItemEventArgs) Handles ListView1.DrawItem
Dim listView As ListView = DirectCast(sender, ListView)
' Check if e.Item is selected and the ListView has a focus.
If Not listView.Focused AndAlso e.Item.Selected Then
Dim rowBounds As Rectangle = e.Bounds
Dim leftMargin As Integer = e.Item.GetBounds(ItemBoundsPortion.Label).Left
Dim bounds As New Rectangle(leftMargin, rowBounds.Top, rowBounds.Width - leftMargin, rowBounds.Height)
e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds)
Else
e.DrawDefault = True
End If
End Sub
Button click handler to simulate item(2) selected
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.ListView1.Items(2).Selected = True
End Sub
This will draw the background color regardless of focus. You have a lot of control over other colors and fonts going this route too.
Here, the button has been clicked, to select item 2, while the button still has focus, and item 2 is selected.
Easiest thing,
Just allocate the LST_ItemIndex = lstList.FocusedItem.Index everytime you select a different item
and then fire the below whenever you want the highlight
If lstList.Items.Count > 0 Then
lstList.Items(LST_ItemIndex).Selected = True
lstList.Items(LST_ItemIndex).EnsureVisible()
End If

Working with too many PictureBoxes, any way to use a For?

Warning : Im completely new to VB.net and only know the most basic form of programming, maybe even less
Visual Basic 2010 Express
I have declared an Array Equipa(x,y) as integer.
I have 30 PictureBoxes ( PictureBox1 to PictureBox30)
In my mind, I assigned each PictureBox to a X and Y in the Array.
At load form, I want to change the image of PictureBox5 to PictureBox25.
For this I copy pasted 20 times PictureBox(x).Image = My.Resources.GreyHexagon
I would like to have a Loop that can do this without me copy pasting so much.
During the program, clicking on PictureBox5, for example, changes it to a different image, depending on the value, of the Array declared.
If Equipa(0, 0) = 0 Then
PictureBox4.Image = My.Resources.GreyHexagon
ElseIf Equipa(0, 0) = 1 Then
PictureBox4.Image = My.Resources.BlueHexagon
Else
PictureBox4.Image = My.Resources.RedHexagon
End If
The problem, again, is I have to repeat this code, for every Array position, since each PictureBox is assigned to each position.
So what I need is a Loop that can go through each PictureBox, I'm not asking you to make that code, I just don't know how to go through each PictureBox, for example PictureBox5 to PictureBox25.
PS . The following code changes EVERY PictureBox to the Image I wan't. But I do not understand any of this code, therefor can't change it to only go through PictureBox5 to PictureBox25.
Me.SuspendLayout()
For Each box As PictureBox In Me.Controls.OfType(Of PictureBox)()
box.Image = My.Resources.RedHexagon2
Next box
Me.ResumeLayout()
What I tried once before: In the declarations,
Dim pic(29) As PictureBox
And then in Public Sub New, I wrote picarray() and then made a Private Sub picarray. In this sub I dimmed each picture to the array
pic(0) = picturebox1
pic(1) = picturebox2
pic(2) = picturebox3
...
pic(29) = picturebox30
In my case, I wanted to move all 30 at once, and I had a timer, so I did:
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _Timer1.Tick
For i = 0 To 29
pic(i).Left = pic(i).Left + 3
Next
End Sub
To move them all to the right at once. I used the same way to move them back if the last one on the left hits the form's edge.
Me.SuspendLayout()
For Each box As PictureBox In Me.Controls.OfType(Of PictureBox)()
box.Image = SomeNewPicture
Next box
Me.ResumeLayout()
If you want to be able to look at specific images, you can still just say things like PictureBox4.Image = SomeNewImage. But if you really want to access them by index, first define this variable as a member of the form class:
Private PictureBoxes() As PictureBox
And then do this in your form's Load event:
PictureBoxes = Me.Controls.OfType(Of PictureBox)().ToArray()
Now you can reference each PictureBox by index:
PictureBoxes(4).Image = SomeNewPicture
PictureBoxes(10).Image = SomeOtherNewPicture
You could also do something like this:
Me.SuspendLayout()
For i As Integer = 5 to 25
PicturesBoxes(i) = My.Resources.RedHexagon2
Next i
Me.ResumeLayout()
Finally, to address the If/Then code in your edited question, I'd do something like this:
'Note this Array shorthand requires a more recent version of Visual Studio
' I used it for brevity in the response. You should be able to convert it on your own
Dim Images() As Image = {My.Resources.GreyHexagon, My.Resources.BlueHexagon, My.Resources.RedHexagon}
Dim ImageIndex As Integer = Equipa(0, 0)
If ImageIndex < 0 OrElse ImageIndex > 1 Then ImageIndex = 2
PictureBox4.Image = Images(ImageIndex)

When creating a label a label inside a panel, the text is cut off VisualBasic

I wanted to be able to dynamically create a panel with a label on it, but the label isn't acting as I would expect it too, its cutting most of it off.
When I create a panel then create a label inside the panel, the text isn't displayed correctly. Anyone know how to fix it?
What is was supposed to do was to create a panel with text on it with the newpanel() sub
Dim timetable(5, 5) As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Width = (74 * 5) - 3
Me.Height = My.Computer.Screen.Bounds.Size.Height
Me.Top = My.Computer.Screen.Bounds.Top
Me.Left = My.Computer.Screen.Bounds.Right - Me.Width
GetTimetable()
End Sub
Private Sub newpanel(colour As Color, textT As String)
Dim Npan As New Panel
Npan.Top = 0
Npan.Left = 0
Npan.Width = Me.Width
Npan.Height = 64
Npan.BackColor = colour
Dim NpanT As New Label
NpanT.Parent = Npan
NpanT.Text = textT
Npan.Controls.Add(NpanT)
Me.Controls.Add(Npan)
End Sub
Private Sub GetTimetable()
'Dim path As String = My.Computer.FileSystem.SpecialDirectories.Desktop + "\Timetable"
newpanel(Color.Aqua, "this is a test! test testtesttest test test test")
End Sub
Looking at the MSDN page for the Label.AutoSize Property, it states as was mentioned above that the Labels AutoSize Property defaults to true in the designer, but it is false when created in code.
From above link:
When added to a form using the designer, the default value is true. When instantiated from code, the default value is false.
So you need to change your newpanel method to this:
Private Sub newpanel(colour As Color, textT As String)
Dim Npan As New Panel
Npan.Top = 0
Npan.Left = 0
Npan.Width = Me.Width
Npan.Height = 64
Npan.BackColor = colour
Dim NpanT As New Label
NpanT.Parent = Npan
NpanT.Text = textT
NpanT.AutoSize = True 'Enables Auto sizing
Npan.Controls.Add(NpanT)
Me.Controls.Add(Npan)
End Sub

Simple VB, handling many buttons

In my VB solution I have a lot of buttons arranged in a grid. When the program is loaded one of the buttons is randomly set to be the right one (You have to click that one to win). Can anybody point my in the right direction? Because there must be a better way than manually coding every single button, and creating an event handler for every single button.
You don't have to give me a working example, just an overall idea of how this is done.
Thanks.
If you want to arrange the buttons in a grid, either use the TableLayoutPanel or add the buttons directly to the form and calculate their positions. The TableLayoutPanel is useful if you want to arrange the buttons automatically when the form resizes, otherwise adding the buttons directly seems easier to me.
Add the buttons to an array defined at form level to make them easily accessible
Public Const NColumns As Integer = 5, NRows As Integer = 4
Private buttons As Button(,) = New Button(NColumns - 1, NRows - 1) {}
You can add the buttons easily in loops
For ix As Integer = 0 To NColumns - 1
For iy As Integer = 0 To NRows - 1
Dim btn = New Button()
btn.Text = String.Format("{0:d2}{1:d2}", ix, iy)
btn.Location = New Point(leftMargin + ix * xDistance,
topMargin + iy * yDistance)
btn.Size = New Size(buttonWidth, buttonHeight)
AddHandler btn.Click, Addressof Button_Clicked
buttons(ix, iy) = btn
Controls.Add(btn)
Next
Next
You can determine the winning button with a random generator. Define it as form member, not as local variable.
Private randomGenrator As System.Random = New System.Random()
Determine the coordinates
Dim xWins = randomGenrator.Next(NColumns) 'Returns a number between 0 and NColumns-1
Dim yWins = randomGenrator.Next(NRows)
The click handler looks like this
Private Sub Button Button_Clicked(sender As Object, e As EventArgs)
If sender = buttons(xWins, yWins) Then
'You win
Else
'You loose
End
End Sub
First, you said you want a grid of buttons, so you have to have a FlowLayoutPanel control in your form in order to let the buttons you want to add, to be arranged automatically.
Second, you have to make use of a for loop, r any kind of look, in order to add the buttons to be added to previously added 'FlowLayoutPanel'.
class Answers
Dim strAnswerText as string
Dim AnswerFlag as Boolean
End Class
Sub LoadForm(byval a_Answers as Answer())
Dim i as Integer = 0
Dim b as Button
For(i=0;i<NUM_OF_BUTTONS;i++)
b = New Button()
b.Text = "Choice -" & i & "- " & a_Answers(i).strAnswerText
b.Tag = a_Answers(i).AnswerFlag
'Supposing that the FlowLayoutPanel control name is fl
AddHandler b.Click, Addressof Clicked
fl.controls.Add(b)
End For
End Sub
Sub Button Clicked(sender as object, e as EventArgs)
if sender.Tag = True
'True answer
else
'Wrong answer
end if
End Sub