vb.net gamepad support, partially working - vb.net

I found the following class code on a forum. It works great for the gamepad (up, down, left, right) however all the code for the buttons is missing. Can anyone fill in the blanks?
This works:
Private Sub joystick1_Up() Handles joystick1.Up
moveUp()
End Sub
This does not:
Private Sub joystick1_buttonPressed() Handles joystick1.buttonPressed
MsgBox(joystick1.btnValue)
End Sub
because there is no "buttonPressed" event and I have no idea how to write it.
And here's the class:
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Public Class joystick
Inherits NativeWindow
Private parent As Form
Private Const MM_JOY1MOVE As Integer = &H3A0
' Public Event Move(ByVal joystickPosition As Point)
Public btnValue As String
Public Event Up()
Public Event Down()
Public Event Left()
Public Event Right()
<StructLayout(LayoutKind.Explicit)> _
Private Structure JoyPosition
<FieldOffset(0)> _
Public Raw As IntPtr
<FieldOffset(0)> _
Public XPos As UShort
<FieldOffset(2)> _
Public YPos As UShort
End Structure
Private Class NativeMethods
Private Sub New()
End Sub
' This is a "Stub" function - it has no code in its body.
' There is a similarly named function inside a dll that comes with windows called
' winmm.dll.
' The .Net framework will route calls to this function, through to the dll file.
<DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joySetCapture", SetLastError:=True)> _
Public Shared Function JoySetCapture(ByVal hwnd As IntPtr, ByVal uJoyID As Integer, ByVal uPeriod As Integer, <MarshalAs(UnmanagedType.Bool)> ByVal changed As Boolean) As Integer
End Function
End Class
Public Sub New(ByVal parent As Form, ByVal joyId As Integer)
AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
AssignHandle(parent.Handle)
Me.parent = parent
Dim result As Integer = NativeMethods.JoySetCapture(Me.Handle, joyId, 100, True)
End Sub
Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
AssignHandle(DirectCast(sender, Form).Handle)
End Sub
Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
ReleaseHandle()
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = MM_JOY1MOVE Then
' Joystick co-ords.
' (0,0) (32768,0) (65535, 0)
'
'
'
' (0, 32768) (32768, 32768) (65535, 32768)
'
'
'
'
' (0, 65535) (32768, 65535) (65535, 65535)
'
Dim p As JoyPosition
p.Raw = m.LParam
' RaiseEvent Move(New Point(p.XPos, p.YPos))
If p.XPos > 16384 AndAlso p.XPos < 49152 Then
' X is near the centre line.
If p.YPos < 6000 Then
' Y is near the top.
RaiseEvent Up()
ElseIf p.YPos > 59536 Then
' Y is near the bottom.
RaiseEvent Down()
End If
Else
If p.YPos > 16384 AndAlso p.YPos < 49152 Then
' Y is near the centre line
If p.XPos < 6000 Then
' X is near the left.
RaiseEvent Left()
ElseIf p.XPos > 59536 Then
' X is near the right
RaiseEvent Right()
End If
End If
End If
End If
If btnValue <> m.WParam.ToString Then
btnValue = m.WParam.ToString
End If
MyBase.WndProc(m)
End Sub
End Class

Instead of using the older winmm, I would use XInput instead (or if you can, use XNA).
There are a couple of ways you could go to do this. One step up is to use the XInput dlls directly as outlined by this question on the MSDN forums. That's still fairly ugly though. Probably the "easier" way to do this is using a wrapper library that exists out there like SlimDX or SharpDX.
One of the advantages of using XInput via SlimDX or SharpDX is that it will also work within a Windows Store app for Windows 8 :).
Here's a snippet from the GamePad sample in SharpDX:
C#
var controllers = new[] { new Controller(UserIndex.One), new Controller(UserIndex.Two), new Controller(UserIndex.Three), new Controller(UserIndex.Four) };
// Get 1st controller available
Controller controller = null;
foreach (var selectControler in controllers)
{
if (selectControler.IsConnected)
{
controller = selectControler;
break;
}
}
VB
Dim controllers As New List(Of Controller)
controllers.Add(New Controller(UserIndex.One))
controllers.Add(New Controller(UserIndex.Two))
controllers.Add(New Controller(UserIndex.Three))
controllers.Add(New Controller(UserIndex.Four))
Dim controller as Controller = Nothing;
For Each selectController In controllers
If selectController.IsConnected Then
controller = selectController
Exit For
End If
Next
Then you can get the state to work with using:
var state = controller.GetState();
You will notice that XInput uses more of a polling model, so you will need to occasionally check for a button press to detect this. If you need to poll continuously, you can probably spin up a new Task to do this on.

Related

CoreAudio in vb.net 6.0 Windows 11 : Is there a way to do a ControlChangeNotify callback?

