Duplicating a TextBox in VB.Net - vb.net

I was wondering how I would duplicate a TextBox, not just the text inside of it.
TextBox.copy copies the text selected inside the TextBox, which is not quite what I need.
I am using vb.net.

Please try this function :)
Private Function CopyControl(ByVal obj As Object, Optional ByVal locationX As Integer = 0, Optional ByVal locationY As Integer = 0) As Object
Dim objnew As Object = Activator.CreateInstance(obj.GetType()) 'Create new control
Dim oldprops As PropertyDescriptorCollection = TypeDescriptor.GetProperties(obj) 'Control properties
Dim newprops As PropertyDescriptorCollection = TypeDescriptor.GetProperties(objnew) 'New control properties
For i As Integer = 0 To oldprops.Count - 1 '
newprops(i).SetValue(objnew, oldprops(i).GetValue(obj)) 'New control properties = Old control properties
Next
objnew.location = New Point(locationX, locationY) 'Set location
Return objnew 'New control is copied
'Im sorry my english is bad, I hope you understand..
End Function
Me.Controls.Add(CopyControl(TextBox1))
I'm sorry for my bad english. I'm turkish :D

Related

UI Automation - Retrieving data from a SysListView32 (or any Listview)

I'm trying to retrieve data from a SysListView32 object with the code below but it's returning an empty string.
The elements that I need to retrieve are those highlighted in red, as well as the others contained in the other ControlType.ListItem elements, according to the Inspector's print.
Can some one check what's wrong with my code?
Msgbox("Position the mouse cursor on the screen and press ENTER.")
Dim pt As POINTAPI
GetCursorPos(pt)
Dim hwnd As IntPtr = WindowFromPoint(pt)
Dim hwnd As IntPtr = 67202
Dim el As AutomationElement = AutomationElement.FromHandle(hwnd)
Dim walker As TreeWalker = TreeWalker.ContentViewWalker
Dim i As Integer = 0
Dim child As AutomationElement = walker.GetFirstChild(el)
While child IsNot Nothing
'
Dim k As Integer = 0
Dim child2 As AutomationElement = walker.GetFirstChild(child)
While child2 IsNot Nothing
Console.WriteLine(child2.Current.ToString)
child2 = walker.GetNextSibling(child2)
End While
child = walker.GetNextSibling(child)
End While
The SysListView32 may not provide the information requested if its current view state is not LV_VIEW_DETAILS, so we should temporarily (if the current view state is different), use the MultipleViewPattern of its AutomationElement to verify the view state and change it if necessary using the MultipleViewPattern.SetCurrentView() method.
The SetCurrentView() method uses the same values of the Win32 Control.
Then use the AutomationElement FindAll() method of the to find all child elements of type ControlType.DataItem or ControlType.ListItem (using an OrCondition).
For each of them, get all child items of type ControlType.Edit and ControlType.Text (using another OrCondition).
The position of each Item in the list is retrieved using the Item's GridItemPattern, to access the Item's Row property.
Finally, we restore the previous View State if we had to change it.
The code in the example fills a Dictionary(Of Integer, ListViewItem) (named sysListViewItems here), containing all the Items extracted from the SysListView32.
The Integer Key represents the position of an Item in the original ListView
The ListViewItem is a .Net object generated from the array of values (as strings) extracted from each item.
If you don't need ListViewItem objects, you can just store the List(Of String), represented by the itemsText object, instead of creating a ListViewItem here:
sysListViewItems.Add(gridPattern.Current.Row, New ListViewItem(itemsText.ToArray())).
The Handle of a SysListView32 can also be acquired enumerating the children of its Top Level Window by ClassName.
AutomationElement.RootElement provides the current Desktop Element:
Dim parentWindow = AutomationElement.RootElement.FindFirst(
TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "[Window Caption]"))
Dim sysListView32 = parentWindow.FindAll(
TreeScope.Subtree, New PropertyCondition(AutomationElement.ClassNameProperty, "SysListView32"))
If more than one SysListView32 is found, filter by Header content, direct parent ControlType or ClassName or anything else that allows to single it out.
UI Automation requires a reference to the UIAutomationClient and UIAutomationTypes assemblies.
Imports System.Windows.Automation
' Find the ListView Handle as described or the ListView UI Element directly
Dim sysListViewHandle = [GetSysListView32Handle()]
Dim sysListViewElement = AutomationElement.FromHandle(sysListViewHandle)
If sysListViewElement Is Nothing Then Return
Dim sysListViewItems = New Dictionary(Of Integer, ListViewItem)()
Dim mulView As MultipleViewPattern = Nothing
Dim pattern As Object = Nothing
Dim currentView As Integer = -1
If sysListViewElement.TryGetCurrentPattern(MultipleViewPattern.Pattern, pattern) Then
mulView = DirectCast(pattern, MultipleViewPattern)
currentView = mulView.Current.CurrentView
If currentView <> ListViewWState.LV_VIEW_DETAILS Then
mulView.SetCurrentView(ListViewWState.LV_VIEW_DETAILS)
End If
End If
Dim childItemsCondition = New OrCondition(
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem),
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem))
Dim childItems = sysListViewElement.FindAll(TreeScope.Children, childItemsCondition)
If childItems.Count = 0 Then Return
Dim subItemsCondition = New OrCondition(
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit),
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text))
For Each item As AutomationElement In childItems
Dim itemsText = New List(Of String)()
Dim subItems = item.FindAll(TreeScope.Children, subItemsCondition)
For Each subItem As AutomationElement In subItems
itemsText.Add(subItem.Current.Name)
Next
Dim gridPattern = DirectCast(subItems(0).GetCurrentPattern(GridItemPattern.Pattern), GridItemPattern)
sysListViewItems.Add(gridPattern.Current.Row, New ListViewItem(itemsText.ToArray()))
Next
If mulView IsNot Nothing Then
mulView.SetCurrentView(currentView)
End If
Friend Enum ListViewWState
LV_VIEW_ICON = &H0
LV_VIEW_DETAILS = &H1
LV_VIEW_SMALLICON = &H2
LV_VIEW_LIST = &H3
LV_VIEW_TILE = &H4
End Enum

