Common keypresses in multiple project solution - vb.net

I have multiple project solution and need some service keystrokes to be common for all projects of that solution.
For example if I press Ctrl+Alt+Right Shift anywhere and any time in any of included project that "something" happens actually that I can detect that keystroke immediately.
For all those projects I have common code in additional project which is included in all other projects as reference to "common.dll" so that may be right place to put this code.
Any idea on how to make that task and how that code should look like?
I am thinking on detecting those keystrokes at PreFilter Message but I can't make that without help.
Maybe is different kind of solution better that one I think?

Windows Forms raises keyboard events for you so you do not need to go as low level as handling window messages yourself. To standardise the approach across multiple forms and projects, create a common DLL project and add a reference to it in all of the other projects. Create a static class (module in VB) in the common project along the lines of the code below.
Imports System.Windows.Forms
Public Module KeyPressHandler
''' <summary>
''' Connects the <see cref="Form.KeyDown"/> event handler for a specified Windows form
''' </summary>
Public Sub ConnectKeyHandler(form As Form)
AddHandler form.KeyDown, New KeyEventHandler(AddressOf KeyPressHandler.KeyDownHandler)
End Sub
''' <summary>
''' Handles the KeyDown event for a windows form
''' </summary>
Private Sub KeyDownHandler(sender As Object, e As KeyEventArgs)
If (e.KeyData.HasFlag(Keys.Control) AndAlso e.KeyData.HasFlag(Keys.Alt) AndAlso e.KeyData.HasFlag(Keys.Shift)) Then
'' Do whatever you want here
End If
End Sub
End Module
Then in the code-behind of each form, in the constructor, add the following line of code:
Common.KeyPressHandler.ConnectKeyHandler(Me)
(so it will end up looking like this):
Public Class Form1
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Common.KeyPressHandler.ConnectKeyHandler(Me)
End Sub
End Class
The HasFlag method on enums was added in .NET 4, so if you are using a version before that then the logic will be a little more long winded:
If ((e.KeyData And Keys.Control = Keys.Control) AndAlso (e.KeyData And Keys.Alt = Keys.Alt) AndAlso (e.KeyData And Keys.Shift = Keys.Shift)) Then
There is still a problem with specifically detecting the right shift key as opposed to any shift key. I couldn't find a working way to differentiate it from the left shift key, although it looked like using RShift was supposed to do it. One solution might be to call the Windows API function GetKeyState (see http://www.pinvoke.net/default.aspx/user32.getkeystate) with the VK_RSHIFT constant. The historical reason for this is probably to do with the Left and Right shift key distinction being a later addition to Windows than undifferentiated shift keys.

You should create one 'controller' project, which has a keyboard hook, and , maybe a fancy tray icon with animation :)
And from that 'central' project, pass the commands to the other forms...

Related

Why am I getting an 'import statement unnecessary'

