MDI Form w Excelu   strona główna:
A po co ten Excel ;-)
 
Kiedyś był o tym temat na ExcelForum choć rozwinął się zupełnie w innym kierunku. My zajmiemy się stworzeniem i obsługą MDIForm  
w Excelu.   What is the difference between simple form & MDI form?
 
   Że w tłumaczeniu to ja noga jestem to nie będę się  
starał wyjaśniać co to jest MDI Form ciekawi profes.  
definicji znajdą w sieci dość informacji. Napiszę tylko  
w dwóch słowach że jest to taka Forma która pozwala   How do I work with MDI forms?
na tworzenie swoich "podokien". To tak jakbyśmy pisali  
Excela na UserForm'ie: Trzeba zezwolić na utworzenie  
lub otwarcie dowolnej ilości plików w jednej instancji  
Excela. Tak na przykładzie UserForm'a trzeba zezwolić  
na możliwość otwarcia dowolnej ilości "podokien" naszej  
Formy. Poza tym cały zestaw: UserForm i jego podokna  
muszą cię "trzymać razem" tj. Po kliknięciu na UserForm  
nie zasłania on swoich podokien :-) Jeżeli dodamy do  
takich podokien przyciski Min i Max to ich okna będą  
minimalizowane nie w oknie Excela a w oknie UseForm'a.  
Zmaksymalizowanie również odbędzie się w granicach  
formy głównej :-)  
    Słowem jak na obrazku po prawej. Tak to nie obraz  
rodem z Paint'a a odpowiednio oprogramowane dwa  
UserForm'y.  
    Kiedy na ExcelForum zaistniał temat o MDI Form  
nie byłem w stanie w całości podołać tym zadaniom ale ostatnio, szukając "czort wie gdzie" i "czort wie czego" ;-) rzuciła mi się w   SetParent Function
oczy funkcja SetParent (API) i temat odżył. :-)  
 
    Jednak przyciski 'New" i "Close All' chciałem mieć na ToolBar'ze i koniecznie wstawić do nich swoje ikony. Całość więc poszerzyłem  
o zastosowanie:  
Microsoft ImageList Control 6.0 (SP6)  
Microsoft ToolBar Control 6.0 (SP6)  
 
    Najpierw więc tworzymy większy UserForm i nadajemy mu nazwę frmParent. W oknie ToolBox / PPM / Additional Controls...  
odnajdujemy Microsoft ToolBar Control 6.0 i wstawiamy go w LewyGórny róg frmParent. To samo z ImageList tyle że położenie tej  
kontrolki jest dość obojętne. Jest ona widoczna tylko w oknie: View object danego UserForm'a. Jej zadaniem jest przechowywanie  
zdjęć które posłużą nam za ikony naszych przycisków.  
    Po wstawieniu kontrolki ImageList odnajdujemy w oknie Properties właściwości (Custom) i klikamy na ikonę [...] po jej lewej str.  
Otworzy się okno: Properties Page.  
W zakładce: General - ustawimy wielkość ikony jaka ma się wyświetlić na przycisku.   
                 Images - poprzez przycisk InsertPicture wstawiamy nasze zdjęcia.  
 
Z tego co po sieci można poczytać o współpracy kontrolki ImageList z np.: TreeView czy ToolBar wskazanie że to właśnie ze zdjęć  
zapisanych w tej kontrolce należy:  
 - We właściwościach ToolBar'a odnaleźć właściwość (Custom) i kliknąć na ikonę [...]  
 - na Properties Page w zakładce General w poz. ImageList z ComboBox'a wybrać naszą ImageList1  
Ja jednak nie mogę tam nic wybrać :-| Jest tylko <None>. Poradzimy sobie inaczej :-P  
 