I am writing a volume control app in vb.net 6.0, using a reference to CoreAudioApi.dll.
I can:
Change the volume of the default render and capture devices.
Mute the default devices.
Alter the balance on the render device
List the full names, guid string and status of all the devices
However, despite a lot of research, I can not setup a callback for ControlChangeNotify.
It appears to be simple in C, however I am writing in VB. Can any one suggest a solution?
Update 18/12/2022 #Jimi supplied some very useful definitions and I have edited the post to show the current code attempts. I can get the master default volume using masterVol = GetMasterVolumeObject() as an IAudioEndpointVolume.
Update 22/12/2022 Following a re-read of many postings it became apparent that it was important to create a persistent reference to mastervol and the class reference (MyCallBack) to the class module implementing IAudioEndpointVolumeCallback.
Update 24/12/2022 The AudioCallback class is now firing when volume/mute are changed internally and externally. Transferring the callback data to the main form is problematic. I now have a working system which involves:
Declaring public events in the call back class for each type of control I want to change in the main form.
Initiating a BackgroundWorker to Process the data 10mS after being called by the callback class.
Calculating new audio values from the pNotifyData pointer.
RaiseEvent for each slider/checkbox and label in the main form that needs updating.
AddHandler in the main form to process the callback events.
Create handler routines with delegates for each control type.
From my point of view this topic is solved.
My thanks to #Jimi for his contribution.
Latest Updates:
Option Explicit On
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Public Class AudioCallback
Implements IAudioEndpointVolumeCallback
Public Event LabelReady(sender As AudioCallback, ByVal T As String)
Public Event SliderReady(sender As AudioCallback, EP As String, ByVal T As Integer)
Public Event CheckReady(sender As AudioCallback, EP As String, ByVal T As Boolean)
Public Function OnNotify(pNotifyData As IntPtr) As Integer Implements IAudioEndpointVolumeCallback.OnNotify
' Move to global structure
Gstructure = Marshal.PtrToStructure(pNotifyData, GetType(AUDIO_VOLUME_NOTIFICATION_DATA))
BackGround() ' Asynchronous call and delagate controls
Return 0
End Function
Private Sub BackGround()
Dim bgw = New BackgroundWorker()
AddHandler bgw.DoWork,
Sub()
System.Threading.Thread.Sleep(10)
End Sub
AddHandler bgw.RunWorkerCompleted,
Sub()
Update()
End Sub
bgw.RunWorkerAsync()
End Sub
Private Sub Update()
Dim svolL As Integer
Dim svolR As Integer
Dim balance As Integer
'ChkMaster.Checked = Gstructure.bMuted
RaiseEvent CheckReady(Me, "R", Gstructure.bMuted)
svolL = 0.5 + 100 * Gstructure.Left
svolR = 0.5 + 100 * Gstructure.Right
If svolL = svolR Then
balance = 0
Else
If svolR > svolL Then
balance = 100 - svolL * 100.0 / svolR
Else
balance = -(100 - svolR * 100.0 / svolL)
End If
End If
RaiseEvent SliderReady(Me, "RV", 0.5 + 100 * Gstructure.fMasterVolume) ' HMaster.Value = 0.5 + 100 * Gstructure.fMasterVolume
RaiseEvent SliderReady(Me, "RB", balance) ' HBalance.Value = balance
'LGuid.Text = Gstructure.guidEventContext.ToString
RaiseEvent LabelReady(Me, Gstructure.guidEventContext.ToString)
End Sub
End Class
' Main form Extracts ============================================
Imports CoreAudioApi
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Show()
' events for handling AudioCallback events
AddHandler MyCallBack.LabelReady, AddressOf LReady
AddHandler MyCallBack.SliderReady, AddressOf HReady
AddHandler MyCallBack.CheckReady, AddressOf CReady
End Sub
' Label Guid displays Callback GUID
' CheckBox ChkMaster displays Mute status
' Horizontal scrollbar HMaster displays default render volume
' Horizontal scrollbar HBalance displays default render balance
' The global variable IsSettingMaster prevents code associated
' with altering the value of the sliders from re-setting
' the volume / balance.
Private Sub LReady(sender As AudioCallback, Data As String) ' Set GUID label for Render
UpdateLabel(LGuid, Data)
End Sub
Private Delegate Sub UpdateLabelDelegate(TB As Label, param As String)
Private Sub UpdateLabel(TB As Label, param As String)
If TB.InvokeRequired Then
TB.Invoke(New UpdateLabelDelegate(AddressOf UpdateLabel), New Object() {TB, param})
Else
TB.Text = param
End If
End Sub
Private Sub CReady(sender As AudioCallback, EP As String, Data As Boolean) ' Set Master (Render) Mute checkbox
If EP = "R" Then UpdateChkbox(ChkMaster, Data)
End Sub
Private Delegate Sub UpdateChkboxDelegate(TB As CheckBox, param As Boolean)
Private Sub UpdateChkbox(TB As CheckBox, param As Boolean)
If TB.InvokeRequired Then
TB.Invoke(New UpdateChkboxDelegate(AddressOf UpdateChkbox), New Object() {TB, param})
Else
TB.Checked = param
End If
End Sub
Private Sub HReady(sender As AudioCallback, EP As String, Data As Integer) ' Set Master (Render) Volume / Master Balance slide
IsSettingMaster = True
If EP = "RV" Then UpdateHslide(HMaster, Data)
If EP = "RB" Then UpdateHslide(HBalance, Data)
IsSettingMaster = False
End Sub
Private Delegate Sub UpdateHslideDelegate(TB As HScrollBar, param As Integer)
Private Sub UpdateHslide(TB As HScrollBar, param As Integer)
If TB.InvokeRequired Then
TB.Invoke(New UpdateHslideDelegate(AddressOf UpdateHslide), New Object() {TB, param})
Else
TB.Value = param
End If
End Sub
History:
' ===============================
' Public Class containing Callback:
Option Explicit On
Imports System.Runtime.InteropServices
Public Class AudioCallback
Implements IAudioEndpointVolumeCallback
Public Function OnNotify(pNotifyData As IntPtr) As Integer Implements IAudioEndpointVolumeCallback.OnNotify
' Move to global structure
Gstructure = Marshal.PtrToStructure(pNotifyData, GetType(AUDIO_VOLUME_NOTIFICATION_DATA))
' Need mechanism to process new data in main form
' Must be asynchronous
' Raise event did not work
' Enabling Timer did not work
HaveChange = True ' Flag global variable
Return 0
End Function
End Class
' ===============================
' Public Class to Setup Callback:
Option Explicit On
Imports System.Runtime.InteropServices
' Implements IMMDevice, IMMDeviceEnumerator, IAudioEndpointVolume
' developed from https://exchangetuts.com/how-to-check-if-the-system-audio-is-muted-1641156904496320
Public Class CoreAudio
' Public definition in ModCoreaudioAlt https://stackoverflow.com/questions/52001368/how-to-check-if-the-system-audio-is-muted/52013031#52013031
' Public Interface IAudioEndpointVolumeCallback
' Public Structure AUDIO_VOLUME_NOTIFICATION_DATA
' Public Interface IMMDevice
' Public Interface IMMDeviceEnumerator
' End Public definition in ModCoreaudioAlt ==============================
Dim CLSID_MMDeviceEnumerator As Guid = New Guid("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
Dim MMDeviceEnumeratorType As Type = Type.GetTypeFromCLSID(CLSID_MMDeviceEnumerator, True)
Private hr As Integer
Friend Enum EDataFlow
eRender
eCapture
eAll
EDataFlow_enum_count
End Enum
Friend Enum ERole
eConsole
eMultimedia
eCommunications
ERole_enum_count
End Enum
<Flags>
Friend Enum CLSCTX As UInteger
CLSCTX_INPROC_SERVER = &H1 ' In CLSCTX_ALL
CLSCTX_INPROC_HANDLER = &H2 ' In CLSCTX_ALL
CLSCTX_LOCAL_SERVER = &H4 ' In CLSCTX_ALL
CLSCTX_INPROC_SERVER16 = &H8
CLSCTX_REMOTE_SERVER = &H10 ' In CLSCTX_ALL
CLSCTX_INPROC_HANDLER16 = &H20
CLSCTX_RESERVED1 = &H40
CLSCTX_RESERVED2 = &H80
CLSCTX_RESERVED3 = &H100
CLSCTX_RESERVED4 = &H200
CLSCTX_NO_CODE_DOWNLOAD = &H400
CLSCTX_RESERVED5 = &H800
CLSCTX_NO_CUSTOM_MARSHAL = &H1000
CLSCTX_ENABLE_CODE_DOWNLOAD = &H2000
CLSCTX_NO_FAILURE_LOG = &H4000
CLSCTX_DISABLE_AAA = &H8000
CLSCTX_ENABLE_AAA = &H10000
CLSCTX_FROM_DEFAULT_CONTEXT = &H20000
CLSCTX_ACTIVATE_32_BIT_SERVER = &H40000
CLSCTX_ACTIVATE_64_BIT_SERVER = &H80000
CLSCTX_INPROC = CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER
CLSCTX_SERVER = CLSCTX_INPROC_SERVER Or CLSCTX_LOCAL_SERVER Or CLSCTX_REMOTE_SERVER ' In CLSCTX_ALL
CLSCTX_ALL = CLSCTX_SERVER Or CLSCTX_INPROC_HANDLER
End Enum
Friend Function GetMasterVolumeObject() As IAudioEndpointVolume
' Get the default IAudioEndpintVolume as "ppEndpoint" for eRender & eMultimedia
Dim deviceEnumerator As IMMDeviceEnumerator = Nothing
Dim MediaDevice As IMMDevice = Nothing
Dim ppEndpoint As IAudioEndpointVolume = Nothing
Dim EndPointVolID As Guid = GetType(IAudioEndpointVolume).GUID
Try
Dim MMDeviceEnumerator As Object = Activator.CreateInstance(MMDeviceEnumeratorType)
deviceEnumerator = CType(MMDeviceEnumerator, IMMDeviceEnumerator)
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, MediaDevice)
MediaDevice.Activate(EndPointVolID, CLSCTX.CLSCTX_ALL, IntPtr.Zero, ppEndpoint)
Catch ex As Exception
Form1.Showme("Error in GetMasterVolumeObject: " & ex.Message, Color.Red)
ppEndpoint = Nothing
Finally
If Not IsNothing(deviceEnumerator) Then Marshal.ReleaseComObject(deviceEnumerator)
If Not IsNothing(MediaDevice) Then Marshal.ReleaseComObject(MediaDevice)
End Try
Return ppEndpoint
End Function
Public Sub Callback()
Try
masterVol = GetMasterVolumeObject()
' MyCallBack defined in Module ModCoreAudioAlt : Public MyCallBack As New AudioCallback
If IsNothing(MyCallBack) Then
Form1.Showme("Failed to set MyCallBack", Color.Red)
Else
hr = masterVol.RegisterControlChangeNotify(MyCallBack)
If hr <> 0 Then
Form1.Showme("Callback register failed", Color.Red)
If Not IsNothing(masterVol) Then Marshal.ReleaseComObject(masterVol)
Else
Form1.Showme("Callback register OK", Color.Blue)
CallBackOn = True
End If
End If
Catch ex As Exception
Form1.Showme("CallBack error " & ex.Message, Color.Red)
If Not IsNothing(masterVol) Then Marshal.ReleaseComObject(masterVol)
End Try
End Sub
Public Sub Cancelcallback()
If CallBackOn = False Then Exit Sub
hr = masterVol.UnregisterControlChangeNotify(MyCallBack)
If hr <> 0 Then
MsgBox("Callback Failed to UnRegister", vbOK, "Core Audio Callback")
Else
If Not IsNothing(masterVol) Then Marshal.ReleaseComObject(masterVol)
Form1.Showme("Callback Un-register OK", Color.Blue)
CallBackOn = False
End If
End Sub
End Class
' ==============
' Public Module:
Imports System.Runtime.InteropServices
Imports CoreAudioApi
Module ModCoreAudioAlt
' https://stackoverflow.com/questions/74833398/coreaudio-in-vb-net-6-0-windows-11-Is-there-a-way-to-do-a-controlchangenotify
Public CallBackOn As Boolean = False ' Callback is on Flag
Public masterVol As IAudioEndpointVolume = Nothing
Public MyCallBack As New AudioCallback
Public Gstructure As New AUDIO_VOLUME_NOTIFICATION_DATA ' Callback data
Public HaveChange As Boolean = False ' Callback has fired
<ComImport>
<Guid("657804FA-D6AD-4496-8A60-352752AF4F89")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IAudioEndpointVolumeCallback
<PreserveSig()>
Function OnNotify(pNotifyData As IntPtr) As Integer
End Interface
<StructLayout(LayoutKind.Sequential)>
Public Structure AUDIO_VOLUME_NOTIFICATION_DATA
Public guidEventContext As Guid
Public bMuted As Boolean
Public fMasterVolume As Single
Public nChannels As UInteger
Public Left As Single ' .net will not allow pre-dimensioned array (aVolumes(1) as single)
Public Right As Single
End Structure
' https://gist.github.com/sverrirs/d099b34b7f72bb4fb386
<ComImport>
<Guid("5CDF2C82-841E-4546-9722-0CF74078229A")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IAudioEndpointVolume
Function RegisterControlChangeNotify(<MarshalAs(UnmanagedType.Interface)> pNotify As IAudioEndpointVolumeCallback) As Integer
Function UnregisterControlChangeNotify(<MarshalAs(UnmanagedType.Interface)> pNotify As IAudioEndpointVolumeCallback) As Integer
Function GetChannelCount(ByRef channelCount As Integer) As HRESULT
Function SetMasterVolumeLevel() As HRESULT
Function SetMasterVolumeLevelScalar(level As Single, eventContext As Guid) As HRESULT
Function GetMasterVolumeLevel(<Out> ByRef level As Single) As HRESULT
Function GetMasterVolumeLevelScalar(<Out> ByRef level As Single) As HRESULT
Function SetChannelVolumeLevel(channelNumber As Integer, level As Single, eventContext As Guid) As HRESULT
Function SetChannelVolumeLevelScalar(channelNumber As Integer, level As Single, eventContext As Guid) As HRESULT
Function GetChannelVolumeLevel(channelNumber As Integer, <Out> ByRef level As Single) As HRESULT
Function GetChannelVolumeLevelScalar(channelNumber As Integer, <Out> ByRef level As Single) As HRESULT
Function SetMute(<MarshalAs(UnmanagedType.Bool)> isMuted As Boolean, eventContext As Guid) As HRESULT
Function GetMute(<Out> ByRef isMuted As Boolean) As HRESULT
Function GetVolumeStepInfo(<Out> ByRef pnStep As Integer, ByRef pnStepCount As Integer) As HRESULT
Function VolumeStepUp(eventContext As Guid) As HRESULT
Function VolumeStepDown(eventContext As Guid) As HRESULT
Function QueryHardwareSupport(<Out> ByRef hardwareSupportMask As Integer) As HRESULT
Function GetVolumeRange(<Out> ByRef volumeMin As Single, <Out> ByRef volumeMax As Single, <Out> ByRef volumeStep As Single) As HRESULT
End Interface
<ComImport>
<Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IMMDeviceEnumerator
Function EnumAudioEndpoints(ByVal dataFlow As EDataFlow, ByVal dwStateMask As Integer, <Out> ByRef ppDevices As IMMDeviceCollection) As HRESULT
' for 0x80070490 : Element not found
<PreserveSig>
Function GetDefaultAudioEndpoint(ByVal dataFlow As EDataFlow, ByVal role As ERole, <Out> ByRef ppEndpoint As IMMDevice) As HRESULT
Function GetDevice(ByVal pwstrId As String, <Out> ByRef ppDevice As IMMDevice) As HRESULT
Function NotImpl1() As Integer
End Interface
<ComImport>
<Guid("D666063F-1587-4E43-81F1-B948E807363F")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IMMDevice
Function Activate(ByRef iid As Guid, ByVal dwClsCtx As CLSCTX, ByVal pActivationParams As IntPtr, <Out> ByRef ppInterface As IAudioEndpointVolume) As HRESULT
Function OpenPropertyStore(ByVal stgmAccess As Integer, <Out> ByRef ppProperties As IPropertyStore) As HRESULT
Function GetId(<Out> ByRef ppstrId As IntPtr) As HRESULT
Function GetState(<Out> ByRef pdwState As Integer) As HRESULT
End Interface
End Module
' ==========
' Main Form:
Public Class Form1
' Contains:
' HMaster horizonal scroll 0-105 for Volume
' HBalance horizontal scroll -100 to 105 for Balance
' ChkMaster CheckBox for Mute
' LGuid label for Callback GUID
' Uses timer1 with 1 second interval to poll for Callback flag
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If HaveChange = False Then Exit Sub
HaveChange = False
ChkMaster.Checked = Gstructure.bMuted
Dim svolL As Integer = 100 * Gstructure.Left
Dim svolR As Integer = 100 * Gstructure.Right
Dim balance As Integer
If svolL = svolR Then
balance = 0
Else
If svolR > svolL Then
balance = 100 - svolL * 100.0 / svolR
Else
balance = -(100 - svolR * 100.0 / svolL)
End If
End If
HMaster.Value = 100 * Gstructure.fMasterVolume
HBalance.Value = balance
LGuid.Text = (Gstructure.guidEventContext.ToString)
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If CallBackOn Then ClassCoreAudio.Cancelcallback()
End Sub
end class

