Controls Supported: EditBox, Radio, Checkbox, DropDownList, ComboBox, EditBox, ListBox, Push Button
Static Controls (Text and Picture) that have SS_NOTIFY Style set
See the example for usage.
Class_ControlMonitor.ahk
Code: Select all
; =======================================================================================================================
; Class: ControlMonitor
; Monitors controls for changes and triggers a Label or Function when modified.
; Uses WM_COMMAND notifications instead of gLabels
; Supported controls: EditBox, Radio, Checkbox, DropDownList, ComboBox, EditBox, ListBox, Push Button
; Static Controls (Text and Picture) that have SS_NOTIFY Style set
; Tested on: Win 10 (x64)
; Author: RazorHalo
; Change log: 1.0 (August 18, 2020) - Initial release.
;
; =======================================================================================================================
Class ControlMonitor {
; ===================================================================================================================
; Constructor / Destructor
; ===================================================================================================================
__New() {
; Bind the Check() method to WM_COMMAND notifications
this.fn := ObjBindMethod(this, "Check")
OnMessage(0x111, this.fn)
this.Ctrl := {}
this.State := 1
}
; ===================================================================================================================
; Destroy Frees the bound method and unregisters it from OnMessage
; ===================================================================================================================
Destroy() {
OnMessage(0x111, this.fn, 0)
this.fn := ""
}
; ===================================================================================================================
; On Turns on monitoring of controls
; ===================================================================================================================
On() {
this.State := 1
}
; ===================================================================================================================
; Off Turns off monitoring of controls
; ===================================================================================================================
Off() {
this.State := 0
}
; ===================================================================================================================
; Add Adds controls to the Monitor, saving its current value
; Parameters: hControl - passed as either a single control hWnd or an array of control hWnds
; Group (Optional) - Specify a string to identify a group of controls, these can then be reset
; together. Required when adding radio controls that are part of the same button group
; Return values: None
; ===================================================================================================================
Add(hControl, Group := "") {
If (IsObject(hControl)) {
For Each, hWnd in hControl {
this.Ctrl[hWnd] := {}
GuiControlGet, Value,, % hWnd
this.Ctrl[hWnd].Value := Value
If (Group) {
this.Ctrl[hWnd].Group := Group
}
}
} Else {
this.Ctrl[hControl] := {}
GuiControlGet, Value,, % hControl
this.Ctrl[hControl] := Value
If (Group) {
this.Ctrl[hControl].Group := Group
}
}
}
; ===================================================================================================================
; Remove Removes controls from the Monitor
; Parameters: hControl - passed as either a single control hWnd or an array of control hWnds
; Group (Optional) - Removes all controls from the specified group
; Return values: None
; ===================================================================================================================
Remove(hControl, Group := "") {
If (IsObject(hControl))
For Each, hWnd in hControl {
If (Group && this.Ctrl[hWnd].Group != Group)
Continue
Else
this.Ctrl.Delete(hWnd)
}
this.Ctrl.Delete(hControl)
}
; ===================================================================================================================
; Reset Resets the controls in the monitor, saving their current values to be the new 'unchanged' value
; Parameters: hControl - passed as either a single control hWnd or an array of control hWnds
; ** OMIT to reset all controls in the monitor
; Group (Optional) - Resets all controls in the specified group
; Return values: None
; ===================================================================================================================
Reset(hControl := "", Group := "") {
If !(hControl) {
For hWnd in this.Ctrl {
If (Group && this.Ctrl[hWnd].Group != Group)
Continue
GuiControlGet, Value,, % hWnd
this.Ctrl[hWnd].Value := Value
}
} Else If (IsObject(hControl)) {
For Each, hWnd in hControl {
GuiControlGet, Value,, % hWnd
this.Ctrl[hWnd].Value := Value
}
} Else {
GuiControlGet, Value,, % hControl
this.Ctrl[hControl].Value := Value
}
}
; ===================================================================================================================
; Target Sets the target label or function to execute when monitored control is modified
; Parameters: Target - Name of a label or function
; Return values: On success - False
; On failure - True
; Remarks: If the target is a function it will pass the controls hWnd as the first parameter
; ===================================================================================================================
Target(Target) {
If IsLabel(Target) {
this.Target := Target
Return False
} Else If IsFunc(Target) {
this.Target := Func(Target)
Return False
}
MsgBox, 16, Error, % Target " is not a valid label or function"
Return True
}
; ===================================================================================================================
; Check Checks if the value of a control has changed
; Parameters:
; Return values: On success - False
; On failure - True
; Remarks: Called by function bound to WM_COMMAND OnMessage
; ===================================================================================================================
Check(wParam, hCtrl, msg, hWnd) {
Modified := 0
NC := (wParam >> 16) ; Get notification code
If (this.State && this.Ctrl.HasKey(hCtrl)
&& (NC = 0x300 ;EN_CHANGE
|| NC = 0 ;STN_CLICKED
|| NC = 0x0 ;BN_CLICKED
|| NC = 1 ;CBN_SELCHANGE, LBN_SELCHANGE
|| NC = 5)) { ;CBN_EDITCHANGE
; Make sure click is finished if any functions are using WM_LBUTTONUP
While GetKeyState("LButton","P")
Sleep 50
; Compare the value
GuiControlGet, Value,, % hCtrl
If (this.Ctrl[hCtrl].Value != Value) {
Modified := 1
}
; Execute target if control is modified
If (Modified) {
If (this.Ctrl[hCtrl].Group)
this.Reset("", this.Ctrl[hCtrl].Group)
Else
this.Reset(hCtrl) ; Reset controls compare value to the new value
If IsLabel(this.target)
GoSub % this.Target
Else If IsFunc(this.Target)
this.Target.Call(hCtrl)
}
}
}
}
Code: Select all
; =======================================================================================================================
; Class_ControlMonitor Example - By RazorHalo
; =======================================================================================================================
#NoEnv
#SingleInstance Force
SetWorkingDir %A_ScriptDir%
SetBatchLines -1
#Include Class_ControlMonitor.ahk
MonitorRadioControls := 0
Gui Add, CheckBox, hWndhCheckBox vCheckBox x14 y6 w68 h23, CheckBox
Gui Add, ComboBox, hWndhComboBox vComboBox x160 y40 w121, ComboBox||Option 1|Option 2|Option 3
Gui Add, Edit, hWndhEditBox vEditBox x108 y8 w172 h21, EditBox
Gui Add, DropDownList, hWndhDropDownList vDropDownList x12 y39 w131, DropDownList||Option 1|Option 2|Option 3
Gui Add, ListBox, hWndhListBox vListBox x161 y76 w121 h108, ListBox||Red|Green|Blue|Black|White
Gui Add, Radio, hWndhRadioButton1 vRadioButton1 x31 y95 w91 h23, Radio Button 1
Gui Add, Radio, hWndhRadioButton2 vRadioButton2 x31 y122 w91 h24, Radio Button 2
Gui Add, Radio, hWndhRadioButton3 vRadioButton3 x31 y151 w91 h23, Radio Button 3
Gui Add, GroupBox, hWndhGroupBox vGroupBox x12 y72 w131 h113, NOT Monitored
Gui Add, Button, gMonitorRadioControls_TOGGLE x10 y194 w131 h23, Toggle Radio Monitor
Gui Font, s16 cRed
Gui Add, Text, hWndhTxtStatus x22 y253 w249 h24 +0x200 Center, None
Gui Font
Gui Font, s16 Underline
Gui Add, Text, x20 y223 w249 h24 +0x200 Center, Last Updated Control
Gui Font
Gui Add, Button, hWndhBtnMonitor_TOGGLE vBtnMonitor_TOGGLE gMonitor_TOGGLE x152 y194 w131 h23, Pause Monitor
Gui Show, w291 h289, Control Monitor
; Define some arrays of control hWnds
SomeControls := [hCheckBox, hComboBox, hEditBox, hDropDownList, hGroupBox]
RadioControls := [hRadioButton1, hRadioButton2, hRadioButton3]
; Create a new instance
Monitor := New ControlMonitor
Monitor.Add(SomeControls) ; Add controls by passing an array of hWnds
Monitor.Add(hListBox) ; Add single control by passing a single hWnd
; Set the target to execute when a control changes
; In this case its the name of a function, but can also be a label to jump to
Monitor.Target("RefreshStatus")
Return
; Clean up and Exit
GuiEscape:
GuiClose:
Monitor.Destroy() ; Needed to release the references to the WM_COMMAND message function
Monitor := ""
ExitApp
; Toggle monitoring of Radio Controls by adding and removing them from the monitor
MonitorRadioControls_TOGGLE:
If (MonitorRadioControls := !MonitorRadioControls) {
GuiControl ,, % hGroupBox, Monitoring
Monitor.Add(RadioControls, "Radio") ; Radio controls need to be added as a group
} Else {
GuiControl ,, % hGroupBox, NOT Monitored
Monitor.Remove(RadioControls)
}
Return
; Pause and resume monitoring
Monitor_TOGGLE:
If (Monitor.Status := ! Monitor.Status) {
GuiControl ,, % hBtnMonitor_TOGGLE, Resume Monitor
Monitor.Off()
} Else {
GuiControl ,, % hBtnMonitor_TOGGLE, Pause Monitor
Monitor.On()
}
Return
; Target function - just updates the gui as to what control was changed
RefreshStatus(hWnd) {
Global hTxtStatus
GuiControlGet, vLabel, Name, % hWnd
GuiControl ,, % hTxtStatus, % vLabel
}