Is it possible for a project to have 2 Forms, a main primary Form and a second Form that never takes the focus.
Sorry I didn't word that very well. The main Form is used for most all of the user interaction and the second form is optional and displays progress and status information. The second Form also provides configuration options, so it will be 'clicked' on from time to time.
If possible, I'd like to have the main Form to always keep the focus as the active window. Placing code in the second Form to relinquish focus once it clicked upon, and returning it to the main Form, is problematic causing flashing titlebars.
Hey Arnoutdv, thanks for the links, and showing a Form with the SW_SHOWNOACTIVATE flag is a good start, but if possible, I'd like to keep the secondary Form from ever taking the focus away from the primary Form.
It would be like how a MDI child Form behaves, but allowing it to reside outside the bounds of the MDI parent.
When working with a MDI parent form, it keeps the focus even when one interacts with one of it's child Forms. The TitleBars on all of the Forms, parent and children, all show as if they are 'Active'. Clicking on a child form does not cause the main MDI form to pass off the focus to the child. This is the functionality I'd like to copy.
Option Explicit
Private Sub Form_Load()
Form2.Show
End Sub
Form2 code:
Code:
Option Explicit
Private Sub Form_Activate()
Form1.SetFocus
End Sub
Do you want the user to be able to drag the "status form" around by its titlebar? Would you want the "status form" to be sizable?
Good Luck,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
If possible, I'd like to have the main Form to always keep the focus as the active window. Placing code in the second Form to relinquish focus once it clicked upon, and returning it to the main Form, is problematic causing flashing titlebars.
Elroy - you sort of nailed it with your last remark - In a perfect solution, Yes I'd love to be able to have the status Form act and behave as any other normal Form. Move, Size, Min - Max, all in the normal fashion so that the user will automatically understand how to interact with it. And it would seem the best way to accomplish that is to have it behave as any other window would, supporting all of the standard commands / interface.
Microsoft Word's Assistant comes really close to what I'm looking for, it allows one to drag the 'child' window outside of the bounds of the main window, and while doing so, Word does not loose focus.
I just want to change the Word Assistant Window to a Program Status Window.
Edit: I'm referring to the old style of Word's assistant when it appeared as a normal Form complete with TitleBar.
Last edited by stuck-n-past; Dec 13th, 2017 at 10:56 AM.
Did you try the little demo (Form1 & Form2) I outlined? Maybe if you were to specify what that's not doing that you'd like to do.
Take Care,
Elroy
EDIT1: And yeah, to suppress the bit of an activation flash that Form2 occasionally gets, it would take sub-classing.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Sorry Eduardo, I just missed your post as I wrote mine. While I'm not always ready to invest the time needed to subclass things, this time around it seems the trade offs will be worth the effort if I can get the results I'm looking for.
Sorry Eduardo, I just missed your post as I wrote mine. While I'm not always ready to invest the time needed to subclass things, this time around it seems the trade offs will be worth the effort if I can get the results I'm looking for.
Subclassing the WM_NCACTIVATE message and setting the wParam to 1 will prevent the title bar from showing the inactive state. But the focus will still change. Perhaps you can use this with a combination of Elroy's suggestion.
'---------------------------------------------------------------------------------------
' Module : Form1
'---------------------------------------------------------------------------------------
Option Explicit
Private Sub Command1_Click()
Dim fForm2 As Form2
Set fForm2 = New Form2
Set fForm2.Parent = Me
fForm2.Move Me.Left + Me.Width, Me.Top
fForm2.Show
End Sub
Code:
'---------------------------------------------------------------------------------------
' Module : Form2
'---------------------------------------------------------------------------------------
Option Explicit
Private m_fParent As Form
Public Property Set Parent(fValue As Form)
Set m_fParent = fValue
End Property
Private Sub Form_Activate()
SetCaption "Activate"
Timer1.Enabled = True
End Sub
Private Sub Form_Deactivate()
Timer1.Enabled = False
SetCaption "De_Activate"
End Sub
Private Sub Form_Load()
Timer1.Interval = 100
Timer1.Enabled = True
End Sub
Private Sub Form_Unload(Cancel As Integer)
Timer1.Enabled = False
Set m_fParent = Nothing
End Sub
Private Sub Timer1_Timer()
Timer1.Enabled = False
'On Error Resume Next
SetCaption "Timer event: set focus parent"
m_fParent.SetFocus
End Sub
Private Sub SetCaption(ByVal sValue As String)
Label1.Caption = sValue
Debug.Print Timer, sValue
End Sub
I'm probably willing to throw together a bit of subclassing, but I've got some more questions.
Will you want to type on this "status-slave form" such as in a TextBox?
Will you want to click buttons on this "status-slave form"?
Will you want to check checkboxes and/or select option buttons on the "status-slave form"?
The typing one is the one that's most difficult for me to get my head around. I mean, that's sort of the whole idea of focus/activation. Why would you want one form showing active but keyboard input goes to another. Is that what we're talking about? Splitting mouse and keyboard input between two forms?: Mouse going wherever it is, but keyboard staying with some "master" form?
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Thanks Eduardo, I had started looking through some of the Window messages wondering about how to prevent the Titlebar from flashing.
I have code squirreled away somewhere that makes a functioning window created solely through API calls. Since the Status Form / Window has minimal functionality compared to the main form, I was curious if I could make use of it. Perhaps having that much control over the message processing loop, could there be a way of preventing the API created form gaining focus?
The Status Form is fairly sparse, containing only two types of controls, Option Buttons and a FlexGrid. There is nothing for the user to ever type when it comes to the status form. Depending on the data displayed on the FlexGrid, the need to size the form comes into play, and the few Option Buttons present are there to toggle between which columns are displayed.
I've tried searching for information on Office97 assistant to see if I could make use of the same functionality, but it looks to be tied to Word.
Arnoutdv - thanks for the code, I'll check it out.
Last edited by stuck-n-past; Dec 13th, 2017 at 11:55 AM.
I use a Frame as a Form, I need sometimes to display additional Information.
You just place a Frame on your Form and add the Controls you need.
here the Class: clsFrameForm
Code:
' Optionen: Moveable, Exitbutton, Area on Form
' Events: QueryUnload, isMoved, LostFocus
Option Explicit
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function DrawCaption Lib "user32" (ByVal hwnd As Long, _
ByVal hdc As Long, pcRect As RECT, _
ByVal un As Long) As Long
Private Declare Function SetRect Lib "user32" (lpRect As RECT, _
ByVal X1 As Long, ByVal Y1 As Long, _
ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Const SM_CYSIZE = 31
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const tbarWidth = 20
Private Const tbarColor = &H29
Private Const BS_RIGHT& = &H200&
Private Const BS_LEFT& = &H100&
Private Const BS_BOTTOM& = &H800&
Private Const BS_TOP& = &H400&
Private Const BS_VCENTER& = &HC00&
Private Const BS_CENTER& = &H300&
Private Const GWL_STYLE& = -16
'---------------------------------------------------------
Private WithEvents Parentform As Form 'Parentform des Frames
Private WithEvents myFrame As Frame 'Frame selbst
Private ShapeA(1) As Shape 'Border Frame Left/Top Right/Bottom
Private WithEvents PicTitleBar As PictureBox 'die Titlebar
Private WithEvents LabelBasic As Label 'zum Abdecken der PicBox
Private WithEvents cmdExit As CommandButton 'Exitbutton
Private WithEvents mTimer As Timer 'zur Überprüfung des Focus
Public Event QueryUnload(Cancel As Boolean) 'Event Click Exitbutton
Public Event FrameMoved() 'Event Frame was moved
Public Event LostFocus() 'Event Frame(Controls) LostFocus
Public Event GotFocus() 'Event GotFocus
Private MeFormX As Single 'Variable moving Frame
Private MeFormY As Single
Private MeMouseDown As Boolean
Private PicTitleBarGotFocus As Boolean 'Titlebar hat gerade den Focus erhalten
Private mvarMoveable As Boolean 'Frame is moveable
Private mvarExitButton As Boolean 'Exitbutton or not
Private mvarCaption As String
Private mvarOnFormTopMax As Single 'Area moving Frame
Private mvarOnFormLeftMax As Single
Private mvarOnFormRightMax As Single
Private mvarOnFormBottomMax As Single
Private mvarVisible As Boolean 'Frame visible schalten
Public Property Let Visible(ByVal vData As Boolean)
mvarVisible = vData
myFrame.Visible = vData
If myFrame.Visible Then
PicTitleBar.SetFocus
mTimer.Enabled = True
Else
mTimer.Enabled = False
End If
ZOrder 0
End Property
Public Property Get Visible() As Boolean
Visible = mvarVisible
End Property
Public Property Let Caption(ByVal vData As String)
mvarCaption = vData
Refresh
End Property
Public Property Get Caption() As String
Caption = mvarCaption
End Property
Public Property Let OnFormBottomMax(ByVal vData As Single)
mvarOnFormBottomMax = vData
End Property
Public Property Get OnFormBottomMax() As Single
OnFormBottomMax = mvarOnFormBottomMax
End Property
Public Property Let OnFormRightMax(ByVal vData As Single)
mvarOnFormRightMax = vData
End Property
Public Property Get OnFormRightMax() As Single
OnFormRightMax = mvarOnFormRightMax
End Property
Public Property Let OnFormLeftMax(ByVal vData As Single)
mvarOnFormLeftMax = vData
End Property
Public Property Get OnFormLeftMax() As Single
OnFormLeftMax = mvarOnFormLeftMax
End Property
Public Property Let OnFormTopMax(ByVal vData As Single)
mvarOnFormTopMax = vData
End Property
Public Property Get OnFormTopMax() As Single
OnFormTopMax = mvarOnFormTopMax
End Property
Public Property Let ExitButton(ByVal vData As Boolean)
'Exitbutton zugelassen oder nicht
mvarExitButton = vData
If mvarExitButton Then
cmdExit.Visible = True
Else
cmdExit.Visible = False
End If
End Property
Public Property Get ExitButton() As Boolean
ExitButton = mvarExitButton
End Property
Public Property Let Moveable(ByVal vData As Boolean)
mvarMoveable = vData
End Property
Public Property Get Moveable() As Boolean
Moveable = mvarMoveable
End Property
'Initialisieren Frame und sonstige Controls
Public Sub Init(mFrame As Frame, Optional IsVisible As Boolean = True)
Static Initdone As Boolean
Dim i As Long
Dim s As String
If Initdone Then
'nicht 2 x aufrufen
Exit Sub
End If
Initdone = True
'zuweisen Frame
Set myFrame = mFrame
'zuweisen der Parentform (aufrufende Form)
Set Parentform = mFrame.Parent
'Caption Ãœberschrift
mvarCaption = myFrame.Caption
'Grenzen für das Frame auf der Form
mvarOnFormTopMax = 0
mvarOnFormLeftMax = 0
mvarOnFormRightMax = 0
mvarOnFormBottomMax = 0
'Controls aufnehmen in Parentform Collection
Set LabelBasic = LoadControl("VB.Label", "FaF_LabelBasic")
Set PicTitleBar = LoadControl("VB.Picturebox", "FAF_PicTitleBar")
Set ShapeA(0) = LoadControl("VB.Shape", "FaF_ShapeA")
Set ShapeA(1) = LoadControl("VB.Shape", "FaF_ShapeB")
Set cmdExit = LoadControl("VB.CommandButton", "FaF_CmdExit")
Set mTimer = LoadControl("VB.Timer", "FaF_mTimer")
'Controls einrichten
SetControls
'anzeigen
Visible = IsVisible
End Sub
'ein Control auf die Parentform beamen
Private Function LoadControl(LibraryName As String, _
objCtlName As String) As Control
Dim i As Long
Dim s As String
Dim objCtl As Control
i = 1
Do
On Error Resume Next
'durch Anhängen einer Laufnummer eindeutige Bezeichner bilden
s = objCtlName & i
'Control in Parentform Collection aufnehmen
Set objCtl = Parentform.Controls.Add(LibraryName, s, Parentform)
If Err.Number = 0 Then
'kein Fehler
Exit Do
ElseIf Err.Number = 727 Then
'Control mit Bezeichner schon vorhanden, Laufnummer + 1
i = i + 1
Err.Clear
Else
'sonstiger Fehler
FehlerAnzeige Err.Number, Err.Description, "clsFrameAsForm.LoadControl"
Exit Do
End If
Loop
'Control zuweisen
Set LoadControl = objCtl
Set objCtl = Nothing
End Function
'Controls endgültig einrichten
Private Sub SetControls()
Dim R As RECT
'Frame ohne Border setzen
myFrame.BorderStyle = 0
'Border Right & Bottom
With ShapeA(0)
Set .Container = myFrame
.BorderWidth = 2
.BorderColor = vb3DDKShadow
.Top = -60
.Left = -60
.Width = myFrame.Width + 60
.Height = myFrame.Height + 60
.Visible = True
End With
'Border Left & Top
With ShapeA(1)
Set .Container = myFrame
.BorderWidth = 3
.BorderColor = vb3DHighlight
.Top = 0
.Left = 0
.Width = myFrame.Width + 60
.Height = myFrame.Height + 60
.Visible = True
End With
'Titlebar
With PicTitleBar
Set .Container = myFrame
.TabStop = False
.Left = 60
.Top = 60
.Width = myFrame.Width - 120
.BorderStyle = 0
.ScaleMode = vbPixels
.Height = GetSystemMetrics(SM_CYSIZE) * Screen.TwipsPerPixelY
.AutoRedraw = True
.Cls
.Visible = True
'Fill Rect
SetRect R, 0, 0, PicTitleBar.ScaleWidth, tbarWidth
'Draw Titlebar
DrawCaption PicTitleBar.hwnd, PicTitleBar.hdc, R, tbarColor
.CurrentY = (.ScaleHeight - .TextHeight("W")) / 2
.CurrentX = 5
.ForeColor = vbTitleBarText
.FontBold = True
PicTitleBar.Print mvarCaption
.AutoRedraw = False
End With
'Titlebar Abdeckung
With LabelBasic
Set .Container = PicTitleBar
.Caption = ""
.BorderStyle = 0
.BackStyle = 0
.Move 0, 0, PicTitleBar.Width, PicTitleBar.Height
.Visible = True
End With
'Exitbutton
With cmdExit
Set .Container = PicTitleBar
.Caption = "r"
.FontName = "Marlett"
.FontSize = 7
.FontBold = False
.FontItalic = False
.Width = 17
.Height = 14
.Top = 2
.Left = PicTitleBar.ScaleWidth - .Width - 2
.TabStop = False
.Visible = True
End With
'anpassen Höhe für X
ButtonTextAlign 1&
'der Timer
mTimer.Interval = 500
mTimer.Enabled = True
End Sub
'anpassen Beschriftung für Exitbutton
Private Sub ButtonTextAlign(ByVal pos As Long)
Dim oldStyle As Long
Dim align As Long
Dim ret As Long
Select Case pos
Case 0 ' Center
align = BS_CENTER
oldStyle = GetWindowLong(cmdExit.hwnd, GWL_STYLE) And Not (BS_TOP Or BS_BOTTOM)
Case 1 ' Top
align = BS_TOP
oldStyle = GetWindowLong(cmdExit.hwnd, GWL_STYLE) And Not BS_BOTTOM
Case 2 'Bottom
align = BS_BOTTOM
oldStyle = GetWindowLong(cmdExit.hwnd, GWL_STYLE) And Not BS_TOP
End Select
ret = SetWindowLong(cmdExit.hwnd, GWL_STYLE, oldStyle Or align)
cmdExit.Refresh
End Sub
'ist der Focus bei einem Steuerelement des Frames
Private Function IsActiveControlOnFrame() As Boolean
If Parentform.ActiveControl.Container = myFrame Then
IsActiveControlOnFrame = True
End If
End Function
'Grundeinstellungen (Default)
Private Sub Class_Initialize()
mvarExitButton = True
mvarMoveable = True
End Sub
'Objecte aus Parentform entfernen und terminieren
Private Sub Class_Terminate()
Parentform.Controls.Remove LabelBasic
Parentform.Controls.Remove ShapeA(0)
Parentform.Controls.Remove ShapeA(1)
Parentform.Controls.Remove cmdExit
Parentform.Controls.Remove mTimer
Parentform.Controls.Remove PicTitleBar
Set mTimer = Nothing
Set LabelBasic = Nothing
Set ShapeA(0) = Nothing
Set ShapeA(1) = Nothing
Set cmdExit = Nothing
Set PicTitleBar = Nothing
Set Parentform = Nothing
End Sub
'Abbruch, Frame schliessen
Private Sub CmdExit_Click()
Dim Cancel As Boolean
'Event für Parentform auslösen
PicTitleBar.SetFocus
RaiseEvent QueryUnload(Cancel)
If Not Cancel Then
'Frame unvisible
myFrame.Visible = False
Else
PicTitleBar.SetFocus
End If
End Sub
'Frame verschieben
Private Sub MoveFrame(X As Single, Y As Single)
Dim X1 As Single
Dim Y1 As Single
'beachten des vorgegebenen Areas beim Verschieben
With myFrame
X1 = .Left + X - MeFormX
Y1 = .Top + Y - MeFormY
If X1 > (Parentform.ScaleWidth - OnFormRightMax - .Width) Then
X1 = Parentform.ScaleWidth - OnFormRightMax - .Width
End If
If X1 < OnFormLeftMax Then
X1 = OnFormLeftMax
End If
If Y1 > (Parentform.ScaleHeight - OnFormBottomMax - .Height) Then
Y1 = Parentform.ScaleHeight - OnFormBottomMax - .Height
End If
If Y1 < OnFormTopMax Then
Y1 = OnFormTopMax
End If
.Left = X1
.Top = Y1
End With
End Sub
'allgemeine Routine
Private Sub FehlerAnzeige(ErrNumber As Long, ErrDescription As String, _
Optional Titel As String = "")
Dim Msg As String
Msg = "Fehler " & ErrNumber & vbCrLf & vbCrLf & _
ErrDescription
MsgBox Msg, vbCritical, Titel
End Sub
'überprüfen, ob das Focus haltende Steuerelement
'zum Frame gehört
Private Sub mTimer_Timer()
If Not myFrame.Visible Then
mTimer.Enabled = False
Exit Sub
End If
If Not Parentform.ActiveControl.Container Is myFrame Then
'nein, Event LostFocus feuern
RaiseEvent LostFocus
End If
End Sub
'beim Click auf das Frame
Private Sub myFrame_Click()
'in den Vordergrund
myFrame.ZOrder 0
'wenn der Focus nicht bei einem Steuerelement
'des Frames liegt
If Not IsActiveControlOnFrame Then
PicTitleBar.SetFocus
RaiseEvent GotFocus
End If
End Sub
Public Sub Refresh()
'Refreshen
SetControls
End Sub
'reagieren auf vbMaximize, vbNormal, FormResize
Private Sub Parentform_Resize()
MeFormX = myFrame.Left
MeFormY = myFrame.Top
MoveFrame MeFormX, MeFormY
End Sub
Private Sub PicTitleBar_LostFocus()
PicTitleBarGotFocus = False
End Sub
'Beginn Moven
Private Sub LabelBasic_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'in den Vordergrund
myFrame.ZOrder 0
'verschiebbar ? Start Move
If Moveable Then
MeMouseDown = True
MeFormX = X
MeFormY = Y
End If
'der Focus liegt hier
If Not PicTitleBarGotFocus Then
PicTitleBarGotFocus = True
RaiseEvent GotFocus
End If
'Timer abschalten
mTimer.Enabled = False
End Sub
'Frame moven
Private Sub LabelBasic_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Static IsLocked As Boolean
Static Lasttimer As Single
Static LastCheck As Long
If IsLocked Then
Exit Sub
End If
If MeMouseDown Then
IsLocked = True
MoveFrame X, Y
'Event auslösen
RaiseEvent FrameMoved
IsLocked = False
End If
End Sub
'Ende Frame moven
Private Sub LabelBasic_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
MeMouseDown = False
mTimer.Enabled = True
End Sub
'ZOrder setzen
Public Sub ZOrder(Position As Long)
myFrame.ZOrder Position
End Sub
and here the Form , add a Frame and a Commandbutton
Code:
Option Explicit
Private cFrameF As clsFrameForm 'Class
Private Sub Command1_Click()
cFrameF.Visible = True
cFrameF.ZOrder 0
Text1.SetFocus
'add the Controls you need
'EDIT:
'I mean add the Controls to your Frame, and adjust the Frame Height/Width
End Sub
Private Sub Form_Load()
With Frame1
.Width = 3615
.Height = 2775
.Caption = "this is my Frame as Form"
End With
'set the Frame Visible to False
Set cFrameF = New clsFrameForm
cFrameF.Init Frame1, False
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set cFrameF = Nothing
End Sub
regards
Chris
Last edited by ChrisE; Dec 14th, 2017 at 02:04 AM.
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
To prevent unwanted Focus-Switching, Subclassing is not needed at all, when certain Style-Flags get set
(in the following Demo on a normal Private-UserControl, which then acts as such a "PopOut").
Add a Private UserControl to an empty Project and name it: ucPopOut
On that UserControl - add a VB-Button named: Command1
Set the BackGround-Color of the UserControl to a "contrasting color" of your choice (to see its area better).
Now put the following Demo-Code into the UserControl:
Code:
Option Explicit
Event CommandClick()
Private Const GWL_STYLE = -16, GWL_EXSTYLE = -20, WS_EX_TOOLWINDOW = &H80, WS_EX_NOACTIVATE = &H8000000
Private Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Private Declare Function GetWindowLongW Lib "user32" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLongW Lib "user32" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long
Private Declare Function SendMessageW Lib "user32" (ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, lParam As Any) As Long
Private mX!, mY!
Public Sub PopOut(X, Y)
SetWindowLongW hWnd, GWL_EXSTYLE, GetWindowLongW(hWnd, GWL_EXSTYLE) Or WS_EX_TOOLWINDOW Or WS_EX_NOACTIVATE
SetParent hWnd, 0
mX = UserControl.Extender.Left: mY = UserControl.Extender.Top
UserControl.Extender.Move X, Y
End Sub
Public Sub PopIn(hWndParent As Long)
SetParent hWnd, hWndParent
UserControl.Extender.Move mX, mY
End Sub
Private Sub Command1_Click()
RaiseEvent CommandClick
End Sub
Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Call ReleaseCapture: SendMessageW hWnd, &H112, &HF012&, 0& 'just to allow dragging on the UC
End Sub
Now close all UserControl-Windows in your IDE and add the new Control to your Form, named: ucPopOut1
Add a normal VB-TextBox to your Form as well, named: Text1
FormCode:
Code:
Option Explicit
Private Sub Form_Load()
Caption = "Click Me"
End Sub
Private Sub Form_Click()
Static InOut As Boolean: InOut = Not InOut
If InOut Then
ucPopOut1.PopOut Left + Width, Top
Else
ucPopOut1.PopIn hWnd
End If
End Sub
Private Sub ucPopOut1_CommandClick() 'just to show that the Event-Transport works
Text1.Text = Text1.Text & " " & Time
End Sub
Private Sub Form_Unload(Cancel As Integer)
ucPopOut1.PopIn hWnd 'just to make sure, that the uc is back within the Parent it started from (before cleanup)
End Sub
Clicking the Form repeatedly will Pop the ucPopup1 "out or back in"
(the Control will be draggable as well).
Hmmm, well, I've been playing around with it, and I can't quite get the correct combination. Here ... I'll post my "play" code:
For a BAS module:
Code:
Option Explicit
'
Public gbAllowSubclassing As Boolean
'
Private Const WM_DESTROY As Long = &H2&
'
Private Declare Function SetWindowSubclass Lib "comctl32.dll" Alias "#410" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, Optional ByVal dwRefData As Long) As Long
Private Declare Function GetWindowSubclass Lib "comctl32.dll" Alias "#411" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, pdwRefData As Long) As Long
Private Declare Function RemoveWindowSubclass Lib "comctl32.dll" Alias "#412" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long) As Long
Private Declare Function NextSubclassProcOnChain Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'Private Declare Function DefSubclassProc Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'
Private Declare Sub PutMem4 Lib "msvbvm60.dll" (ByRef Ptr As Any, ByRef Value As Any)
Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function SetActiveWindow Lib "user32.dll" (ByVal hWnd As Long) As Long
'
Dim bSetWhenSubclassing_UsedByIdeStop As Boolean ' Never goes false once set by first subclassing, unless IDE Stop button is clicked.
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
Private Sub SubclassSomeWindow(hWnd As Long, AddressOf_ProcToHook As Long, Optional dwRefData As Long)
' This just always uses hWnd for uIdSubclass, as we never have a need to subclass the same window to the same proc.
' The uniqueness is pfnSubclass and uIdSubclass (second and third argument below).
'
' This can be called AFTER the initial subclassing to update dwRefData.
'
If Not gbAllowSubclassing Then Exit Sub
'
bSetWhenSubclassing_UsedByIdeStop = True
Call SetWindowSubclass(hWnd, AddressOf_ProcToHook, hWnd, dwRefData)
End Sub
Private Sub UnSubclassSomeWindow(hWnd As Long, AddressOf_ProcToHook As Long)
' Only needed if we specifically want to un-subclass before we're closing the form (or control),
' otherwise, it's automatically taken care of when the window closes.
'
' Be careful, some subclassing may require additional cleanup that's not done here.
Call RemoveWindowSubclass(hWnd, AddressOf_ProcToHook, hWnd)
End Sub
Private Function ProcedureAddress(AddressOf_TheProc As Long)
' A private "helper" function for writing the AddressOf_... functions (see above notes).
ProcedureAddress = AddressOf_TheProc
End Function
Private Function IdeStopButtonClicked() As Boolean
' The following works because all variables are cleared when the STOP button is clicked,
' even though other code may still execute such as Windows calling some of the subclassing procedures below.
IdeStopButtonClicked = Not bSetWhenSubclassing_UsedByIdeStop
End Function
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
Public Sub SubclassFormNeverFocus(frmChild As VB.Form, frmParent As VB.Form)
SubclassSomeWindow frmChild.hWnd, AddressOf NeverFocus_Proc, frmParent.hWnd
End Sub
Private Function NeverFocus_Proc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
If uMsg = WM_DESTROY Then
UnSubclassSomeWindow hWnd, AddressOf_NeverFocus_Proc
NeverFocus_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
Exit Function
End If
If IdeStopButtonClicked Then ' Protect the IDE. Don't execute any specific stuff if we're stopping. We may run into COM objects or other variables that no longer exist.
NeverFocus_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
Exit Function
End If
'
Const WM_NCACTIVATE = 134&
Const WM_ACTIVATE = 6&
Const WM_PAINT = 15&
Const GW_OWNER = 4&
Dim hOwner As Long
'If uMsg = WM_NCACTIVATE Or uMsg = WM_ACTIVATE Then
If uMsg = WM_ACTIVATE Then
SetActiveWindow dwRefData
Exit Function
End If
'
NeverFocus_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
End Function
Private Function AddressOf_NeverFocus_Proc() As Long
AddressOf_NeverFocus_Proc = ProcedureAddress(AddressOf NeverFocus_Proc)
End Function
For Form1 (the "parent"):
Code:
Option Explicit
Private Sub Form_Activate()
Load Form2
gbAllowSubclassing = True
SubclassFormNeverFocus Form2, Me
Form2.Show
End Sub
For Form2 (the child/status form):
Code:
Option Explicit
Private Sub Command1_Click()
MsgBox "Command1 clicked"
End Sub
I also tried with assigning an official "parent" on the Form2.Show line, but that didn't help matters. When done the way above, you lose all ability to click the Form2.
I'll let others play with it, but the non-subclass approach might be the best way to do it. That way, you still get resize, close, etc.
Good Luck,
Elroy
EDIT1: The NeverFocus_Proc was where I was doing most of the playing. If you want a list of windows messages, a long list of them can be found here.
EDIT2: The "assumed" task I was shooting for: 1) slave/child window's title bar never shows active, 2) slave/child window never receives keyboard input, 3) slave/child window's resize, close, drag still work as expected, 4) slave/child window processes mouse clicks, 5) slave/child window auto-gives focus to its parent.
That reminds me of something else. Notice that NeverFocus_Proc has access to both hwndChild and hwndParent (even if/when it's not the official parent.
EDIT3: And truth be told, I like some of the other ideas. And this isn't something I'd probably ever do. It just sounded a bit fun.
Last edited by Elroy; Dec 13th, 2017 at 02:06 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Dang everyone, it must be the Holiday Season, I can't thank all of you enough for so much support and example code that you have provided. I'm busy over here typing away learning from all of your generous posts.
I'll post an update shortly.
Don't know if it's OK to say this or not.
With all of the craziness in the news these days, I've got to say how cool it is that complete strangers from around the world come together and help others on this Forum. Too bad not everybody takes after the kindness found here.
Thanks to all and here's wishing everyone a wonderful holiday season.
Stuck, IMHO, it's absolutely wonderful that you've said that.
And the absolute best wishes to you and yours as well, and that goes for everyone here.
Happy Holidays,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
With all of the craziness in the news these days, I've got to say how cool it is that complete strangers from around the world come together and help others on this Forum. Too bad not everybody takes after the kindness found here.
Thanks to all and here's wishing everyone a wonderful holiday season.
true words
have a great holiday season
regards from Germany
Chris
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
Hey all, sorry I haven't gotten back here sooner, been spending a great deal of time with AutoCad woes.
I don't always pay attention to where people come from on this Forum, except for when I had first joined. That's when I started noticing that people from around the world were posting here. Sitting in front of a computer screen in a hazy-eyed stare 8 hours a day, you sort of loose track and forget about the fact that messages fly around the globe in mere seconds bringing everybody closer together. Something growing up I never could of imagined, yea OK so I'm a dinosaur.
Chris - I just went Doh! realizing the location of Frankfurt being in Germany, probably slept through that lesson at school.
Anyhow, while born and raised here in the US, both of my parent are of German descent, and have always been jealous of my older Sister who spent close to a month of a vacation touring Germany. I don't know if it was the particular tour she was on or what, but there were many old castles in which they were able to visit and explore. I love historic sites especially castles. Maybe someday somehow.
Private Function SubclassProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
Select Case uMsg
#If Debugging Then
Case WM_NCACTIVATE
OnNCActivate wParam, lParam
#End If
Case WM_ACTIVATE
#If Debugging Then
OnActivate wParam, lParam
#End If
If (wParam And &HFFFF&) = WA_INACTIVE Then 'If main form is being deactivated
If GetWindowThreadProcessId(lParam) = App.ThreadID Then 'If the window being activated belongs to the same thread as the main form
If IsWindowEnabled(hWnd) Then 'If main form isn't disabled (e.g. by a modal dialog)
dwRefData = SendMessageW(hWnd, WM_NCACTIVATE, TRUE_, 0&) 'Tell the OS to render the main form's nonclient area as if it still is active
End If
End If
End If
Case WM_ACTIVATEAPP
#If Debugging Then
OnActivateApp wParam, lParam
#End If
If wParam Then 'If the main form (or another window in the same thread) is being activated
If IsWindowEnabled(hWnd) Then 'If main form isn't disabled (e.g. by a modal dialog)
dwRefData = SendMessageW(hWnd, WM_NCACTIVATE, TRUE_, 0&) 'Ensure the main form's nonclient area still appears active
End If 'even when the focus is actually on a secondary form
Else
dwRefData = SendMessageW(hWnd, WM_NCACTIVATE, FALSE_, 0&) 'If this thread is losing the keyboard focus, deactivate the main
End If 'form's nonclient area just in case it still appears active
End Select
SubclassProc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
End Function
The main form appears active (red X button and drop shadow) even though the tool window actually has the focus.
Wow - I have to admit I've really enjoyed all of the code posted here and done a bit of mixing. In one way or another, I think I've used pieces from every example.
While grabbing pieces from all, the bulk of my current Frankenstein code uses ChrisE's "Frame/Form" Class overlaid on Olaf's UserControl. The results have the UserControl appearing just like a child form. But hey I'm nothing if not greedy so I also took advantage of dilettante's code in addition to the now new Status UserControl. It allowed me to un-clutter the main form, hiding some of the less frequently used controls.
OK - so I actually wrote this offline as my internet computer happen to be on life support after the nastiest AutoCAD install ever seen by humanity. So I hadn't seen the latest post by Victor which looks really interesting, so it's quite possible Frankenstein is up for still more pieces / parts.
I'll be uploading the combined project here soon, till then - Thanks to all.
Edit: Sorry I forgot to ask something about this newest project which involves Signal-Processing. Its by far the largest I've worked on and the IDE takes a good deal of time just to load, unload and or save and its only getting larger.
My background has been to create libraries of object modules of reusable code. The libraries grew over the years to be extremely large but only the referenced modules were pulled into the final Exe.
With this large of a project, I'm contemplating breaking things down into Class Modules to make things easier to manage. Is that the best way to handle large VB projects or is there a better way?
Thanks.
Last edited by stuck-n-past; Dec 14th, 2017 at 10:16 PM.