prevent "print screen" button in vb.net

I made a simple windows form application using vb.net, the first element of my project "Form1.vb"contains common code such :
Public Class Form1
Public Sub Form1_Load
.....
End Class
I needed to disable "Print Screen" button in my application and I found the following code using google :
Option Explicit On
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Public Class HotKeyClass
Inherits Control
<DllImport("user32.dll")> _
Private Shared Function RegisterHotKey(hWnd As IntPtr, id As Integer, fsModifiers As Integer, vk As Integer) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function UnregisterHotKey(hWnd As IntPtr, id As Integer) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Sub keybd_event(bVk As Byte, bScan As Byte, dwFlags As UInteger, dwExtraInfo As Integer)
End Sub
Public Event HotKeyPressed(Key As Keys, Modifer As HotKeyModifer)
Private Const KEYEVENTF_KEYUP = &H2
Private Const WM_HOTKEY = &H312
Private m_Modifer As Integer
Private m_Key As Integer
Private m_Id As Integer
'Конструктор
Sub New()
Me.BackColor = Color.Black
Me.Visible = False
End Sub
'Обработка сообщений
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_HOTKEY Then
'Dim idHotKey As Integer = CInt(m.WParam) 'Получаем идентификатор комбинации
RaiseEvent HotKeyPressed(m_Key, m_Modifer)
End If
MyBase.WndProc(m)
End Sub
'Переопределяем, получаем уникальный ID
Public Overrides Function GetHashCode() As Integer
Return m_Modifer ^ m_Key ^ Me.Handle.ToInt32()
End Function
'Переопределяем, снять регистрацию клавиш
Protected Overrides Sub Dispose(disposing As Boolean)
UnregisterHotKey(Me.Handle, Me.GetType().GetHashCode())
MyBase.Dispose(disposing)
End Sub
'Регистрация клавиш
Public Function Register(Key As Keys, Modifer As HotKeyModifer) As Boolean
m_Id = Me.GetHashCode()
m_Modifer = Modifer
m_Key = Key
Return RegisterHotKey(Me.Handle, m_Id, m_Modifer, m_Key)
End Function
'Снять регистрацию клавиш
Public Function Unregiser() As Boolean
Return UnregisterHotKey(Me.Handle, m_Id)
End Function
'Для эмуляции нажатия Ctrl + V
Public Shared Sub EmulateControlV()
keybd_event(Keys.ControlKey, 0, 0, 0)
keybd_event(Keys.V, 0, 0, 0)
keybd_event(Keys.V, 0, KEYEVENTF_KEYUP, 0)
keybd_event(Keys.ControlKey, 0, KEYEVENTF_KEYUP, 0)
End Sub
End Class
<Flags> _
Public Enum HotKeyModifer As UInteger
NO_MODIFICATION = 0
ALT = 1
CONTROL = 2
SHIFT = 4
WIN = 8
End Enum
Now how to use this code to detect "print screen" button press in order to stop the action?
Simply use the following code to achieve your purpose
Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
' Prevent/Disable Print Screen
If e.KeyData = Keys.PrintScreen Then
Clipboard.Clear()
End If
End Sub
I already said this here: https://stackoverflow.com/questions/34238442/how-to-disable-printscreen-functionality-on-32-bit-64-bit-machine/34238923#34238923 but maybe it needs repeating :-)
You can monitor key presses and when the user pushes print screen (key code 44) execute a Clipboard.Clear(). It's pretty pointless though as anything you do in your application doesn't prevent the user from taking a picture of the screen with a mobile phone or using the screen capture software that is built into Windows (snipping tool).