Passing array of sheet controls to modify their properties

I'd like to pass an array of (sheet) controls to another sub to modify their properties:
E.g.
Dim collChk(0 To 2) As ? ' Tried Variant, OLEObject, Control...
collChk(0) = chk0
collChk(1) = chk1
collChk(2) = chk2
Sub:
Sub SetFalse(ByRef controls As ?)
Dim i As Long
For i = LBound(controls) To UBound(controls)
controls(i) = False
Next i
End Sub
But I keep getting: ByRef Argument Type mismatch or other errors
It could also be a Collection, that doesn't matter, but I have not been able to set that up too. I'm a bit lost what works how. Originally, the idea was to pass it as ParamArray, but errors popped out too.
(What would be great is simply to have a function that would work both with Sheet and UserForm controls but I want them specify manually and without duplication.)
For CheckBoxes:
Dim collChk(0 To 2) As MSForms.CheckBox
Set collChk(0) = Chk0
Set collChk(1) = Chk1
Set collChk(2) = Chk2
Note you need to use Set as they are objects. Then:
Sub SetFalse(ByRef controls() As MSForms.CheckBox)
Dim i As Long
For i = LBound(controls) To UBound(controls)
controls(i) = False
Next i
End Sub

Why won't the correct definition in my code work in an "If/Else" statement?

