Can't seem to disable button - vb.net

I'm new to Visual Basic and I am stuck on a Tic Tac Toe exercise. Here is my code.
Public Class Form1
Public Enum Buttons As Byte
btn1 = 1
btn2 = 2
btn3 = 3
btn4 = 4
btn5 = 5
btn6 = 6
btn7 = 7
btn8 = 8
btn9 = 9
End Enum
Public Sub Computer()
Dim RandomNumberGenerator As New Random
Dim RandomNumber As Integer
RandomNumber = RandomNumberGenerator.Next(1, 9)
Dim RandomButton = CType(RandomNumber, Buttons)
Do
If RandomButton.Enabled = True Then
RandomButton.Enabled = False
RandomButton.Text = "O"
RandomButton.Font = New Font("Consolas", 50, FontStyle.Bold)
Exit Do
Else
RandomNumber = RandomNumberGenerator.Next(1, 9)
Dim RandomButton = CType(RandomNumber, Buttons)
End If
Loop
The problem that I am having is the if loop. I am trying to see if the Random button selected is enabled or not. But instead Visual Basic tells me that "Enable is not a member of Tic_Tac_Toe.Fourm1.Buttons. I wanted to know if there is any way I can disable or enable a button via an enum. Can someone please help me figure this out?

First problem: Your enum (Buttons) doesn't have a member "Enabled". You are setting properties of a Button on an enum, which won't work.
You have to create an actual UI Button for each "RandomButton" and set the value, ID, and text, then add it to the form. At this point you could get/set those properties, including "Enabled".
If these buttons are all on the Form already and you're just trying to pick a random one, you can use Me.Controls.Find(ID)

Related

Dynamic UserControl Not Showing - flowlayoutpanel

I've array of items and for each item, creating a new instance of custom user control. The problem is that when the control is created and added to Flowlayoutpanel, it's not showing up.
Code
Sub createList()
Dim len_ids As Integer = account_ids.Length - 1
Dim x As Integer = 1
While x <= len_ids
Dim listItem As New wrapperItems()
listItem.txtName.Text = account_names(x)
listItem.picIcon.BackgroundImage = Image.FromFile(account_icons(x))
listItem.lblPath.Text = account_paths(x)
listItem.Tag = "listItem" & x
listItem.Top = 10 * x
listItem.Left = 10
Me.flowWrapper1.Controls.Add(listItem)
listItem.Show() ' tried this
listItem.Visible = True 'tried this
listItem.BringToFront() ' tried this
x += 1
End While
Me.Refresh()
End Sub
Anyone has idea of why it's not working?
It seems that when the background worker is started on Form.Show instead of Form.Load, the code doesn't work.
For the fix, I added background worker to Form.Load().
The better way was finally to add a refresh list button and then load the background worker.

Accessing buttons names using variables

