How to handle events from Multiple forms of the same class - vb.net

I create a number of sub-forms, each of the same class, from my parent form.
Each of the subforms defines an event that should cause the parent form to create a new subform of a different class. - However only the latest created sub form is having it's events handled.
Thinking that it is because I have reused the reference to the sub-form, I store each of the subforms in a linked list of type subform.
SubForm Code.
Public Class UserEditor
Property ID As Integer
Public Event EditGroup(ByRef group As GroupPrincipal, ByVal newWindow As Boolean, ByVal Source As Integer)
Public Sub New(ByVal U As UserPrincipal, ByVal theId As Integer)
InitializeComponent()
ID = theId
UserEditor1.DisplayUserPrincipal(U)
End Sub
Private Sub UserEditor1_GroupEdit(ByRef groupItem As GroupListItem) Handles UserEditor1.GroupEdit
RaiseEvent EditGroup(groupItem.Grp, False, ID)
End Sub
Code on parentform
Public Class ASADManager
Dim WithEvents UserEditorInstance As UserEditor
Dim WithEvents UserEditorList As New List(Of UserEditor)
This code triggered on selection of leafobject in a displayed list.
If TypeOf leafObject Is UserPrincipal Then
UserEditorInstance = New UserEditor(currentLeaf.Principal, UserEditorList.Count) With {
.Text = currentLeaf.SamAccountName
}
UserEditorInstance.Show()
UserEditorInstance.Activate()
UserEditorList.Add(UserEditorInstance)
End If
Private Sub EditGroup(ByRef grp As GroupPrincipal, newWindow As Boolean, Source As Integer) Handles UserEditorInstance.EditGroup
If Not newWindow Then
Dim GEdit As New GroupEditorForm(grp) With {
.Text = grp.Name
}
GEdit.Show()
End If
End Sub
Everything works fine in the latest opened UserEditorInstance, it captures and re-throws the event, and that is cause by the parent, which opens the GroupEditor form.
However, if I select a 2nd LeafObject (to open another UserEditor window) it's events are trapped, and not the previous.
How do I trap both?
Hope this explanation makes it clear what I am doing (and doing wrong)

Related

ONMOUSEMOVE wont TRIGGER after selecting BUTTON (VB.NET)

Please help.
I have a form and a class.
Form - frmTestTool
Class - MainClass
What I am trying to do is to print text everytime the mouse cursor is moved. So the scenario is, I have a software where I embedded the custom command. So I open the custom command and the form will pop up, I need to select somewhere in the software before clicking the "PlaceText" button in the form. After clicking the "PlaceText" button it will implement btnPlaceText_Click_1 but will no longer trigger "OnMouseMove".
Scenario 1(WORKING WELL steps)
Select location in the software
Open Custom Command
Select Place Text
Move MouseCursor (prints "Hello Word" every mouse move)
Scenario 2(NOT WORKING steps)
Open Custom Command
Select location in the software
Select Place Text
Move Mouse Cursor (this time, OnMouseMove does not triggered)
Here's the Code
Partial Public Class frmTestTool
Inherits Form
Public Sub btnPlaceText_Click_1(sender As Object, e As EventArgs) Handles btnPlaceText.Click
WriteMessage("Hello World")
End Sub
End Class
Public Class TextWizard
Inherits BaseStepCommand
Private Shared ofrmTestTool As frmTestTool = New frmTestTool()
Public Overrides Sub OnSuspend()
MyBase.OnSuspend()
End Sub
Public Overrides Sub OnResume()
MyBase.OnResume()
End Sub
Public Overrides Sub OnStart(ByVal commandID As Integer, ByVal argument As Object)
MyBase.OnStart(commandID, argument)
Try
m_running = True
m_oTxnMgr = ClientServiceProvider.TransactionMgr
ofrmTestTool = New frmTestTool()
ofrmTestTool.Show()
Catch commonException As CmnException
ClientServiceProvider.ErrHandler.ReportError(ErrorHandler.ErrorLevel.Critical, MethodBase.GetCurrentMethod().Name, commonException, commandFailed)
End Try
End Sub
Protected Overrides Sub OnMouseDown(ByVal view As GraphicView, ByVal e As GraphicViewManager.GraphicViewEventArgs, ByVal position As Position)
MyBase.OnMouseDown(view, e, position)
ofrmTestTool.btnPlaceText_Click_1(Nothing, Nothing)
End Sub
End Class
I think you need to use RemoveHandler
RemoveHandler Me.MouseMove, AddressOf OnMouseMove
If you don't know the addresses of the handlers then you will need to use System.Reflection to find and remove them.
Sub RemoveEvents(Of T As Control)(Target As T, ByVal EventName As String)
Dim oFieldInfo As FieldInfo = GetType(Control).GetField(EventName, BindingFlags.[Static] Or BindingFlags.NonPublic)
Dim oEvent As Object = oFieldInfo.GetValue(Target)
Dim oPropertyInfo As PropertyInfo = GetType(T).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim oEvenHandlerList As EventHandlerList = CType(oPropertyInfo.GetValue(Target, Nothing), EventHandlerList)
oEvenHandlerList.RemoveHandler(oEvent, oEvenHandlerList(oEvent))
End Sub
Call the Sub like this:
RemoveEvents(Me, "EventMouseMove")
' This can be used for event, just enter the string as "Event<EventName>" where the event name is the event you want to remove all handlers for on the target.

