; ScrollBind(简单生成可滚动的子页面)  By FeiYue
;
; 使用方法:
;   参考下面的例子,将父控件和GUI子窗口绑定即可

#NoEnv
#SingleInstance force

; 在GUI创建过程避免中途触发GuiSize线程
Critical, % (bak:=A_IsCritical)?"":""

; 先备份GUI主窗口的句柄
Gui, +Hwndgui_id -DPIScale +ReSize  ; +AlwaysOnTop
Gui, Margin, 15, 15
Gui, Color, DDEEFF
Gui, Font, s12

Gui, Add, Tab3, HwndhMyTab w350, 第1页|第2页|第3页|第4页
For k,v in StrSplit("第1页|第2页|第3页|第4页", "|") {
  Gui, Tab, % k
  ; 添加一个文本控件作为父控件,获取父控件句柄
  Gui, Add, Text, Hwndparent_id w300 h400
  ;----------------------------
  ; 新建一个GUI子窗口,获取子窗口句柄
  Gui, New, +Hwndsub_id -DPIScale
  Gui, Color, DDEEFF
  Gui, Font, s12
  Loop 50
    Gui, Add, Button, w250 gRunButton, %v%  %A_Index%
  ; 利用ScrollBind类,将GUI子窗口绑定到父控件中
  ScrollBind.Bind(parent_id, sub_id)
  ; 绑定之后备份父子两个句柄用于调整大小
  parent_id%k%:=parent_id, sub_id%k%:=sub_id
  ;----------------------------
  ; 利用GUI主窗口的句柄返回默认的GUI
  Gui, %gui_id%: Default
}
Gui, Tab

; 获取Tab3控件的高度用于编辑控件对齐
GuiControlGet, p, Pos, %hMyTab%
Gui, Add, Edit, ym w500 h%pH% HwndhMyEdit
Gui, Show, Center, 简单生成可滚动的子页面 - By FeiYue

; 在GUI创建完毕后允许GuiSize线程触发
Critical, %bak%

; 可选的左键任意拖动窗口
OnMessage(0x201, Func("LButton_Down"))
LButton_Down() {
  if (A_Gui!="") and (A_GuiControl="")
    SendMessage, 0xA1, 2
}
return

; 使用函数可避免修改全局变量
RunButton() {
  k:=A_GuiControl
  Gui, +OwnDialogs
  MsgBox, 4096, Tip, % k
}

; 使用函数可避免修改全局变量
GuiSize() {
  global
  local id, w, h, k, v
  Critical
  ; 只响应GUI主窗口的调整大小消息
  Gui, +Hwndid
  if (ErrorLevel=1) or (id!=gui_id)
    return
  w:=Floor(A_GuiWidth-350-15*3), h:=Floor(A_GuiHeight-15*2)
  GuiControl, Move, %hMyEdit%, w%w% h%h%
  GuiControl, Move, %hMyTab%, h%h%
  ; 对GUI子窗口的调整大小,先确保创建完毕
  For k,v in StrSplit("第1页|第2页|第3页|第4页", "|")
  if (parent_id%k% and sub_id%k%) {
    h:=Floor(A_GuiHeight-15*2-60)
    GuiControl, Move, % parent_id%k%, h%h%
    ScrollBind.ReSize(sub_id%k%, 300, h)
  }
}

GuiClose() {
  ExitApp
}


;===== 下面是 ScrollBind 类 =====