Private Sub UserForm_Initialize()  
    Dim objButton As MSComctlLib.Button  
      
    hWndParent = FindWindow("ThunderDFrame", Me.Caption)  
    sTopKor = -Me.Height  
 
    With Me.Toolbar1  
        .Width = Me.Width  
        Set .ImageList = Me.ImageList1  
 
        Set objButton = .Buttons.Add(Index:=1, Key:="B1")  
        With objButton  
            .Style = tbrDefault  
            .Caption = "New"  
            .TooltipText = "Utwórz nowe okno frmChild"  
            .Image = 1  
        End With  
        Set objButton = Nothing  
      
        .Buttons.Add(Index:=2).Style = tbrSeparator  
              
        Set objButton = .Buttons.Add(Index:=3, Key:="B2")  
        With objButton  
            .Caption = "Close All"  
            .TooltipText = "Zamknij wszystkie okna frmChild"  
            .Image = 2  
        End With  
        Set objButton = Nothing  
 
    End With  
End Sub  
 
Wskazanie że ikony do naszego ToolBar'a znajdują się w ImageList1 odbywa się w:  
        Set .ImageList = Me.ImageList1  
 
A wskazanie które zdjęcie ma być w danym przycisku - poprzez określenie właściwości Image tego przycisku równej indeksowi  
zdjęcia w naszej ImageList1.  
 
Teraz obsłużymy zadania naszych przycisków:  
 
Private Sub Toolbar1_ButtonClick(ByVal Button As MSComctlLib.Button)  
    Select Case Button.Key  
        Case "B1": NoweOkno  
        Case "B2": ZamknijOkna  
    End Select  
End Sub  
 
Private Sub NoweOkno()  
    Dim objUFChild As frmChild, frmHwnd As Long  
    Dim lngStyle As Long  
      
    Static sChildTop As Single  
    Static sChildLeft As Single  
 
    Set objUFChild = New frmChild  
      
    With objUFChild  
        frmHwnd = FindWindow("ThunderDFrame", .Caption)  
          
        '---------------- Min Max-----------------  
        lngStyle = GetWindowLong(frmHwnd, GWL_STYLE)  
        lngStyle = lngStyle Or WS_MAXIMIZEBOX  
        lngStyle = lngStyle Or WS_MINIMIZEBOX  
        SetWindowLong frmHwnd, GWL_STYLE, lngStyle  
        DrawMenuBar frmHwnd  
        '-----------------------------------------  
          
        SetParent frmHwnd, hWndParent  
          
        '----- przykładowa Poz okna frmChild -----  
        .StartUpPosition = 0  
        sChildLeft = sChildLeft + 10  
        sChildTop = sChildTop + 10  
        .Top = sTopKor + sChildTop + 20  
        .Left = sChildLeft  
        '-----------------------------------------  
          
        .Show 0  
    End With  
    Set objUFChild = Nothing  
End Sub  
 
Private Sub ZamknijOkna()  
    Dim objChild As Object  
    For Each objChild In VBA.UserForms  
        If objChild.Name = "frmChild" Then  
            Unload objChild  
        End If  
    Next  
End Sub  
 
Trzeba jeszcze zadeklarować funkcje API. Czemu teraz o tym wspominam? Żeby nie zaczynać od tego co wydaje się ludziom   
najtrudniejsze :-P  
 
Option Explicit  
 
Private Declare Function SetParent _  
    Lib "user32.dll" ( _  
        ByVal hWndChild As Long, _  
        ByVal hWndNewParent As Long) _  
    As Long  
      
Private Declare Function FindWindow _  
    Lib "user32" _  
    Alias "FindWindowA" ( _  
        ByVal lpClassName As String, _  
        ByVal lpWindowName As String) _  
    As Long  
      
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 Declare Function DrawMenuBar _  
    Lib "user32" ( _  
        ByVal hwnd As Long) _  
    As Long  
 
Private Const WS_MAXIMIZEBOX As Long = &H10000  
Private Const WS_MINIMIZEBOX As Long = &H20000   Przykłąd można pobrać z:
Private Const GWL_STYLE As Long = (-16)  
  MDIForm.zip
Private hWndParent As Long  
Private sTopKor As Single