I am working in Visual Studio creating a Windows Form Application in Visual Studio. I am very new to the language, and am doing a very simple app. I currently have 3 different forms created on one project. I created a button that switches from the first form to the second form. I am not trying to create a button that switches from the second form to the third form, but cannot import the third form into the code of the second project without getting an unnecessary import error.
This is the code for the first form to the second form and this button works
Imports random__Fun_Page_2
Public Class frmMainPage
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Dim newForm = New frmChoiceOne()
newForm.Show()
Me.Visible = False
End Sub
This is the code from the second form to the third form which gives me the error 'unnecessary import error'
Imports random__Fun_Page_3
Public Class frmChoiceOne
Private Sub radioOneChoiceOne_CheckedChanged(sender As Object, e As EventArgs) Handles radioOneChoiceOne.CheckedChanged
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim newForm2 = New frmChoiceTwo
newForm2.show()
Me.Visible = False
End Sub
End Class
So my question is why can I import the second form on the first project, but cannot import the third form on the second project. Sorry if this does not give clear detail, but if there is any other information I can provide please let me know
As noted, it may well be just a suggestion.
But then again, as a normal course of action one will VERY rare if at all need to use imports for the ability to reference say some other form? That "practice" of using imports for forms is really never required here. It not clear what example or why imports for form names are being used here?
So a general rule, imports are to allow with greater ease to reference or use some .net assembly or library code (by what we call a namespace). As an typical development approach, one does not need nor as a regular development approach do a imports of forms in applications.
So if we have say Form AA and we want to launch form B?
Your code can do this on the click event:
BB.Show()
Me.Hide()
Note that you can (and should) use .show() and .Hide() in palce of .visible.
Now in our form BB, to close the form and return? We could/would use this:
AA.Show()
Me.Close()
Notice how I CLOSED form BB. Note how I did NOT close form AA, since my whole application and startup form was form AA. (if I close form AA, then my whole application shuts down if that is the first "base" form you specify in the project to load on startup. So I did want to close form BB (not just hide it), and then return to form AA.
And in say form BB, there is nothing that prevents you from getting/grabbing controls or values from form AA (or say form CC or whatever).
So no need for "importing" the other
In form BB, you can get/grab/use controls from from AA like this:
Me.TextBox1.Text = AA.TextBox1.Text
So you have full use of any of the loaded forms as per above, and no imports of the forms is or should be required.
So, you would never "import say form AA or CC into that form BB. You have full use of any loaded form and its values etc. in code.
Perhaps you goal is some type of sub-form here? (we don't have that option in vb.net).
But the "general" idea and concept is to not use imports for use of other forms.
If you need some "general" code module for some subs to call (and code not really needed to be part of a form? Then under project, you can add a code module, and place the general subs and functions that are not part of say a form. But once again, no imports should be required to do this.
I will also point out in above, I did not create a "instance" of the form before I used it. Over time one can adopt creating a form instance, but in your general code you can just use the "one line" BB.Show() and the form will load for you - not really a need to "create" the form instance before you use (show) it.

VB.NET 2019 Open a form from another form

I know this appears to be a rather common topic and should have been resolved from earlier posts. But what I am experiencing still does not seem to have a solution online:
I have a form called ExpenseEntry in which there is a sub procedure called Public Sub OpenVoucher.
I want to call this sub from another form for which I use the following code:
Dim ExpForm As New ExpenseEntry
ExpForm.Show()
ExpForm.OpenVoucher()
While this works well enough, the problem is everytime I click the button, a new window of ExpenseEntry is launched. As per how I have designed the application, repeat windows is not permissible and only one window should be available at a time.
I have tried various methods to restrict more than one form such as by using a variable to control the form but that gives rise to other issues.
If I use Application.OpenForms but still does not resolve the issue.
I have earlier queried in this regard in the following link:
Textbox not refreshing
I am using VB.NET 2019 which does not allow the launch of default instance of a form like Form.Show. I know this is bad practice but it was easier to manage with that till VB.NET 2017.
Now by creating a form variable and launching that creates an infinite loop where I cannot have just one instance of a form running on a single thread.
The really simple way to handle this is to use the default instance of the form type. In VB, since 2005, each form type has a default instance that you can access via the type name. Read here for more info. In your case, you can do this:
'Display the form if it is not already displayed.
ExpenseEntry.Show()
'Activate the form if it is already displayed.
ExpenseEntry.Activate()
'Do the deed.
ExpenseEntry.OpenVouncher()
That said, default instances are a bit dodgy. They do enable beginners to access forms from anywhere in their project under certain circumstances but they also have limitations that can cause issues. Most importantly though, they help to prevent you learning proper OOP by treating forms differently to other types. If you want to do this the way a proper developer would then simply declare a variable to refer to the current instance of the form:
Private expenseEntryDialogue As ExpenseEntry
When it's time to use the form, you simply check whether that variable refers to a usable instance and use it if it does, otherwise create a new one:
If expenseEntryDialogue Is Nothing OrElse expenseEntryDialogue.IsDisposed Then
expenseEntryDialogue = New ExpenseEntry
End If
expenseEntryDialogue.Show()
expenseEntryDialogue.Activate()
expenseEntryDialogue.OpenVoucher()
A third option would be to implement your own singleton, i.e. a type that can only ever have a single instance. You probably wouldn't do that in VB, given that the default instance is basically a thread-specific singleton and does more automatically but, if you wanted to, you could do this:
Public Class ExpenseEntry
Private Shared _instance As ExpenseEntry
'The one and only instance of the type.
Public Shared ReadOnly Property Instance As ExpenseEntry
Get
If _instance Is Nothing OrElse _instance.IsDisposed Then
_instance = New ExpenseEntry
End If
Return _instance
End Get
End Property
'The constructor is private to prevent external instantiation.
Private Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
End Class
and then this:
ExpenseEntry.Instance.Show()
ExpenseEntry.Instance.Activate()
ExpenseEntry.Instance.OpenVoucher()

Label Text not updating even when using Refresh, Invalidate, and DoEvents

My code is designed to be a control system for a 2-axis motion system. I have 2 drives that each output a count of their steps. I can read the device, update a property, and update the text field of a label. However, it does not update the form. When I use a message box, I can display the text value being correct, but nothing updates the label.
I'm happy to try any suggestions, but I've been fighting this for about 16 hours and I'm at my wits end - as evidenced by the clear overkill/terrible coding that is shown in the code. I can't understand why it's not updating.
Additionally, a manual button with all versions seen below to refresh a form doesn't update the control.
Direction, recommendations?
Private Sub PositionChanged(ByVal sender As Object, ByVal e As EventArgs)
If TraverseController.InvokeRequired Then
TraverseController.Invoke(
New EventHandler(Of EventArgs)(AddressOf PositionChanged), sender, e)
Return
End If
'RaiseEvent PropertyChanged(TraverseController, New System.ComponentModel.PropertyChangedEventArgs("Position"))
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
TraverseController.lblLinearDrivePosDisp.Text = CStr(_position)
Application.DoEvents()
TraverseController.lblLinearDrivePosDisp.ResetBindings()
TraverseController.GBDrivePositionDisp.Refresh()
TraverseController.lblLinearDrivePosDisp.Refresh()
TraverseController.Refresh()
TraverseController.Invalidate()
TraverseController.Update()
Application.DoEvents()
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
End Sub
Assumption: TraverseController is form's class name.
This looks like a VB default form instance issue. It is apparent that you are trying to properly marshal control interaction back to the UI thread by using checking TraverseController.InvokeRequired. However, due to the way these default instance are created, TraverseController.InvokeRequired is creating a new instance of TraverseController on the secondary thread and all subsequent code is modifying that instance and not the one created on the UI thread.
One way to deal with this is to pass a synchronizing control instance to the class where PositionChanged changed method is defined and check that control's InvokeRequired method instead of TraverseController.InvokeRequired. If the containing class is itself a UI control, then use that class instance (Me.InvokeRequired).

vb.net control's events only when user clicks [duplicate]

Consider a simple .NET form with a couple of radio buttons and a checkbox.
Each of the radio buttons has a CheckedChanged handler setup that performs some action based on the state of the checkbox.
My problem is, when I initialize on the default radiobutton to be checked (from the designer properties window) the CheckedChanged event is fired for that radio button, but the Checkbox hasn't been initialized yet so I either get a null pointer exception or the wrong value is used in the handler. Either way, I don't want that handler code to be run unless the user picks a radio button after the form has been loaded.
I currently get around this by not initializing the radio button, but I need to set that default eventually and the best place is from the designer. I also can add a boolean field that's not set to true until the form is fully loaded and not process the events if that is false, but it's a dirty hack.
What can I do to prevent that handler from running its code?
To make it feel slightly less dirty, if you initialize the controls in the constructor of the form you might be able to use the forms IsHandleCreated property rather than your own bool to check if it should actually validate or not.
I would think that normally you wouldn't want to validate anything before it's been shown for the first time and handle isn't created until it is.
Code Example:
Private Sub myRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles myRadioButton.CheckedChanged
If myRadioButton.Checked AndAlso myRadioButton.IsHandleCreated Then
'Do Work
End If
End Sub
"I also can put a boolean field that's not set to true until the form is fully loaded and not process the events if that is false, but it's a dirty hack."
It's also the easist and best way to do it!
Lets say .NET provides a neat way to turn an and off all the event handlers until the form is loaded. Even just the ones YOU are handling. It would still not be sufficiently flexible to disable what you wanted to enable but disable what you didn't. Often form setups happen and you want the events to fire. Also the form won't build right if no events fire.
The easy solution is to declare an initializing variable:
Private Initializing as boolean = True
Private Sub rb_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rbNuevos.CheckedChanged, RbDesaparecidos.CheckedChanged, RbModificados.CheckedChanged, RbNoDesap.CheckedChanged, RbDesHoy.CheckedChanged, RbChT.CheckedChanged
if Initializing then return
'Your Code
End Sub
Public Sub New()
' Llamada necesaria para el Diseñador de Windows Forms.
InitializeComponent()
' Agregue cualquier inicialización después de la llamada a InitializeComponent().
initializing = false
end sub
Most sophisticated: Remove the "handles" from the method, and use AddHandler on the new method.
Public Sub New()
' Llamada necesaria para el Diseñador de Windows Forms.
InitializeComponent()
' Agregue cualquier inicialización después de la llamada a InitializeComponent().
AddHandler RbChT.CheckedChanged, AddressOf rb_CheckedChanged
end sub
For radiobutton see Hans Olsson answer
For numeric up down, do it like this
Private Sub myNumeric_ValueChanged(sender As Object, e As EventArgs) Handles myNumeric.ValueChanged
If myNumeric.Value >= 0 AndAlso myNumeric.IsHandleCreated Then
'Do the work
End If
End Sub
The keyword is myNumeric.Value and IsHandleCreated
Yet another way:
Private Sub dgvGroups_CellValueChanged(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvGroups.CellValueChanged
If Me.Visible = False Then Exit Sub ' Sub gets called on form load which causes problems
wksGroups.Cells(e.RowIndex + 1, 1) = dgvGroups.Item(e.ColumnIndex, e.RowIndex).Value
wksGroups.Cells(1, 5) = dgvGroups.RowCount
One thing I've found that works is adding the events manually after you load the form.
To do this you can simply go into the generated form code found in the designer file of that form, and pull out the lines that add the event. It would look something like this:
this.controlName.CheckedChanged += new System.EventHandler(this.controlName_CheckedChanged);
Then put all of these calls into a method that you call after the InitializeComponent call in your form's constructor.
Just in case anyone is still searching for this the event is fired upon initializing the form BUT the form is not yet visible, Also Say that you have a foreign key relationship upon which you have a default value needed issue that gets fired every row update too. So the following code worked for me....
if (Visible && !(e.ColumnIndex == 0))
{
phoneEdited = true;
MessageBox.Show("A Phone entry has been entered");
}
Don't set checked on a control that really does much in designer.
The global flag and conditional exits where needed.
Try..Catch the sore spots to ignore a meaningless exception.
(Using VS 2017) It appears to me that it is an annoyance but not a bug. It is consistent with the model in use. The event is fired by normal operation of code, but code I did not write (but can access where fools fear to tread) and where there appears to be no (decent) place earlier in the normal flow to anticipate it.
The cleanest answer seems to be not to check radio button or checkbox controls in the designer at all if they trigger any significant code. Instead these controls should be changed by code (e.g. checked = true) in the Load event (for example) AFTER all the initialization is done.
There is no loss of flexibility here since both are fixed before the build, only in different places. The event handlers will handle it exactly as if a user had clicked the control in the natural flow of a well designed GUI application. (This reminds me of the ancient RPG proverb "Don't buck the cycle". (Anyone here remember RPG? I, not part of IBM-oriented team, never used it but had interesting discussions with some who did. ) Pre-checking controls hits the wrong part of the VS cycle.)
If for any reason that will not work, the next best thing is the kludge suggested elsewhere of a single status boolean initialized false and set true at the appropriate time with conditional exits in the necessary places to prevent them from crashing until then. It will get the job done, but it's ugly. Better than failure.
Another thing I tried before I decided that designer level pre-set checks were the problem and there was a very acceptable alternative was to put the danger spots in a Try..Catch to be able to ignore the exception. Also a kludge.
For the cleanest code, reverse the True/False approach used in some other examples. Focus on 'ready' rather than 'busy'. Here's an example for a Windows Form:
At the Class level, add Private app_ready As Boolean (it will be False by default).
At the end of the Form.Shown event handler, add app_ready = True.
In each control event handler where it's needed, add:
If app_ready Then
' code
End If
Starting a routine with something like If initialising Then Exit Sub just doesn't feel right!
Maybe for some functionality you can use the click event instead of the check changed event.
I put a public variable in the Module1 file
Dim Public bolForm_LoadingTF as Boolean = True
In each formLoad event I put
bolForm_LoadingTF = True
In each control with an OnSelectedIndexChanged
event I put if bolForm_LoadingTF = True then Exit Sub
At the end of the form load event I put
bolForm_LoadingTF = False
I am probably breaking a bunch of rules but this works
for me.

Configure App.config Application Settings During MSI Install vb.net

I am trying to create an msi install for my windows service. The reason for creating an msi is that the intended users want to be able to quickly install the service with as-little intervention as possible.
i can get the service to install as a msi but i have a variable within my code that i need the user to define when the msi is being installed. the variable i require from the user is the file path which they want the xml files my service creates to be located.
i thought i could configure the app.config application settings to contain the file path that the xml files should be written to. However i'm struggling to do this and im not to sure if its the best way to do it?
I have my setup project that contains my executable and has my textbox which will contain the one variable from the user.
I have an installer class which contains my serviceinstaller and process installer. This is where im struggling to understand what i need to do next.
Do i need to override the install method? The current code of my installer class was automatically generated and is as follows:
Imports System.Configuration.Install
Imports System.Configuration
<System.ComponentModel.RunInstaller(True)> Partial Class ProjectInstaller
Inherits System.Configuration.Install.Installer
'Installer overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
'Required by the Component Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Component Designer
'It can be modified using the Component Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.ServiceProcessInstaller1 = New System.ServiceProcess.ServiceProcessInstaller()
Me.ServiceInstaller1 = New System.ServiceProcess.ServiceInstaller()
'
'ServiceProcessInstaller1
'
Me.ServiceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem
Me.ServiceProcessInstaller1.Password = Nothing
Me.ServiceProcessInstaller1.Username = Nothing
'
'ServiceInstaller1
'
Me.ServiceInstaller1.ServiceName = "Spotter"
Me.ServiceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic
'
'ProjectInstaller
'
Me.Installers.AddRange(New System.Configuration.Install.Installer() {Me.ServiceProcessInstaller1, Me.ServiceInstaller1})
End Sub
Friend WithEvents ServiceProcessInstaller1 As System.ServiceProcess.ServiceProcessInstaller
Friend WithEvents ServiceInstaller1 As System.ServiceProcess.ServiceInstaller
End Class
I can even add the CustomActionData values. The string determines what gets passed into the context object that i used to collect the user value that is entered. param1 being my variable name.
I'm pretty much struggling with the installer code...i think?
One option to consider is using a third party installer, such as Installshield, that has builtin support for modification of xml configuration files, such as config files.
However, if you want to roll your own, you definitely need to override the Install method. Any parameters that you pass in CustomData will be available in the dictionary that is passed as a parameter to this method.
For example:
Public Overrides Sub Install(ByVal stateSaver As System.Collections.IDictionary)
MyBase.Install(stateSaver)
If Me.Context.Parameters.Count <> 0 Then
For Each sKey As String In Context.Parameters.Keys
Select Case sKey.ToUpper
Case "PARAM1"
' XML directory
Me.XMLDir = Context.Parameters(sKey)
End Select
Next
End If
End Sub
In cases like this, we always write the value to the registry so that the user doesn't have to re-enter it if they uninstall or reinstall.
I am not sure of the exact sequence of events for modifying the app.config, but you could write to the registry, then modify app.config when the service is first started.
You will probably also find that you need to remove the custom action from the Commit phase in order for your installer to work successfully.