Hiding up/down buttons on NumericUpDown control

I am trying to subclass NumericUpDown in several ways to get better functionality and appearance.
Since NUD is construct of two controls I would like to hide up/down buttons in cases where property "Increment" is set to 0.
This code is in subclass:
Protected Overrides Sub OnTextBoxResize(ByVal source As Object, ByVal e As System.EventArgs)
Controls(0).Hide()
End Sub
... and it work OK.
But in that function I cannot check a value of Increment property like this:
Protected Overrides Sub OnTextBoxResize(ByVal source As Object, ByVal e As System.EventArgs)
If Me.Increment = 0 Then
Controls(0).Hide()
End if
End Sub
In scope of this function Me is not reachable.
I am also try with using local variables but can't find which event is fired before OnTextBoxResize to read value of Increment property.
What to do in such case to get desired functionality?
This seems to work fairly well. It Shadows the Increment property in order to set the visibility of the spinner controls when the Increment value is being changed. There is an underlying private method the base control calls called PositionControls which you cannot stop — that method could create some flicker, but on my test, it didn't.
Public Class MyNumBox
Inherits NumericUpDown
Shadows Property Increment As Decimal
Get
Return MyBase.Increment
End Get
Set(value As Decimal)
MyBase.Increment = value
OnTextBoxResize(Me, EventArgs.Empty)
End Set
End Property
Protected Overrides Sub OnHandleCreated(e As EventArgs)
MyBase.OnHandleCreated(e)
OnTextBoxResize(Me, EventArgs.Empty)
End Sub
Protected Overrides Sub OnTextBoxResize(source As Object, e As EventArgs)
If Me.IsHandleCreated Then
Me.Height = Me.PreferredHeight
Me.Controls(0).Visible = (MyBase.Increment > 0)
Dim borderWidth As Integer = 0
If Me.BorderStyle > BorderStyle.None Then
borderWidth = SystemInformation.Border3DSize.Width
End If
Dim textWidth As Integer
If Me.Increment = 0 Then
textWidth = Me.ClientSize.Width - (borderWidth * 2)
Else
textWidth = Me.ClientSize.Width - Me.Controls(0).Width - (borderWidth * 2)
End If
If Me.UpDownAlign = LeftRightAlignment.Left Then
If Me.Increment = 0 Then
Me.Controls(1).SetBounds(borderWidth, borderWidth, _
textWidth, Me.Controls(1).Height)
Else
Me.Controls(1).SetBounds(borderWidth + Me.Controls(0).Width, _
Me.Controls(1).Top, textWidth, Me.Controls(1).Height)
End If
Else
Me.Controls(1).SetBounds(borderWidth, Me.Controls(1).Top, _
textWidth, Me.Controls(1).Height)
End If
Me.Refresh()
End If
End Sub
End Class
In the OnTextBoxResize override, I am re-positioning the controls into their proper place, and this version does account for the UpDownAlign property.
If you can, read this thread over at EE where I answered a similar question. It resizes the edit portion so that the control is redrawn correctly when the buttons have been hidden and the control is resized. *Otherwise the portion of the control where the buttons used to be leaves ghosts behind.
One solution to your specific problem is to wait for the VisibleChanged() event and check the Increment() property from there. Here is a conversion of my previous answer with some minor changes:
Public Class NoArrowNumericUpDown
Inherits NumericUpDown
Private itb As InnerTextBox = Nothing
Protected Overrides Sub OnVisibleChanged(e As System.EventArgs)
If Me.Visible Then
If Me.Increment = 0 AndAlso IsNothing(itb) Then
Dim ctl As Control = Me.Controls(0) ' get the spinners
Me.Controls.Remove(ctl) ' remove the spinners
ctl = Me.Controls(0) ' get the edit control
itb = New InnerTextBox(Me, ctl)
End If
End If
MyBase.OnVisibleChanged(e)
End Sub
Public Class InnerTextBox
Inherits NativeWindow
Private parentControl As Control = Nothing
Const WM_WINDOWPOSCHANGING As Integer = &H46
Public Sub New(parentControl As Control, InnerTextBox As Control)
Me.parentControl = parentControl
Me.AssignHandle(InnerTextBox.Handle)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case WM_WINDOWPOSCHANGING
Dim wp As WindowPos = CType(System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(WindowPos)), WindowPos)
If Me.parentControl IsNot Nothing Then
wp.cx = Me.parentControl.ClientSize.Width - 2 * wp.x
wp.cy = Me.parentControl.ClientSize.Height
System.Runtime.InteropServices.Marshal.StructureToPtr(wp, m.LParam, True)
End If
Exit Select
End Select
MyBase.WndProc(m)
End Sub
Public Structure WindowPos
Public hwnd As IntPtr
Public hwndInsertAfter As IntPtr
Public x As Integer
Public y As Integer
Public cx As Integer
Public cy As Integer
Public flags As UInteger
End Structure
End Class
End Class
EDIT: You could just enclose your code in a Try/Catch block?
Public Class NoArrowNumericUpDown
Inherits NumericUpDown
Protected Overrides Sub OnTextBoxResize(ByVal source As Object, ByVal e As System.EventArgs)
Try
If Me.Increment = 0 Then
Controls(0).Hide()
End If
Catch ex As Exception
End Try
End Sub
End Class