I am making a quiz for my computer science class and the basic concept is that you have 15 keywords and 15 definitions. All need to be randomly displayed and the correct answer has to appear. The user has to match the correct definition to the keyword twice and then that keyword and definition are not displayed again. When all have been answered twice the quiz is over.
I have stored both my keywords and my definitions in the same file so they don't get out of sync. The text file looks like so:
Keyword1,Definition1
Keyword2,Definition2
Keyword3,Definition3
...
Thanks to the help from others here on StackOverflow I have managed to get the randomizing and dictionary completed. For reference that looks like this:
Public Class Form1
Private kv As Dictionary(Of String, Answer) 'Define kv as a dictionary
Private keyword As String
Private correctDefinition As String
Const NUMBER_OF_ANSWERS As Integer = 3 'Define the constant NUMBER_OF_ANSWERS
Private Sub RandomiseAnswers()
Dim r As New Random
Dim kvRandom As List(Of KeyValuePair(Of String, Answer)) =
kv.OrderBy(Function() r.Next).ToList
'questions will appear in random order
For Each line As KeyValuePair(Of String, Answer) In kvRandom
Dim keyword As String = line.Key
Dim correctDefinition As String = line.Value.Answer
Dim keywords As New List(Of String)
keywords.Add(keyword)
keywords.AddRange(kv.Keys.Except({keyword}).
OrderBy(Function() r.Next).Take(NUMBER_OF_ANSWERS - 1))
Dim definitionsRandom As List(Of String) =
keywords.Select(Function(x) kv(x).Answer).OrderBy(Function() r.Next).ToList
LabelKeyword.Text = keyword
RadioButtonDef1.Text = definitionsRandom(0)
RadioButtonDef2.Text = definitionsRandom(1)
RadioButtonDef3.Text = definitionsRandom(2)
Next
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'The text file is structured like so:
'Keyword1,Definition1
'Keyword2,Definition2
'Keyword3,Defintion3
'...
'This makes sure that the keywords and the definitions do not get out of sync when randomising, as a dictionary is a key-value data structure. Call the key, get the right value etc.
kv = New Dictionary(Of String, Answer) 'kv = a new dictionary
For Each line As String In IO.File.ReadAllLines("C:\Users\Matt\Documents\keywords.txt") 'Foreach loop populating the dictionary from the text file
Dim parts() As String = line.Split(",") 'Split the Keywords from the definitions where the , is
kv.Add(parts(0), New Answer With {.Answer = parts(1), .Answered = False}) 'Add the two parts (Keyword and Definition) to the Parts with the Answer class
Next
Dim r As New Random 'Define r as new random
Dim kvRandom As List(Of KeyValuePair(Of String, String)) =
kv.OrderBy(Function() r.Next) _
.Select(Function(x) New KeyValuePair(Of String, String)(x.Key, x.Value.Answer)) _
.ToList() 'A Select method to convert the items in the list properly
'questions will appear in random order
For Each line As KeyValuePair(Of String, String) In kvRandom
Dim keyword As String = line.Key
Dim correctDefinition As String = line.Value 'Checks that the correct definition will be displayed when its keyword is randomly displayed.
'Define keywords as a new list
Dim keywords As New List(Of String)
keywords.Add(keyword) 'Adds the keyword to the list as the Part(0) from the text file in the first loop
keywords.AddRange(kv.Keys.Except({keyword}).
OrderBy(Function() r.Next).Take(NUMBER_OF_ANSWERS - 1))
Dim definitionsRandom As List(Of String) =
keywords.Select(Function(x) kv(x).Answer).OrderBy(Function() r.Next).ToList 'Define definitionsRandom as a list. Checks against correctDefinition to know to always display the correct answer as one of the
'random definitions
LabelKeyword.Text = keyword 'Randomly display a keyword
RadioButtonDef1.Text = definitionsRandom(0) 'Randomly display a definition. Checks against correctDefinition to see make sure one definition is always the right answer.
RadioButtonDef2.Text = definitionsRandom(1) 'Randomly display a definition. Checks against correctDefinition to see make sure one definition is always the right answer.
RadioButtonDef3.Text = definitionsRandom(2) 'Randomly display a definition. Checks against correctDefinition to see make sure one definition is always the right answer.
Next
End Sub
Private Sub ButtonNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonNext.Click
'If (RadioButtonDef1.Checked And RadioButtonDef1.Text = correctDefinition) Or (RadioButtonDef2.Checked And RadioButtonDef2.Text = correctDefinition) Or (RadioButtonDef3.Checked And RadioButtonDef3.Text = correctDefinition) Then
' If kv(keyword).Answered Then
' kv.Remove(keyword)
' Else
' kv(keyword).Answered = True
' End If
'Else
' MsgBox("Incorrect Answer!")
'End If
If (RadioButtonDef1.Checked And RadioButtonDef1.Text = correctDefinition) Or (RadioButtonDef2.Checked And RadioButtonDef2.Text = correctDefinition) Or (RadioButtonDef3.Checked And RadioButtonDef3.Text = correctDefinition) Then
MsgBox("Correct Answer")
Else
MsgBox("Incorrect Answer. The correct answer is: " & correctDefinition)
End If
End Sub
End Class
I use a subroutine in my buttonNext_click to prevent duplicate code, as you can see above Private Sub RandomiseAnswers().
my issue however is when I come down to the If/else statement where I determine whether the user has selected the correct answer. As you can see I have commented out the original line as I found that it didn't work, and therefore I created one which would display a message box. By using that If/Else statement I have found that it always displays the Incorrect Answer message box and it doesn't display the correct definition.
What is puzzling me the most is that I know that correctDefinition works as it will always display the correct definition to the current keyword however I cannot seem to fathom out how to make it work in the conditional/s.
Does anyone have a possible solution as to why it doesn't seem to like being used in the If/Else statement/s but does work when randomising.
Sorry if it is hard to understand what I am asking.
Any help is much appreciated.
EDITS
code relevant to the Answer class
Public Class Answer
Public Answer As String
Public Answered As Boolean
End Class
You declare correctDefinition twice in your code. Declare it once or better yet make correctDefinition an array eg.
Private correctDefinition() String = New String() { "Definition 1", "Definition 2", "Definition 3" }

