Hello again StackOverflow community.
I need to add text to an externally generated form into TextBox.
My Code (For generating new form):
Public Sub NewPage()
Dim newtab As New CenterForm1
newtab.MdiParent = Me
newtab.Show(DockPanel1)
newtab.ComID.Text = newtab.codeblock.Name
newtab.codeblock.Name = +count
newtab.Name = +count
count += 1
End Sub
My Code (for insert text to TextBox):
Dim GetActive As GetActiveWindow
Dim codeblock As New TextBox
GetActive.codeblock.Text = "TESTIIIING"
Code for "GetActivateWindow" class
Imports WeifenLuo.WinFormsUI.Docking
Public Class GetActiveWindow
Public Property codeblock As TextBox
Public Function GetActive()
'Verify if forms that dock in main window are already open
For Each form As DockContent In Form1.DockPanel1.Contents
If form.DockHandler.Pane.ActiveContent.DockHandler.Form.Name.ToString() = form.Name.ToString() Then
Dim formName As String = form.Name.ToString()
Return formName
End If
Next
Return Nothing
End Function
End Class
Any ideas?
Related
I have a dynamically generated TabControl, and am trying to update a Combobox in the TabPage. The function that updates the Combobox is called of a click event.
I've tried to follow some guides regarding manipulating dynamically generated controls by storing the dynamically generated controls as properties on the class: How to pass value from one form to another form's dynamically created control
The controls are generated dynamically as such:
Public Class Form1
Public Sub loadForm()
Dim ctp As New CustomTabPage("Tab number" & i, ErrorList(i), myList, New Object)
Me.myTabControl.TabPages.Add(ctp)
...
End Sub
End Class
Public Class CustomTabPage
Inherits TabPage
Private m_testSelect As ComboBox
...
Public Property testSelect() As ComboBox
Get
testSelect = m_testSelect
End Get
Set(ByVal mytestSelect As ComboBox)
m_testSelect = mytestSelect
End Set
End Property
Public Sub newTab()
m_testSelect = New ComboBox
With m_testSelect
.Location = New System.Drawing.Point(locX + labelSize, locY)
End With
Me.Controls.Add(m_testSelect)
Dim ccb As New CustomCheckBox()
Me.Controls.Add(m_testSelect)
End Sub
Public Sub UpdateCBOs(ByVal i As Integer)
If i = 1 Then
testSelect.Text = "Test1"
ElseIf i = 0 Then
testSelect.Text = "Test2"
End If
...
End Sub
End Class
Public Class CustomCheckBox
Inherits CheckBox
Public Sub Clicked(sender As Object, e As EventArgs) Handles MyBase.CheckedChanged
Dim ctp = CType(Form1.myTabControl.SelectedTab, CustomTabPage)
ctp.UpdateCBOs(i)
End Sub
End Class
Currently while debugging through I stop on the line after line
errorBy.Text = "Test1"
When I mouse over errorBy.Text, and see that errorBy.Text ="" and indeed after the click event finishes, I see on the form that the combobox is not updated.
Basically, I am rewriting some code working for years. Over the time I have many (60+) references to forms - there's a menuitem with OnClick event for each form, where a form reference was created:
Private Sub SomeForm_Click(sender As Object, e As EventArgs) Handles MenuItemForSomeForm.Click
NewTab("Some Form", New SomeForm, 0)
End Sub
...where first parameter is a name to put in a tabPage.Text where the form is opened, second is a new instance of the (particular) form SomeForm and 0 is a default record to display (0 means no default record).
Now, I created a dynamic menu and stored the form names in a database (due to better access control over the access rights, etc). Now, because the menu is generated at runtime, I can't have the OnClick event with separate instance definition of the form and have to create it at runtime, after the MenuItems are created. The side-effect idea was to cut the code short by using only 1 OnClick event or such with MenuItem.Tag paremeter as FormName. Something like:
Private Sub clickeventhandler(sender As Object, e As EventArgs)
Dim tsmi As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
Dim newForm As New >>>FormFrom(tsmi.Tag.ToString)<<< ' only explanation, this won't work
MainW.OpenModuleInTab(new newForm, tsmi.Tag.ToString, 0)
However I am failing to find a way to create form (instances) from this string reference. Reference through collection (i.e. List(of) or Dictionary) would be fine too, I believe.
The structure is obviously:
Object → Form → Form1 (class) → MyForm1 (instance)
I know I can create an object like this:
' Note that you are getting a NEW instance of MyClassA
Dim MyInstance As Object = Activator.CreateInstance(Type.GetType(NameOfMyClass))
I can re-type it to a Form type:
Dim NewForm as Form = CType(MyInstance,Form)
... to acccess some of the form properties like Width, TopLevel, etc., but that's about it. I can't do:
Dim NewForm1 as Form1 = CType(NewForm,Form1)
...because obviously, Form1 comes as a string "Form1".
I don't know how to create a Form1 reference from a "Form1" text (then it would be easy to create an instance) or how to create an instance directly (MyForm1).
SOLUTION
As sugested, I used reflection to get the form. The only way working for me I found was this:
Dim T As Type = System.Type.GetType(FormName, False)
If T Is Nothing Then 'if not found prepend default namespace
Dim Fullname As String = Application.ProductName & "." & FormName
T = System.Type.GetType(Fullname, True, True)
End If
Dim f2 As New Form ' here I am creating a form and working with it
f2 = CType(Activator.CreateInstance(T), Form)
f2.TopLevel = False
f2.Name = FormName.Replace(" ", "") & Now.ToString("yyyyMMddmmhh")
f2.FormBorderStyle = FormBorderStyle.None
f2.Dock = DockStyle.Fill
I am using VB.net CallByName to set public variable and same function to run a sub method (every form contains RecordID variable and LoadRecords sub):
CallByName(f2, "RecordID", CallType.Set, 111)
CallByName(f2, "LoadRecords", CallType.Method, Nothing)
For testing purposes, I put following into the testing form:
Public RecordID As Int32
Public Sub LoadRecords()
MsgBox("Load records!!!!" & vbCrLf & "RecordID = " & RecordID)
End Sub
Activator.CreateInstance(TypeFromName("Form1"))
TypeFromName Function:
Dim list As Lazy(Of Type()) = New Lazy(Of Type())(Function() Assembly.GetExecutingAssembly().GetTypes())
Function TypeFromName(name As String) As Type
Return list.Value.Where(Function(t) t.Name = name).FirstOrDefault()
End Function
So, let's go with the idea that I have an assembly called "WindowsApp2" and in that assembly I've defined Form1 and Form2. I've also created this module in the same assembly:
Public Module Module1
Public Function GetDoStuffWiths() As Dictionary(Of Type, System.Delegate)
Dim DoStuffWiths As New Dictionary(Of Type, System.Delegate)()
DoStuffWiths.Add(GetType(WindowsApp2.Form1), CType(Sub(f) WindowsApp2.Module1.DoStuffWithForm1(f), Action(Of WindowsApp2.Form1)))
DoStuffWiths.Add(GetType(WindowsApp2.Form2), CType(Sub(f) WindowsApp2.Module1.DoStuffWithForm2(f), Action(Of WindowsApp2.Form2)))
Return DoStuffWiths
End Function
Public Sub DoStuffWithForm1(form1 As Form1)
form1.Text = "This is Form 1"
End Sub
Public Sub DoStuffWithForm2(form2 As Form2)
form2.Text = "This is Form 2"
End Sub
End Module
Now, in another assembly "ConsoleApp1" I write this:
Sub Main()
Dim DoStuffWiths As Dictionary(Of Type, System.Delegate) = WindowsApp2.Module1.GetDoStuffWiths()
Dim formAssembly = System.Reflection.Assembly.Load("WindowsApp2")
Dim typeOfForm = formAssembly.GetType("WindowsApp2.Form1")
Dim form As Form = CType(Activator.CreateInstance(typeOfForm), Form)
DoStuffWiths(typeOfForm).DynamicInvoke(form)
Application.Run(form)
End Sub
When I run my console app I get a form popping up with the message "This is Form 1".
If I change the line formAssembly.GetType("WindowsApp2.Form1") to formAssembly.GetType("WindowsApp2.Form2") then I get the message "Wow this is cool".
That's how you can work with strongly typed objects that you dynamically instantiate.
Dim AssemblyProduct As String = System.Reflection.Assembly.GetExecutingAssembly().GetName.Name
Dim FormName As String = "Form1"
Dim NewForm As Object = Reflection.Assembly.GetExecutingAssembly.CreateInstance(AssemblyProduct & "." & FormName)
If TypeOf (NewForm) Is Form1 Then
Dim NewForm1 As Form1 = CType(NewForm, Form1)
NewForm1.BackColor = Color.AliceBlue
NewForm1.Show()
End If
I'm trying to generate a list of all of the TableName and FieldName properties for a custom object type called LxTextBox. I've gotten as far as generating a list of all of the LxTextBox names on my form, but I can't figure out a way to call the properties of the custom object... I've been looking into System.Reflection, but I haven't ever used it. Additionally, I'm returning the list to a RichTextBox while I'm testing this out, but ultimately, I need to return each objects properties as a data row. Example:
ObjectName Table Field
---------------------------------------
LxTextBox23 SomeTbl SomeFld
Here's my code to return the list - updated based on #OneFineDay...
Imports System.Collections.Generic
Imports Application.UDF.Controls
Public Class MeasurementsControl
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim textBoxList As New List(Of Control)
Dim customTbs = GetAllControls(Me)
Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To customTbs.Count - 1
sb.Append(customTbs.Item(index).TableName & "." & customTbs.Item(Index).FieldName & System.Environment.NewLine)
Next
RichTextBox1.Text = sb.ToString
End Sub
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
If searchWithin.HasChildren Then
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
For Each ctrl As Control In searchWithin.Controls
returnList.AddRange(GetAllControls(ctrl))
Next
End If
Return returnList
End Function
End Class
I made the changes suggested and I'm throwing an error: OfType is not a member of System.Windows.Forms.Control.ControlCollection
FYI - Adding Imports System.Linq did not fix the error.
You are boxing it into a Control the object from where it derives, where you're custom properties cannot be found. You can cast it right from the control collection.
Dim customTbs = GetAllControls(Me)
'recursive function
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
If searchWithin.HasChildren Then
For Each ctrl As Control In searchWithin.Controls
Dim ctrls = GetAllControls(ctrl)
If Not ctrls Is Nothing Then returnList.AddRange(ctrls)
Next
End If
Return returnList
End Function
I'm trying to access the Context Menu UI AutomationElement of Notepad, however I am struggling to do so:
Imports System.Windows.Automation
Imports System.Windows.Forms
Module AutomateNotepad
Sub Main()
Dim wNotepad, document As AutomationElement
wNotepad = AutomationElement.RootElement.FindFirst(TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "Untitled - Notepad"))
document = wNotepad.FindFirst(TreeScope.Children, New PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "document"))
document.SetFocus()
SendKeys.SendWait("+{F10}")
context = AutomationElement.RootElement.FindFirst(TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "context"))
While context Is Nothing
Console.WriteLine("Trying to get context again")
Threading.Thread.Sleep(100)
context = AutomationElement.RootElement.FindFirst(TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "context"))
End While
MsgBox("Found it!")
End Sub
End Module
My problem is that when I run the application, the notepad context menu opens but UIAutomation never appears to get the AutomationElement of it...
This is a screenshot from Inspect.exe:
Given the inspect picture and the structure it presents, I see no reason why this would be occurring... Does anyone know where I might be going wrong?
P.S. I'm very new to VB.NET but have been working with VBA for 2-3 years so I apologise for any bad habits I may have...
I've found a solution to my issue. The trick is to subscribe to UIAutomation OpenMenuEvent. To do this I created a ContextWatcher class:
Public Class ContextWatcher
Public Shared Menu As AutomationElement
Private Shared _EventHandler As AutomationEventHandler
Public Shared Sub trackContext()
_EventHandler = New AutomationEventHandler(AddressOf OnContextOpened)
Automation.AddAutomationEventHandler(AutomationElement.MenuOpenedEvent, AutomationElement.RootElement, TreeScope.Descendants, _EventHandler)
End Sub
Public Shared Sub untrackContext()
Automation.RemoveAutomationEventHandler(AutomationElement.MenuOpenedEvent, AutomationElement.RootElement, _EventHandler)
End Sub
Private Shared Sub OnContextOpened(src As Object, args As AutomationEventArgs)
Console.WriteLine("Menu opened.")
Dim element = TryCast(src, AutomationElement)
If element Is Nothing Then
Return
Else
Menu = element
End If
End Sub
End Class
To access the context menu I can use this:
ContextWatcher.trackContext()
SendKeys.SendWait("+{F10}")
Dim context As AutomationElement
context = ContextWatcher.Menu
While context Is Nothing
Console.WriteLine("Trying to get context again")
Threading.Thread.Sleep(100)
context = ContextWatcher.Menu
End While
' Do Stuff with context menu
ContextWatcher.untrackContext()
Imports System.Windows.Automation
Imports System.Windows.Forms
Module AutomateNotepad
Sub Main()
Dim wNotepad, document As AutomationElement
'Get 'Untitled - Notepad' main window
wNotepad = AutomationElement.RootElement.FindFirst(TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "Untitled - Notepad"))
'Get Notepad document element
document = wNotepad.FindFirst(TreeScope.Children, New PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "document"))
'Set focus to document
document.SetFocus()
'Start watching for context menu
ContextWatcher.trackContext()
'Open context menu
SendKeys.SendWait("+{F10}")
'Get context menu from ContextWatcher class
Dim context As AutomationElement
context = ContextWatcher.Menu
While context Is Nothing
Console.WriteLine("Trying to get context again")
Threading.Thread.Sleep(100)
context = ContextWatcher.Menu
End While
'trigger undo
invokeContextMenuItem(context, "Undo")
'Stop watching for context menu
ContextWatcher.untrackContext()
End Sub
Sub invokeContextMenuItem(context As AutomationElement, sMenuItem As String)
'Get context menu children
Dim controls As AutomationElementCollection = context.FindAll(TreeScope.Children, Condition.TrueCondition)
'Loop over controls to find control with name sMenuItem
Dim control As AutomationElement
For Each control In controls
If control.Current.Name = sMenuItem Then
'Invoke control
getInvokePattern(control).Invoke()
Exit Sub
End If
Next
End Sub
'Helper function to get InvokePattern from UI Element
Function getInvokePattern(element As AutomationElement) As InvokePattern
Return element.GetCurrentPattern(InvokePattern.Pattern)
End Function
Public Class ContextWatcher
Public Shared Menu As AutomationElement
Private Shared _EventHandler As AutomationEventHandler
Public Shared Sub trackContext()
_EventHandler = New AutomationEventHandler(AddressOf OnContextOpened)
Automation.AddAutomationEventHandler(AutomationElement.MenuOpenedEvent, AutomationElement.RootElement, TreeScope.Descendants, _EventHandler)
End Sub
Public Shared Sub untrackContext()
Automation.RemoveAutomationEventHandler(AutomationElement.MenuOpenedEvent, AutomationElement.RootElement, _EventHandler)
End Sub
Private Shared Sub OnContextOpened(src As Object, args As AutomationEventArgs)
Console.WriteLine("Menu opened.")
Dim element = TryCast(src, AutomationElement)
If element Is Nothing Then
Return
Else
Menu = element
End If
End Sub
End Class
End Module
I'm making a "Preference form" that will hold all the users preferences and when they go to Apply/Save I want the new values to transfer back to the main form and updateand close the form2. In the past I have done this like this:
Private Sub PreferencesToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PreferencesToolStripMenuItem.Click
Preferences.Show()
End Sub
and when I click the "Apply/Save" button before it closes I would Transfer all data like this:
form1.textbox.text = form2.textbox.text
Is there anything wrong doing it this way??
What I have been reading is I should be doing it like this:
Private Sub PreferencesToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PreferencesToolStripMenuItem.Click
Dim dialog As New Preferences
dialog.ShowDialog()
End Sub
And when when they click "Apply/Save" it would take all the values from Form2 and store them in a private variable (or Property) in Form2 and when that form closes I would then access the value like this:
Private Sub PreferencesToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PreferencesToolStripMenuItem.Click
Dim dialog As New Preferences
dialog.ShowDialog()
form1.textbox.text = dialog.variable
End Sub
Why would this be a better way of doing this?
UPDATE....Looking at the code below this is just a SMALL sample of all the options I will have. What is the best way to collect of the data into the object to use when serializing?
<Serializable>
Public Class Preference
#Region "Properties"
Public Property ScaleLowest As String = "5"
Public Property ScaleHighest As String = "200"
Public Property ScaleInc As String = "5"
Public Property ThickLowest As Double = 0.125
Public Property ThickHighest As Double = 4
Public Property ThickInc As Double = 0.125
Public Property WidthLowest As Double = 0.125
Public Property WidthHighest As Double = 0.6
Public Property WidthInc As Double = 0.125
Public Property LengthLowest As Double = 1
Public Property LengthHighest As Double = 96
Public Property LengthInc As Double = 1
Public Property FractionON As Boolean = False
Public Property DecimalON As Boolean = True
Public Property ColorSelection As String = "Colors"
Public Property FinalColor As String = "255, 255, 0"
Public Property roughColor As String = "255, 255, 100"
Public Property SplashON As Boolean = False
Public Property LogInON As Boolean = False
#End Region
Public Sub New()
'for creating new instance for deserializing
End Sub
Public Sub GatherAllData()
'Save Defaults
SaveSerializeObj()
End Sub
Public Sub SaveSerializeObj()
'Get Changes?????
'Serialize object to a text file.
Dim objStreamWriter As New StreamWriter("C:\Users\Zach454\Desktop\test.xml")
Dim x As New XmlSerializer(Me.GetType)
x.Serialize(objStreamWriter, Me)
objStreamWriter.Close()
End Sub
Public Function LoadSerializeObj() As Preference
'Check if new file need created
If File.Exists("C:\Users\454\Desktop\test.xml") = False Then
SaveSerializeObj()
End If
'Deserialize text file to a new object.
Dim objStreamReader As New StreamReader("C:\Users\454\Desktop\test.xml")
Dim newObj As New Preference
Dim x As New XmlSerializer(newObj.GetType)
newObj = CType(x.Deserialize(objStreamReader), Preference)
objStreamReader.Close()
Return newObj
End Function
The best option is to create a class that would have properties for your form controls. Then you can store these properties and then access these when needed.
Also there's really no reason to be passing data back and forth, you can store this data off somewhere (database, file, mysettings etc) and then load this data up into a class. Then you can store and retrieve data from this class. Then if you need to save data back to somewhere you have a class object to use.
Here is a short example to show how you can create another form (Preferences) click save and then show those values back on the other form (calling form).
This is the main form
Public Class Form1
Public _frm2 As Form2
Private Sub btnShowPreferences_Click(sender As Object, e As EventArgs) Handles btnShowPreferences.Click
Using _frm2 As New Form2()
'if we clicked save on the form then show the values in the
'controls that we want to
If _frm2.ShowDialog() = Windows.Forms.DialogResult.OK Then
txtFirstName.Text = _frm2._Preferences.FirstName
txtLastName.Text = _frm2._Preferences.LastName
End If
End Using
End Sub
End Class
Here is an example (Preferences) class
This class would hold all your properties for the preferences. This is an example, you can change anything you need to suit your needs.
Option Strict On
Public Class Preferences
#Region "Properties"
Public Property FirstName As String
Public Property LastName As String
#End Region
Public Sub New()
End Sub
End Class
The second Form could be your (Preference) form with all the controls a user would need to interact with.
Public Class Form2
Public _Preferences As New Preferences 'create class variable you can use for later to store data
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
'set your properties of the class from your form. this will then hold everything you can get from
'the first form...
With _Preferences
.FirstName = txtFirstName.Text
.LastName = txtLastName.Text
End With
Me.DialogResult = Windows.Forms.DialogResult.OK 'this is used to determine if user clicked a save button...
End Sub
End Class
I hope this get's you started, if you do not understand something please let me know.
To directly answer your question, the main difference in your two code samples is that the second uses ShowDialog to open the form modally, vs the first sample which lets you interact with the parent form while the second is open.
The second approach may be better from the view of user flow control. If your real question is whether to push data back to the main form or pull data from the dialog, it is probably better to pull from the dialog. This approach makes the dialog reusable from other forms.