custom textboxes in VB.Net

tldr - Made a subclass of Textbox, text looks screwy when it has focus. What's the proper way to handle it?
For my company's VB.Net application, I've been asked to make our textboxes behave like Google's textboxes, ie they need to have a blue-ish border around them when they have focus and a gray-ish border when they do not. I can already accomplish this by setting a textbox's BorderStyle to 'None', then drawing the appropriate rectangle within a form's Paint event. However, I have to do this for each and every single textbox that I use. And our application has quite a few of them. Needless to say, this is a pain and I'd rather have one piece of code that I can call upon.
So I figured that I have two options; I can either make a user control that contains a single textbox which uses the above method, or I can write my own class that inherits from the TextBox class and makes this behavior standard. I have elected to use the latter approach, and via overriding the OnPaint method I have achieved the desired behavior. But now I'm encountering some new pitfalls.
The main problem that I'm having is that text within the textbox is not rendered correctly when the textbox has focus. The text takes on a different font, appears bold, and highlighting looks wonky. If the textbox loses focus, the text looks correct. I suspect that I need to handle drawing for highlighted text differently, but I'm not sure what I need to do. Do I handle it in the OnPaint method or do I need to catch it somewhere else? Do I need to abandon this approach altogether and just make a user control?
Bonus question: for anyone with experience making custom textboxes, are there any tips or gotchas that I need to know about? This is my first time making a custom control, so I don't really know what all to expect.
edit: forgot to mention that I'm able to override OnPaint because I set the UserPaint flag to true. I'm guessing this was obvious, but I just want to be thorough.
edit2: Here's the class in its entirety.
Imports System.Drawing
Public Class MyCustomTextBox
Inherits TextBox
Public Sub New()
MyBase.New()
Me.BorderStyle = BorderStyle.None
SetStyle(ControlStyles.UserPaint, True)
End Sub
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
'I want these textboxes to highlight all text by default
Me.SelectAll()
MyBase.OnGotFocus(e)
End Sub
Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
Me.SelectionLength = 0
MyBase.OnLostFocus(e)
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim p As Pen = Nothing
'MyBase.OnPaint(e)
e.Graphics.FillRectangle(Brushes.White, Me.ClientRectangle)
If Me.Focused Then
p = New Pen(Brushes.CornflowerBlue)
Else
p = New Pen(Brushes.Gainsboro)
End If
e.Graphics.DrawRectangle(p, 0, 0, Me.ClientSize.Width - 1, Me.ClientSize.Height - 1)
e.Graphics.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), Me.ClientRectangle)
End Sub
End Class
As Hans mentioned, the TextBox doesn't even use the OnPaint method when it draws its text.
One way to do it is paint over the 3D border of the control in the WM_NCPAINT message. I won't claim it's completely flicker free:
Imports System.Runtime.InteropServices
Public Class TextBoxWithBorder
Inherits TextBox
Public Const WM_NCPAINT As Integer = &H85
<Flags()> _
Private Enum RedrawWindowFlags As UInteger
Invalidate = &H1
InternalPaint = &H2
[Erase] = &H4
Validate = &H8
NoInternalPaint = &H10
NoErase = &H20
NoChildren = &H40
AllChildren = &H80
UpdateNow = &H100
EraseNow = &H200
Frame = &H400
NoFrame = &H800
End Enum
<DllImport("User32.dll")> _
Public Shared Function GetWindowDC(ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function RedrawWindow(hWnd As IntPtr, lprcUpdate As IntPtr, hrgnUpdate As IntPtr, flags As RedrawWindowFlags) As Boolean
End Function
Public Sub New()
MyBase.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
End Sub
Protected Overrides Sub OnResize(e As System.EventArgs)
MyBase.OnResize(e)
RedrawWindow(Me.Handle, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Frame Or RedrawWindowFlags.UpdateNow Or RedrawWindowFlags.Invalidate)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = WM_NCPAINT Then
Dim hDC As IntPtr = GetWindowDC(m.HWnd)
Using g As Graphics = Graphics.FromHdc(hDC)
If Me.Focused Then
g.DrawRectangle(Pens.CornflowerBlue, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
Else
g.DrawRectangle(Pens.Gainsboro, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
End If
g.DrawRectangle(SystemPens.Window, New Rectangle(1, 1, Me.Width - 3, Me.Height - 3))
End Using
ReleaseDC(m.HWnd, hDC)
End If
End Sub
End Class
I override the OnResize event to send the RedrawWindow message, which basically makes the control invalidate it's nonclient area.
Refactor as needed.

How can I catch the autosize double-click event on a listview in VB.NET?

I am using Visual Studio 2008 and VB.NET. I've got a listview control on my form and I've added columns using the windows forms designer. As you know, if you double-click on the sizer or divider or whatever you want to call it between two columns, the column on the left will autosize (unless you disable that). How can I catch this specific event? The ColumnWidthChanged event and the DoubleClick event are likely candidates, but in the ColumnWidthChanged event, there's no way I can see to determine if it was an autosize. Similarly, there's no simple way to catch what was clicked exactly with the DoubleClick event. Does anyone have any ideas how I can catch this specific event type?
Detecting events on a listview's header is quite tricky.
You will need to create your own header to replace the one that it normally uses, and then listen to the appropriate messages. There aren't any specific ones for column resize handles, as far as I know.
The following class subclasses ListView and adds a handler that detects a double-click between columns. That is as close as it gets, I think.
I hope it will help you out somewhat.
Class MyListView
Inherits ListView
Protected Overrides Sub CreateHandle()
MyBase.CreateHandle()
New HeaderControl(Me)
End Sub
Private Class HeaderControl
Inherits NativeWindow
Private _parent As ListView = Nothing
<DllImport("User32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Public Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
Public Sub New(parent As ListView)
_parent = parent
Dim header As IntPtr = SendMessage(parent.Handle, (&H1000 + 31), IntPtr.Zero, IntPtr.Zero)
Me.AssignHandle(header)
End Sub
Protected Overrides Sub WndProc(ByRef message As Message)
Const WM_LBUTTONDBLCLK As Integer = &H203
Select Case message.Msg
Case WM_LBUTTONDBLCLK
Dim position As Point = Control.MousePosition
Dim relative As Point = _parent.PointToClient(position)
Dim rightBorder As Integer = 0
For Each c As ColumnHeader In _parent.Columns
rightBorder += c.Width
If relative.X > (rightBorder - 6) AndAlso relative.X < (rightBorder + 6) Then
MessageBox.Show([String].Format("Double-click after column '{0}'", c.Text))
End If
Next
Exit Select
End Select
MyBase.WndProc(message)
End Sub
End Class
End Class
You will need to include a using System.Runtime.InteropServices; statement for this to work.