How to pass a form as a parameter with events - vba

My function currently accepts a form and saves it to a variable so that the form's events are caught in this module. Thing is, the form is strongly typed - every form needs its own function. It's a lot of duplicate code, so I tried making it generic by typing the variable as Access.Form, but the events stopped firing.
I think it might be because now the form is only defined at runtime. Whatever the reason, does anyone know if it's possible to pass a form anonymously without losing its events?
Current code
Private WithEvents frm As [Form_Create]
Public Sub Run(parentForm As [Form_Create])
Set model = New mdlKita
Set frm = parentForm
End Sub
// then I can do things like
Public Sub frm_OnCreate()
Current code updated to be generic - events don't fire
Private WithEvents frm As Access.Form
Public Sub Run(parentForm As Access.Form)
Set model = New mdlKita
Set frm = parentForm
End Sub
// this never fires
Public Sub frm_OnCreate()
Inside the form in case someone's interested
Private ctrl As ctrCreate
Private Sub btnContinue_Click()
Set ctrl = New ctrCreate
ctrl.Run Me
RaiseEvent OnCreate
End Sub

I guess you miss to Initialize and Terminate a helper class - here for textboxes:
Option Explicit
' Helper class for form Palette for event handling of textboxes.
' 2017-04-19. Gustav Brock, Cactus Data ApS, CPH.
' Version 1.0.0
' License: MIT.
' *
Private Const EventProcedure As String = "[Event Procedure]"
Private WithEvents ClassTextBox As Access.TextBox
Public Sub Initialize(ByRef TextBox As Access.TextBox)
Set ClassTextBox = TextBox
ClassTextBox.OnClick = EventProcedure
End Sub
Public Sub Terminate()
Set ClassTextBox = Nothing
End Sub
Private Sub ClassTextBox_Click()
' Select full content.
ClassTextBox.SelStart = 0
ClassTextBox.SelLength = Len(ClassTextBox.Value)
' Display the clicked value.
ClassTextBox.Parent!CopyClicked.Value = ClassTextBox.Value
' Copy the clicked value to the clipboard.
DoCmd.RunCommand acCmdCopy
End Sub
taken from my tiny project VBA.ModernTheme

Related

Updating the text on a dynamically generated control In VB

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.

Handling custom events using modeless form & user defined classes