In visual basic I want to be able to access a button's name using the number stored in a variable.
For example if I have 24 buttons that are all named 'Button' with the numbers 1, 2, 3... 22, 23, 24 after it. If I want to then change the text in the first eight buttons how would I do that.
Here's my example to help show what I mean:
For i = 1 to 8
Button(i).text = "Hello"
Next
The proposed solutions so far will fail if the Buttons are not directly contained by the Form itself. What if they are in a different container? You could simple change "Me" to "Panel1", for instance, but that doesn't help if the Buttons are spread across multiple containers.
To make it work, regardless of the Buttons locations, use the Controls.Find() method with the "searchAllChildren" option:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim ctlName As String
Dim matches() As Control
For i As Integer = 1 To 8
ctlName = "Button" & i
matches = Me.Controls.Find(ctlName, True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is Button Then
Dim btn As Button = DirectCast(matches(0), Button)
btn.Text = "Hello #" & i
End If
Next
End Sub
For index As Integer = 1 To 8
CType(Me.Controls("Button" & index.ToString().Trim()),Button).Text = "Hello"
Next
Use LINQ and you're good to go:
Dim yourButtonArray = yourForm.Controls.OfType(of Button).ToArray
' takes all controls whose Type is Button
For each button in yourButtonArray.Take(8)
button.Text = "Hello"
Next
Or
Dim yourButtonArray = yourForm.Controls.Cast(of Control).Where(
Function(b) b.Name.StartsWith("Button")
).ToArray
' takes all controls whose name starts with "Button" regardless of its type
For each button in yourButtonArray.Take(8)
button.Text = "Hello"
Next
In any case, .Take(8) will iterate on the first 8 items stored inside yourButtonArray

No Title Bar Issue and Windows 10

I am developing an application with a custom title bar. The form design requires it to be resizable. Keeping in mind that it has a custom title bar, using the normal route to remove the title bar in the custom form is not working. It appears that the Windows 10 API is forcing a small white "chunk" to remain at the top of the screen above the title bar.
My question to you is this: Has anyone encountered this issue, and do you know a fix or a work-around so that we can get the forms to look correct in Windows 10?
Here is my current code:
Dim testform As New Form
testform.Width = 350
testform.Height = 100
testform.FormBorderStyle = FormBorderStyle.Sizable
testform.ControlBox = False
testform.Text = String.Empty
testform.Show()
We tested for the API issue due to a suggestion by another support forum that has since seemed to be exhausted as far as help. I compiled our existing code and ran the executable on a Windows 7 machine. On the Windows 7 machine the form opens properly with 0 space between the top of the ClientRectangle and the Form.
Here's an example of how to do (something) like what you want. My VB is really rusty, so make sure you test and debug this carefully.
First Add a Form to your project. For this example I've left the name Form1 but you probably want to change that to something meaningful. On that form I set the BorderStyle property to None. (That's the only change I made)
The code for Form1 is as follows:
Public Class Form1
Public Const WM_NCHITTEST As Integer = &H84
Public Const BorderSize As Integer = 10
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_NCHITTEST Then
Dim x As Integer = m.LParam.ToInt32 And &HFFFF
Dim y As Integer = (m.LParam.ToInt32 >> 16) And &HFFFF
Dim p As Point = New Point(x, y)
If Me.Bounds.Contains(p) Then
Dim top As Boolean = Math.Abs(Me.Bounds.Y - y) < BorderSize
Dim bottom As Boolean = Math.Abs(Me.Bounds.Bottom - y) < BorderSize
Dim left As Boolean = Math.Abs(Me.Bounds.X - x) < BorderSize
Dim right As Boolean = Math.Abs(Me.Bounds.Right - x) < BorderSize
If top And left Then
m.Result = NCHITTEST.HTTOPLEFT
ElseIf top And right Then
m.Result = NCHITTEST.HTTOPRIGHT
ElseIf bottom And left Then
m.Result = NCHITTEST.HTBOTTOMLEFT
ElseIf bottom And right Then
m.Result = NCHITTEST.HTBOTTOMRIGHT
ElseIf top Then
m.Result = NCHITTEST.HTTOP
ElseIf bottom Then
m.Result = NCHITTEST.HTBOTTOM
ElseIf left Then
m.Result = NCHITTEST.HTLEFT
ElseIf right Then
m.Result = NCHITTEST.HTRIGHT
Else
m.Result = NCHITTEST.HTCAPTION
End If
Exit Sub
End If
End If
MyBase.WndProc(m)
End Sub
End Class
Additionally, somewhere in that makes sense in your project include the following enum. (I've stripped out the values I'm not using, but they are all available in the documentation) For the purposes of this demonstration I placed it directly beneath the End Class for Form1.
Public Enum NCHITTEST
HTBOTTOM = 15
HTBOTTOMLEFT = 16
HTBOTTOMRIGHT = 17
HTCAPTION = 2
HTLEFT = 10
HTRIGHT = 11
HTTOP = 12
HTTOPLEFT = 13
HTTOPRIGHT = 14
End Enum
The way it's implemented, there is an invisible 10px border where the mouse will change to the resize cursor. This is controlled by the BorderSize constant. You can move the form by clicking and dragging anywhere in the background. (That's what HTCAPTION does.)
TheB had the best answer, and I had to amend it slightly to work with my coding. Thank you for all of the help, sir!
So, when altering this code to work with a dynamically-created control a few changes needed to be made. Here is how I got it to work:
Create a new class specifically named: SubclassHWND.vb
(As gathered from Microsoft)
Public Class SubclassHWND
Inherits NativeWindow
Public Const WM_NCHITTEST = &H84
Public Const BorderSize As Integer = 10
Public frm As Form = Nothing
Public Sub setFrm(ByVal sender As Form)
frm = sender
End Sub
Protected Overloads Overrides Sub WndProc(ByRef m As Message)
if m.Msg = WM_NCHITTEST Then
Dim x As Integer = m.LParam.ToInt32 And &HFFFF
Dim y As Integer = (m.LParam.ToInt32 >> 16) And &HFFFF
Dim p As Point = New Point(x, y)
If frm.Bounds.Contains(p) Then
Dim top As Boolean = Math.Abs(frm.Bounds.Y - y) < BorderSize
Dim bottom As Boolean = Math.Abs(frm.Bounds.Bottom - y) < BorderSize
Dim left As Boolean = Math.Abs(frm.Bounds.X - x) < BorderSize
Dim right As Boolean = Math.Abs(frm.Bounds.Right - x) < BorderSize
If top And left Then
m.Result = NCHITTEST.HTTOPLEFT
ElseIf top And right Then
m.Result = NCHITTEST.HTTOPRIGHT
ElseIf bottom And left Then
m.Result = NCHITTEST.HTBOTTOMLEFT
ElseIf bottom And right Then
m.Result = NCHITTEST.HTBOTTOMRIGHT
ElseIf top Then
m.Result = NCHITTEST.HTTOP
ElseIf bottom Then
m.Result = NCHITTEST.HTBOTTOM
ElseIf left Then
m.Result = NCHITTEST.HTLEFT
ElseIf right Then
m.Result = NCHITTEST.HTRIGHT
Else
m.Result = NCHITTEST.HTCAPTION
End If
Exit Sub
End If
End If
Debug.WriteLine(m.ToString())
MyBase.WndProc(m)
End Sub
End Class
Public Enum NCHITTEST
HTBOTTOM = 15
HTBOTTOMLEFT = 16
HTBOTTOMRIGHT = 17
HTCAPTION = 2
HTLEFT = 10
HTRIGHT = 11
HTTOP = 12
HTTOPLEFT = 13
HTTOPRIGHT = 14
End Enum
This is basically the same exact code that #TheB supplied me with for the fix earlier with a couple of changes. We went ahead and created a new Public frm variable. We set this in the next set of code so we can reference the dynamic form that the WndProc evaluation will Override.
In the form generation code we added these lines to create the re-sizable window that has no title bar API in Windows 10:
newForm.FormBorderStyle = FormBorderStyle.FixedToolWindow
newForm.ControlBox = False
newForm.Text = String.Empty
Dim s As SubclassHWND = New SubclassHWND()
s.setFrm(newForm)
s.AssignHandle(newForm.Handle)
Now every dynamically-created form in the project uses the Overrides that we specified in SubclassHWND.vb! Thanks go out to everyone and all of their valuable input.