String form name to form reference

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

showing a new Form Using a public function in vb.net

In my application I have to show lot of forms when particular button/panels are clicked. so instead of writing
Frm = New formname
Frm.MdiParent = MDIParent
Frm.Show()
i want to have public function through which i can pass the form name.
for that i have written a function
Public Sub showForm(ByVal formname As Form)
Frm = New formname
Frm.MdiParent = MDIParent1
Frm.Show()
End Sub
Call showForm(myformname)
but problem with this is, it says formname is not defined
EDIT:
I updated my answer to reflect your comment that a form should only be opened once.
I want to have public function through which i can pass the form name.
for that i have written a function
Public Sub showForm(ByVal formname As Form)
You don´t pass the name of a form to your function but an object of type Form instead.
Here is one possible solution with a generic version of showForm:
Public Class FormManager
Private _formByName As New Dictionary(Of String, Form)
Public Sub showForm(Of T As {Form, New})(name As String, parent As Form)
Dim frm As Form = Nothing
If Not _formByName.TryGetValue(name, frm) OrElse _formByName(name).IsDisposed Then
frm = New T()
_formByName(name) = frm
End If
frm.MdiParent = parent
frm.Show()
End Sub
End Class
The FormManager holds a dictionary cache for all opened forms with Key=form name. This is to make sure that a form is only opened once. The check form.IsDisposed makes sure that you can close the form and reopen it.
Usage from the parent form:
Public Class Form1
Private fm = New FormManager()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
fm.showForm(Of MyForm)("MyForm", Me)
End Sub
End Class
The first parameter is to identify the form name. The real magic is in the Type T which we made sure it is 1) of type or subtype Form and 2) it has a parameterless constructor (MyForm is just a placeholder for this example put in the type of your real form you want to show).
The parent parameter will bring you additional flexibility if it is not always MDIParent1. Remove it if you don´t neet the extra flexibility.
For sure you can also drop the FormManager class and put the showForm to another place.

Bind DataSource to new DevExpress Report Designer?

I'm trying to figure out how to set my DataSource as the default when a user clicks New Report, or for any new report, in the DevExpress User Data Report Designer.
Right now, the Blank Report I have load on Form_Load has my DataSources just fine, but anytime I hit New Report, they're gone.
I've googled and followed the docs, but they all seem to be geared towards opening a specific report (as above).
Can anyone help?
0. ICommandHandler interface
You need to handle the ReportCommand.NewReport command by implementing the ICommandHandler interface. You must pass an object that implementing this interface to the XRDesignMdiController.AddCommandHandler method. You can get XRDesignMdiController object from ReportDesignTool.DesignForm.DesignMdiController property or from ReportDesignTool.DesignRibbonForm.DesignMdiController property according to what type of form you want to use.
Here is example:
Private Sub ShowReportDesigner()
Dim tool As New ReportDesignTool(CreateReport)
Dim controller = tool.DesignRibbonForm.DesignMdiController
Dim handler As New NewCommandHandler(controller, AddressOf CreateReport)
controller.AddCommandHandler(handler)
tool.ShowRibbonDesigner()
End Sub
Private Function CreateReport() As XtraReport
Dim report As New XtraReport
report.DataSource = YourDataSourceObjectHere
Return report
End Function
Public Class NewCommandHandler
Implements ICommandHandler
Private ReadOnly _controller As XRDesignMdiController
Private ReadOnly _createReport As Func(Of XtraReport)
Public Sub New(controller As XRDesignMdiController, createReport As Func(Of XtraReport))
_controller = controller
_createReport = createReport
End Sub
Public Function CanHandleCommand(command As ReportCommand, ByRef useNextHandler As Boolean) As Boolean Implements ICommandHandler.CanHandleCommand
useNextHandler = command <> ReportCommand.NewReport
Return Not useNextHandler
End Function
Public Sub HandleCommand(command As ReportCommand, args() As Object) Implements ICommandHandler.HandleCommand
_controller.OpenReport(_createReport())
End Sub
End Class
1. DesignPanelLoaded event
The another way is to subscribe to XRDesignMdiController.DesignPanelLoaded event. In this event you can check where the DataSource of report in loaded panel is empty and set it to your data source.
Here is example:
Private Sub ShowReportDesigner()
Dim report As New XtraReport
report.DataSource = YourDataSourceObjectHere
Dim tool As New ReportDesignTool(New XtraReport)
Dim controller = tool.DesignRibbonForm.DesignMdiController
AddHandler controller.DesignPanelLoaded, AddressOf mdiController_DesignPanelLoaded
tool.ShowRibbonDesigner()
End Sub
Private Sub mdiController_DesignPanelLoaded(ByVal sender As Object, ByVal e As DesignerLoadedEventArgs)
Dim panel = DirectCast(sender, XRDesignPanel)
Dim report = panel.Report
If IsNothing(report.DataSource) Then
report.DataSource = YourDataSourceObjectHere
End If
End Sub