; 这个类只用于绑定父控件和GUI子窗口,不干涉子窗口的生成
Class ScrollBind {  ;-- 类开始

  __New(args*) {
    ; 不生成实体对象,只使用类对象本身
    return this.base
  }

  ; 核心的绑定函数,参数是父控件的句柄和GUI子窗口的句柄
  Bind(parent_id, sub_id) {
    static init:=0
    if !DllCall("IsWindow", "Ptr",parent_id)
    or !DllCall("IsWindow", "Ptr",sub_id)
      return
    if (!init) {
      init:=1, this.Scroll_ID:=[]
      OnMessage(WM_MOUSEWHEEL:=0x20A, this.WM_MOUSEWHEEL.Bind(this))
      OnMessage(WM_KeyDown:=0x100, this.WM_KEYDOWN.Bind(this))
      OnMessage(WM_VSCROLL:=0x115, this.WM_VSCROLL.Bind(this))
      OnMessage(WM_HSCROLL:=0x114, this.WM_VSCROLL.Bind(this))
    }
    ; 避免Gui,Show触发GuiSize线程中断当前线程
    Critical, % (bak:=A_IsCritical)?"":""
    VarSetCapacity(rect,16)
    DllCall("GetClientRect", "Ptr",parent_id, "Ptr",&rect)
    w:=NumGet(rect, 8, "Int"), h:=NumGet(rect, 12, "Int")
    Gui, %sub_id%: +Parent%parent_id% -Caption +ToolWindow -Border
    Gui, %sub_id%: Show, NA x0 y0 w%w% h%h%
    this.UpdateScrollBars(sub_id, w, h)
    this.Scroll_ID[parent_id]:=sub_id
    this.Scroll_ID[sub_id]:=sub_id
    Critical, %bak%
  }

  ; 调整GUI子窗口的大小
  ReSize(sub_id, w, h) {
    DllCall("MoveWindow","Ptr",sub_id,"int",0,"int",0,"int",w,"int",h,"int",1)
    this.UpdateScrollBars(sub_id, w, h)
  }

  ; 当鼠标在可滚动的子窗口上时,可以用鼠标滚轮来滚动
  WM_MOUSEWHEEL(wParam) {
    Critical
    if (A_Gui="")
      return
    MouseGetPos,,,, id, 2
    if (this.Scroll_ID[id])
    or (this.Scroll_ID[id:=DllCall("GetParent","ptr",id)])
    or (this.Scroll_ID[id:=DllCall("GetParent","ptr",id)])
    {
      id:=this.Scroll_ID[id], wParam:=(wParam>>16&0xFFFF)>0x7FFF
      Loop 4
        this.WM_VSCROLL(wParam, 0, 0x115, id)
      return 1
    }
  }

  ; 当鼠标在可滚动的子窗口上时,可以用方向键和翻页键来滚动
  WM_KEYDOWN(wParam) {
    static arr:={ GetKeyVK("Up"):0, GetKeyVK("Down"):1
      , GetKeyVK("PgUp"):2, GetKeyVK("PgDn"):3 }
    Critical
    if (A_Gui="") or (!arr.HasKey(wParam))
      return
    MouseGetPos,,,, id, 2
    if (this.Scroll_ID[id])
    or (this.Scroll_ID[id:=DllCall("GetParent","ptr",id)])
    or (this.Scroll_ID[id:=DllCall("GetParent","ptr",id)])
    {
      id:=this.Scroll_ID[id], wParam:=arr[wParam]
      this.WM_VSCROLL(wParam, 0, 0x115, id)
      return 1
    }
  }

  ; thanks lexikos, FeiYue 作了改进
  UpdateScrollBars(GuiHwnd, GuiWidth, GuiHeight) {
    static SIF_RANGE:=1, SIF_PAGE:=2, SB_HORZ:=0, SB_VERT:=1
    if (GuiWidth<0 or GuiHeight<0)
      return
    DetectHiddenWindows, % (bak:=A_DetectHiddenWindows)?"On":"On"
    WinGet, List, ControlListHwnd, ahk_id %GuiHwnd%
    DetectHiddenWindows, %bak%
    Left:=Top:=9999, Right:=Bottom:=0
    Loop, Parse, List, `n
    {
      GuiControlGet, c, Pos, %A_LoopField%
      Left:=Min(Left, cX), Top:=Min(Top, cY)
      Right:=Max(Right, cX+cW), Bottom:=Max(Bottom, cY+cH)
    }
    Left-=8, Top-=8, Right+=8, Bottom+=8
    ScrollWidth:=Right-Left, ScrollHeight:=Bottom-Top
    ;-- Initialize SCROLLINFO
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
    ;-- Update horizontal scroll bar
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "Ptr",GuiHwnd, "uint",SB_HORZ, "Ptr",&si, "int",1)
    ;-- Update vertical scroll bar
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "Ptr",GuiHwnd, "uint",SB_VERT, "Ptr",&si, "int",1)
    x:=(Left<0 && Right<GuiWidth) ? Min(-Left, GuiWidth-Right) : 0
    y:=(Top<0 && Bottom<GuiHeight) ? Min(-Top, GuiHeight-Bottom) : 0
    if (x || y)
      DllCall("ScrollWindow", "Ptr",GuiHwnd, "int",x, "int",y, "int",0, "int",0)
  }

  ;  thanks lexikos, FeiYue 作了改进
  WM_VSCROLL(wParam, lParam, msg, hwnd) {
    static SIF_ALL:=0x17, SCROLL_STEP:=10
    Critical
    bar:=(msg=0x115)
    VarSetCapacity(si, 28, 0), NumPut(28, si, "UInt") ; cbSize
    NumPut(SIF_ALL, si, 4, "UInt") ; fMask
    if !DllCall("GetScrollInfo", "Ptr",hwnd, "Int",bar, "Ptr",&si)
      return
    VarSetCapacity(rect,16), DllCall("GetClientRect", "Ptr",hwnd, "Ptr",&rect)
    h:=bar ? NumGet(rect, 12, "Int") : NumGet(rect, 8, "Int")
    new_pos:=old_pos:=NumGet(si, 20, "UInt") ; nPos
    Switch (wParam & 0xFFFF) {
      Case 0:  new_pos-=SCROLL_STEP   ; SB_LINEUP
      Case 1:  new_pos+=SCROLL_STEP   ; SB_LINEDOWN
      Case 2:  new_pos-=h-SCROLL_STEP   ; SB_PAGEUP
      Case 3:  new_pos+=h-SCROLL_STEP   ; SB_PAGEDOWN
      Case 4, 5: new_pos:=wParam>>16    ; SB_THUMBPOSITION, SB_THUMBTRACK
      Case 6:  new_pos:=NumGet(si, 8, "Int")   ; nMin  ; SB_TOP
      Case 7:  new_pos:=NumGet(si, 12, "Int")  ; nMax  ; SB_BOTTOM
      Default:   return
    }
    min:=NumGet(si, 8, "Int") ; nMin
    max:=NumGet(si, 12, "Int") - NumGet(si, 16, "UInt") ; nMax-nPage
    new_pos:=Min(Max(min, new_pos), max)
    (bar) ? (x:=0, y:=old_pos-new_pos) : (x:=old_pos-new_pos, y:=0)
    DllCall("ScrollWindow", "Ptr",hwnd, "Int",x, "Int",y, "Int",0, "Int",0)
    NumPut(new_pos, si, 20, "Int") ; nPos
    DllCall("SetScrollInfo", "Ptr",hwnd, "Int",bar, "Ptr",&si, "Int",1)
  }
}  ;-- 类结束

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。