Can I create a property of a property?

So I recently grasped the concept of using classes in my Visual Basic programming, and I found it tremendously helpful. In my current project, I have several groups boxes of check boxes (each check box denotes a "Behavior") and in each group box, there is always one check box that has a textbox control instead of a label (to allow the user to specify an "Other" behavior). It is that user-generated label that is giving me trouble...
I created a class called "Behaviors" that basically does the following:
getChecked > This method gets each checked checkbox and adds it to
the BehaviorCollection for a given Form.
behaviorCollection > represents the collection of checked
checkboxes.
getOtherChecked > does the same as "getChecked" except with the
"Other Behavior" checkboxes.
otherBehaviorCollection > represents the collection of checked
"Other" checkboxes.
The issue is that for each checked "Other Behaviors" checkbox, I need to store the value of its corresponding textbox. I would like to set my getOtherChecked() method to do this, so that in the end, I would be able to something like this...
Dim myBoxes as new Behaviors
Dim cBox as Checkbox
Dim cBoxLabel as String
myBoxes.getOtherChecked(myUserForm) 'This would get each checked "Other Behaviors" checkbox object, and also somehow add another property to it called "LinkedTextboxLabel" that would be assigned the value of the corresponding textbox.
cBox = myBoxes.otherBehaviorCollection.item(0) 'Assign a checkbox from my "Other Behaviors" collection to a variable.
cBoxLabel = cBox.LinkedTextboxLabel 'Assign the user-inputted value of the linked textbox to a variable.
So basically how could/should I add a custom-property to a collection item or checkbox?
I thought about just adding the names of the controls to a temporary DataTable or SQL table, so that each row would have the name of a checkbox in one column and its corresponding textbox value in the next, but I am hoping there is a more commonly used and accepted method.
Thank you in advance!
You could add a property for the text associated with the "Other Behaviors" checkbox.
EDIT: You might be trying to generalize your data too far, because the "Other behaviors" is a special case and deserves separate consideration.
If you have a look at what the following code (in a new Windows Forms project) creates, it might give you ideas:
Public Class Form1
''' <summary>
''' A behaviour domain and its characteristics, with one user-defined entry.
''' </summary>
''' <remarks></remarks>
Public Class BehavioursSectionDescriptor
Property BehaviourTypeName As String
Property BehaviourNames As List(Of String)
Property CustomBehaviours As String
End Class
''' <summary>
''' Return a GroupBox containing CheckBoxes and one Checkbox with a TextBox adjacent to it.
''' </summary>
''' <param name="behaviourSet"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function GetBehaviourGroupPanel(behaviourSet As BehavioursSectionDescriptor) As GroupBox
Dim gb As New GroupBox
gb.Text = behaviourSet.BehaviourTypeName
Dim fixedBehaviourNames As List(Of String) = behaviourSet.BehaviourNames
Dim customBehavioursValue As String = behaviourSet.CustomBehaviours
Dim cbVertSeparation As Integer = 4
Dim gbPadding As Integer = 20
Dim cb As New CheckBox
Dim yLoc As Integer = gbPadding
For i = 0 To fixedBehaviourNames.Count - 1
cb = New CheckBox
cb.Location = New Point(gbPadding, yLoc)
cb.Text = fixedBehaviourNames(i)
' you can use the .Tag Object of a Control to store information
cb.Tag = behaviourSet.BehaviourTypeName & "-Cb-" & i.ToString()
gb.Controls.Add(cb)
yLoc += cb.Height + cbVertSeparation
Next
cb = New CheckBox
cb.Text = ""
cb.Location = New Point(gbPadding, yLoc)
cb.Tag = behaviourSet.BehaviourTypeName & "-Custom behaviours"
gb.Controls.Add(cb)
Dim tb As New TextBox
tb.Location = New Point(gbPadding + 18, yLoc)
tb.Width = 100
tb.Text = customBehavioursValue
gb.Controls.Add(tb)
' make sure the textbox appears in front of the checkbox's label area
tb.BringToFront()
gb.Size = New Size(160, yLoc + gbPadding * 2)
Return gb
End Function
Private Function GetTestData() As List(Of BehavioursSectionDescriptor)
Dim bsds = New List(Of BehavioursSectionDescriptor)
bsds.Add(New BehavioursSectionDescriptor With {.BehaviourTypeName = "In water", _
.BehaviourNames = New List(Of String) From {"Floats", "Spins"}, _
.CustomBehaviours = "Paddles"})
bsds.Add(New BehavioursSectionDescriptor With {.BehaviourTypeName = "Under light", _
.BehaviourNames = New List(Of String) From {"Shines", "Glows", "Reflects"}, _
.CustomBehaviours = "Iridesces"})
bsds.Add(New BehavioursSectionDescriptor With {.BehaviourTypeName = "Near food", _
.BehaviourNames = New List(Of String) From {"Sniffs", "Looks"}, _
.CustomBehaviours = ""})
Return bsds
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim bsds As List(Of BehavioursSectionDescriptor) = GetTestData()
Dim gbs As New List(Of GroupBox)
Dim xLoc As Integer = 20
Dim yLoc As Integer = 20
' make some GroupBoxes to present the data input fields
For i = 0 To bsds.Count - 1
Dim gb = GetBehaviourGroupPanel(bsds(i))
gb.Location = New Point(xLoc, yLoc)
gb.Dock = DockStyle.None
yLoc += gb.Height + 30
Me.Controls.Add(gb)
Next
' size the form to fit the content
Me.Size = New Size(240, yLoc + 40)
End Sub
End Class
I know it doesn't answer the question of adding a property to a property, but could you create a class for the Other checkbox and override it's capabilities? Then you could add checkboxes and OtherCheckBoxes to your generic collection? for instance, (by no means complete, but you should get the idea)
EDIT: Changed code to show Shadows
Public Class OptionalCheckbox : Inherits CheckBox
Private mOptionalText As String
Public Shadows Property Text() As String
Get
Return mOptionalText
End Get
Set(value As String)
mOptionalText = value
MyBase.Text = value
End Set
End Property
End Class
For each item, if you were to retrieve .Text, you would either get your textbox value or your checkbox label (if it was a normal checkbox)
And how to utilize in other parts of your code. Again, this is just more of an example. You would still need to work with the textbox that is assigned to the OtherCheckBox to get it to write the text to that, as well as read from that into the .Text property of the Class.
Dim newCheckBoxCollection As New Collection
Dim cBox As New CheckBox
cBox.Text = "Standard Value Here"
'other properties of the checkbox can be modified here
newCheckBoxCollection.Add(cBox)
Dim cOBox As New OptionalCheckbox
cOBox.Text = "Optional Text Here"
'other properties of the checkbox can be modified here
newCheckBoxCollection.Add(cOBox)
For Each cb As CheckBox In newCheckBoxCollection
Me.FlowLayoutPanel1.Controls.Add(cb)
Next
If you are trying to just save the data into something like a DataTable or SQL table the code would be a bit of an overkill. I suggest you use a stream reader/writer and try checking the values that way as the code would be a lot more simple.