Create and loop through collection subset of controls

Im making a small vb.net windows form application in which I have 4 ComboBoxes. I would like to add the ComboBoxes to a collection and be able to loop through that collection to refer to each one.
There are other ComboBoxes on the form so I cannot just use the collection for the entire form (the form layout cannot be changed, e.g. to add a container, etc).
I was thinking something like the following:
Public Class Form1
Dim IoTypeCombos As New ControlCollection(Me) From {Me.IO1_ComboBox, Me.IO2_ComboBox, Me.IO3_ComboBox, Me.IO4_ComboBox}
Dim IoTypes As New Collection() From {"Out 0", "Out 1", "Input", "Analog"}
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
For Each cb As combobox In Me.IoTypeCombos
FillComboBox(cb, Types)
Next
End Sub
Function FillComboBox(cb As Control, cc As Collection) As Boolean
Dim cbc As ComboBox = CType(cb, ComboBox)
If cc.Count = 0 Then
Return False
End If
For Each cn In cc
cbc.Items.Add(cn)
Next
Return True
End Function
This doesn't raise any exception, BUT it doesn't populate the ComboBoxes either :(
The FillComboBox() works perfectly if I pass a single control to it.
What am I doing wrong? Thanks
This line is illegal:
Public Class Form1
Dim IoTypeCombos As New ControlCollection(Me) From {Me.IO1_ComboBox,
Me.IO2_ComboBox, Me.IO3_ComboBox, Me.IO4_ComboBox }
That code will run before the constructor, before Me or ION_ComboBox exist. In this case, the resulting collection contains nothing since there is nothing to put in it yet.
In other cases, referencing controls before they exist can result in a NullReference being thrown, but due to an odd bug it may not be reported. When that happens, the rest of the code is skipped and the form simply shown.
In either case, the solution is to declare your collection at the form level, but populate it in the form load event once the controls do exist. I would also use a Collection(Of T) instead (an array or List(Of T) will also work, the OP uses/asks about a collection though):
Imports System.Collections.ObjectModel
Public Class Form1
Dim IoTypeCombos As Collection(Of ComboBox) ' form and controls Do No Exist yet
Public Sub New
'...
InitializeComponent()
' NOW they exist
End Sub
Sub Form_Load
IoTypeCombos = New Collection(Of ComboBox)
IoTypeCombos.Add(IO1_ComboBox)
IoTypeCombos.Add(IO2_ComboBox)
...
If you use a List(Of ComboBox), you can populate it different ways:
' in the ctor:
IoTypeCombos = New List(Of ComboBox)({IO1_ComboBox, IO2_ComboBox...})
' using AddRange:
IoTypeCombos.AddRange({IO1_ComboBox, IO2_ComboBox...})
Not sure if you need the where clause, but if you have other comboboxes that do not have names like this and do not want them in the collection then you do need it.
Dim IoTypeComboboxes =
Me.Controls.OfType(Of Combobox)().Where(Function(cb) cb.Name.StartsWith("IO")).ToList()
'on yourFormName
'added :
'45 PictureBox:("PicBarNum1_NotLastOdig" to "PicBarNum45_NotLastOdig")
'added:
'45 PictureBox:("PicBarNum1_UkOdig" to "PicBarNum45_UkOdig")
Public Class yourFormName
Private picbarlistNum1to45_UkOdig As New List(Of PictureBox)
Private picbarlistNum1to45_UkLastNotOdig As New List(Of PictureBox)
Private sub yourFormName_Load
Call AddPicBoxesInList_OdigNoOdig()
End sub
Private Sub AddPicBoxesInList_OdigNoOdig()
picbarlistNum1to45_UkOdig.Clear()
picbarlistNum1to45_UkLastNotOdig.Clear()
picbarlistNum1to45_UkOdig = Me.Controls(0).Controls.OfType(Of PictureBox)()
.Where(Function(pb) pb.Name.StartsWith("PicBarNum") And
pb.Name.EndsWith("_UkOdig")).ToList()
picbarlistNum1to45_UkLastNotOdig = Me.Controls(0).Controls.OfType(Of
PictureBox)().Where(Function(pb) pb.Name.StartsWith("PicBarNum") And
pb.Name.EndsWith("_NotLastOdig")).ToList()
End Sub
End Class