Building a collection of buttons and sorting by text

I have a collection of buttons on a panel, I want to be able to build a collection of those buttons and then sort the buttons by the text on the button. However, I am stuck. This is what I have so far, but I can't figure out how to sort.
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Dim btn1 As Control
Dim btnArray(0 To 3) As Button
btnArray(0) = btnAll
btnArray(1) = btnWine
btnArray(2) = btnBeer
btnArray(3) = btnNonAlcoholic
For Each btn1 In btnArray
Next
End Sub
You can use a simple manual bubble sort:
Dim Changed as Boolean = False
Do
Changed = False
For i = 0 to btnArray.Count - 2
If btnArray(i).Text > btnArray(i+1).Text Then '< > work for strings as well and they check 'position' in the alphabet. So "A" < "B" and so on
'If order is wrong, switch the two buttons
Dim temp as Button = btnArray(i+1)
btnArray(i + 1) = btnArray(i)
btnArray(i) = temp
Changed = True
End If
Next
'Do this until no more switches are necessary
Loop Until Changed = False
This will order your buttons and is reasonably fast for low numbers of buttons. You could use a list and a custom IComparer object as well and simply call List.Sort with the custom comparer.
See here for example implementations of this approach for a similar problem: http://msdn.microsoft.com/en-us/library/cfttsh47%28v=vs.110%29.aspx
Using LINQ:
btnArray = btnArray.OrderBy(Function(btn) btn.Text).ToArray

How to get value from dynamic radio button

I'm creating the 3 radio buttons in a private sub like such:
For counter As Integer = 0 To rc - 1
'controller name Radio button and properties.
Dim dynRadio As New RadioButton()
Me.Controls.Add(dynRadio)
With dynRadio
.Name = CStr(ds.Tables("MakeThisNameMeaningful").Rows(counter).Item(0))
.Location = New Point(xAxis, yAxis)
.TabStop = False
.Text = CStr(ds.Tables("MakeThisNameMeaningful").Rows(counter).Item(0))
.Width = 80
End With
yAxis = yAxis + 40
Next
The radios get drawn ok. So I have 3 radios with the text (result of select from db) controller1, controller2, controller3
I've triead all sorts and couldnt' find anything on Google. Oh, I should mention that I'm trying to get the radio value from another private sub. I want to do along the lines of:
If controller1.Selected = true then
'do stuff
End if
I know the above is wrong but not sure how to determine which radio is selected :(
Cheers,
J
You can iterate through your radio buttons in your 2nd private sub.
Dim radios = Controls.OfType(Of RadioButton).AsQueryable()
For Each r As RadioButton In radios
If r.Checked Then
'this radio is checked. do something.
End If
Next