Referencing an Unbound DataGridView Without Specifically Naming It?

I am using 3 unbound DataGridView controls to display certain information. To load the information into those DGVs, I am pulling the information from an encrypted file, decrypting it, parsing the information, then trying to fill the DGVs with that information. The loading from the file is called by the menu item click. Here is what I have so far:
Private Sub miCLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles miCLoad.Click
Dim FilePath As String = "C:\FList\CList.clt"
Dim LoadFile As New SaveandLoad.SaveAndLoad
Dim FileRead As New Simple3Des("MyPassword")
Dim FileString As String = FileRead.ReadFile(FilePath)
With LoadFile
.WhichList = dgCourses
.FilePath = FilePath
.DecryptedString = FileRead.DecryptData(FileString)
.dgList = dgCourses
End With
Call LoadFile.LoadFile()
End Sub
Public Class SaveandLoad
Public Property WhichList As New DataGridView
Public Property FilePath As String
Public Property DecryptedString As String
Public Property EncryptedString As String
Public Property dgList As Control
Public Sub LoadFile()
Dim dgRow As DataGridViewRow
Dim dgCell As DataGridViewTextBoxCell
Dim Lines() As String = DecryptedString.Split(vbLf)
Dim LinesList As List(Of String) = Lines.ToList
LinesList.RemoveAt(Lines.Length - 1)
For Each Line As String In LinesList
Dim Fields() As String = Line.Split(",")
dgRow = New DataGridViewRow
For x = 0 To (WhichList.Columns.Count - 1) Step 1
dgCell = New DataGridViewTextBoxCell
dgCell.Value = Fields(x).ToString
dgRow.Cells.Add(dgCell)
Next
WhichList.Rows.Add(dgRow)
Next
Select Case WhichList.Name
Case "dgCourses"
frmFacultyList.dgCourses = WhichList
frmFacultyList.dgCourses.Refresh()
WhichList.Dispose()
Case "dgFList"
frmFacultyList.dgFList = WhichList
frmFacultyList.dgFList.Refresh()
WhichList.Dispose()
Case "dgSList"
frmFacultyList.dgSList = WhichList
frmFacultyList.dgSList.Refresh()
WhichList.Dispose()
End Select
MsgBox("List Successfully Loaded", vbOKOnly, "Load")
End Sub
I want to be able to reference (or fill) a DGV without using 'select case' or 'if-then' statements. This will be too inefficient once I start adding the many other DGVs, that will be added in the future. Therefore, the title is the main question. I am using VS Express 2010.
I don't know VB too much, however, I'll post my solution in C# (may be helpfull in some way....)
DataGridView myDGV;
foreach (var item in this.Controls)
{
if (item.GetType() == typeof(DataGridView))
{
if (((DataGridView)item).Name == WhichList.Name)
{
//Cannot assing to 'item' here, because it is a 'foreach iteration variable'
//However you can save the variable for later use.
myDGV = (DataGridView)item;
}
}
}
myDGV = WhichList;
// different approach
DataGridView myDGV = (DataGridView)this.Controls.Find(WhichList.Name, false).First();
myDGV = WhichList;
Here is what worked for me in VB.NET:
Dim FormControls As New frmFacultyList.ControlCollection(frmFacultyList)
For Each DGV As DataGridView In FormControls
If WhichList.Name = DGV.Name Then
DGV = WhichList
DGV.Refresh()
End If
Next
Make an instance of the control collection then search specifically for DGVs using For Each. Simple and efficient.