I'm trying to display the progress of various routines on a modeless form, by having those routines raise custom events detailing their progress. The form should handle those events to display appropriate information.
The problem is that although RaiseEvent is called, the event handlers don't then do anything.
The intended result of the following code is the two debug.prints would be called whenever an event is raised by triggerTest.
The only success I've had is with raising the error within the userform, by CommandButton1_Click in the following code. The form's event handler then kicks in (rather redundantly, but perhaps that means I'm on the right path).
Thanks
Event class clsChangeProgressTrigger
Option Explicit
Public Enum geProgressStatus
geProgressStatusComplete = -1
geProgressStatusRestart = -2
End Enum
Public Event ChangeProgress(dProgress As Double, sProcedure As String)
'
Public Sub Update(dProgress As Double, sProcedure As String)
RaiseEvent ChangeProgress(dProgress, sProcedure)
End Sub
Public Sub Complete(sProcedure As String)
RaiseEvent ChangeProgress(geProgressStatusComplete, sProcedure)
End Sub
Public Sub Restart(sProcedure As String)
RaiseEvent ChangeProgress(geProgressStatusRestart, sProcedure)
End Sub
User form frmOutput
Option Explicit
Private WithEvents mProgressTrigger As clsChangeProgressTrigger
'
Private Sub CommandButton1_Click()
Call mProgressTrigger.Update(12.34, "SomeValue")
End Sub
Private Sub CommandButton2_Click()
Call modZTest.triggerTest
End Sub
Private Sub UserForm_Initialize()
Set mProgressTrigger = New clsChangeProgressTrigger
End Sub
Private Sub mProgressTrigger_ChangeProgress(dProgress As Double, sProcedure As String)
Debug.Print "Form Event Handled"
End Sub
Event test class clsEventTest
Option Explicit
Private WithEvents mProgressTrigger As clsChangeProgressTrigger
'
Private Sub mProgressTrigger_ChangeProgress(dProgress As Double, sProcedure As String)
Debug.Print "Class Event Handled"
End Sub
Private Sub Class_Initialize()
Set mProgressTrigger = New clsChangeProgressTrigger
End Sub
Test wrapper in public module modZTest
Public Sub triggerTest()
Application.EnableEvents = True
' Instantiate Trigger class for this routine
' Dim cChangeProgressTrigger As clsChangeProgressTrigger
Set gChangeProgressTrigger = New clsChangeProgressTrigger
' Instantiate Event Test class, which should handle raised event
Dim cEventTest As clsEventTest
Set cEventTest = New clsEventTest
' Instantiate user form, which should handle raised event
Set gfrmOutput = New frmOutput ' Modeless form, gfrmOutput has global scope
gfrmOutput.Show
Stop
' Raise an event
Call gChangeProgressTrigger.Complete("SomeValue")
' Tidy Up
Set gfrmOutput = Nothing
Set gChangeProgressTrigger = Nothing
Set cEventTest = Nothing
End Sub
Thanks Dee, that helped me along to the solution.
With this declared as global scope:
Public gChangeProgressTrigger As clsChangeProgressTrigger
I had to change the class / form level initialisations as follows:
Private Sub UserForm_Initialize()
' Set mProgressTrigger = New clsChangeProgressTrigger ' Old
Set mProgressTrigger = gChangeProgressTrigger ' New
End Sub
and
Private Sub Class_Initialize()
' Set mProgressTrigger = New clsChangeProgressTrigger ' Old
Set mProgressTrigger = gChangeProgressTrigger ' New
End Sub
Then the event handlers fired as desired.

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

Why does my new form keep moving to the back?

Visual Basic .NET using Visual Studio 2013
I have a form that I open from another form, but when I do, it always goes behind the form that opened it. Al code that passes to the new form, gets passed before the form.Show().
Here is the code that opens the new form.
Private Sub OpenContentWindow(strNewNavigation As String)
Dim newContent As New FContent
newContent.SetIETMPath(strIETMPath)
newContent.SetIETMName(strIETMName)
newContent.SetIETMMan(strNewNavigation)
newContent.SetIETMIcon(strIETMIcon)
newContent.SetPageToLaunch(strNewNavigation)
newContent.Show()
End Sub
Here is the code from the new form.
Public Class FContent
#Region "Variables/Class Instances"
Private logger As New CDataLogger
Private pathing As New CPaths
Private annotes As New CAnnotes
Private mouser As New CMouse
Private strIETMPath As String
Private strIETMName As String
Private strIETMMan As String
Private strIETMIcon As String
Private strPageToLaunch As String
#End Region
#Region "Load Sub Routines"
' Form Load
Private Sub FContent_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Text = strIETMName
Me.Icon = New System.Drawing.Icon(strIETMIcon)
StartNavigation(strPageToLaunch)
End Sub
' Just pass in the file you want to view
Public Sub StartNavigation(strFileToNavigate As String)
StartNavigation(strFileToNavigate, True)
End Sub
' Just pass in the file you want to view ( if a manual change it will load TOCs also )
Public Sub StartNavigation(strFileToNavigate As String, blnManual As Boolean)
If blnManual Then
wbContent.Navigate(New Uri(strIETMPath & strFileToNavigate))
wbTOC.Navigate(New Uri(strIETMPath & strIETMMan & "\toc.html"))
wbLOF.Navigate(New Uri(strIETMPath & strIETMMan & "\lof.html"))
wbLOT.Navigate(New Uri(strIETMPath & strIETMMan & "\lot.html"))
wbLOC.Navigate(New Uri(strIETMPath & strIETMMan & "\loc.html"))
Else
wbContent.Navigate(New Uri(strIETMPath & strFileToNavigate))
End If
End Sub
#End Region
#Region "Set Sub Routines"
' Set IETM Path
Public Sub SetIETMPath(strNewIETM As String)
strIETMPath = strNewIETM
End Sub
' Set IETM Name
Public Sub SetIETMName(strNewIETM As String)
strIETMName = strNewIETM
End Sub
' Set IETM Manual
Public Sub SetIETMMan(strNewIETM As String)
strIETMMan = strNewIETM.Substring(0, strNewIETM.IndexOf("/"))
End Sub
' Set IETM Icon
Public Sub SetIETMIcon(strNewIETM As String)
strIETMIcon = strNewIETM
End Sub
' Set Page To Launch
Public Sub SetPageToLaunch(strNewPage As String)
strPageToLaunch = strNewPage
End Sub
#End Region
The easiest way to ensure the display above the calling form is to set the Owner property of the called form to the instance of the calling form.
So, supposing that this OpenContentWindow method is inside the class code of the form that want to create the instance of an FContent you could call the Show method passing the reference to the current form instance
Private Sub OpenContentWindow(strNewNavigation As String)
Dim newContent As New FContent
newContent.SetIETMPath(strIETMPath)
newContent.SetIETMName(strIETMName)
newContent.SetIETMMan(strNewNavigation)
newContent.SetIETMIcon(strIETMIcon)
newContent.SetPageToLaunch(strNewNavigation)
newContent.Show(Me)
End Sub
In the link above (MSDN) you could read
When a form is owned by another form, it is closed or hidden with the
owner form. For example, consider a form named Form2 that is owned by
a form named Form1. If Form1 is closed or minimized, Form2 is also
closed or hidden. Owned forms are also never displayed behind their
owner form. You can use owned forms for windows such as find and
replace windows, which should not disappear when the owner form is
selected. To determine the forms that are owned by a parent form, use
the OwnedForms property.
Did you try "newContent.BringToFront()" after newContent.Show () or newContent.TopMost =true ?

VB.NET form.show() error

Every time when i use :
form1.show()
I get Reference to a non-shared member requires an object reference.
It worked till now.I don't know what's the problem.
Also,it does not even show in "Startup form" dropdown menu.
Edit : Included whole code.
Private _cpuid As String
///Here is the generated constructor
Sub New()
' TODO: Complete member initialization
End Sub
Public ReadOnly Property cpuid As String
Get
Return _cpuid
End Get
End Property
Private _pc As PerformanceCounter
Private _currentvalue As Integer = 0
Public Sub New(ByVal cpuid As String)
InitializeComponent()
_cpuid = cpuid
_pc = New PerformanceCounter("Processes", "CPU Usage (%)", cpuid)
Me.ProgressBar1.Maximum = 100
Me.ProgressBar1.Minimum = 0
Me.Label1.Text = "CPU" & cpuid
End Sub
Public Sub callperformancecounter()
_currentvalue = CInt(_pc.NextValue())
Me.ProgressBar1.Value = _currentvalue
Me.label2.text = _currentvalue & " %"
End Sub
assuming a form named form1 in the proj you need to create an instance of it:
Dim frm as New Form1 ' creates the instance the msg is talking about
frm.Show
EDIT for new info...
You have overridden the constructor, then not used it. I would not do it that way, do the CPU setup stuff in the Form Load event (just move the code). Fix your Sub New to this:
Sub New(cpuID As String)
' TODO: Complete member initialization
InitializeComponent() ' the TODO is telling you this is needed
_cpuID = cpuID
End Sub
the form load would be the rest of your code:
_pc = New PerformanceCounter("Processes", "CPU Usage (%)", cpuid)
Me.ProgressBar1.Maximum = 100
Me.ProgressBar1.Minimum = 0
Me.Label1.Text = "CPU" & cpuid
You dont need to pass the cpuid to the procedure if you pass it to New or set the Property (you dont really need both methods for what you have so far).
NOW, the way you want to show the form is:
Dim frm as Form1 ' declare what frm is
frm = New Form1(cpuname) ' this 'NEW' triggers 'Sub New'
frm.Show