Aplikacja Windows z dwoma oknami

Aplikacja Windows z dwoma oknami
Manna5
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 685
0

W ramach ćwiczenia pierwszy raz napisałem aplikację windowsową posiadającą nie jedno, ale dwa okna, z czego jedno jest oknem głównym, a drugie jest podrzędne i nie zawsze jest otwarte.

Zrzut ekranu:
screenshot-20250521065931.png
Zaimplementowałem następujące cechy zachowania aplikacji z wieloma oknami:

  • Drugie okno pojawia się na oknie pierwszym, a nie w jakimś przypadkowym miejscu. Potem użytkownik może oczywiście je przemieszczać.
  • Zamknięcie przez użytkownika drugiego okna nie wiąże się z zakończeniem programu. Okno pierwsze staje się wtedy oknem aktywnym.
  • Gdy okno drugie jest otwarte, to pierwsze jest wyszarzałe i nie reaguje na kliknięcia, a jeśli spróbujemy coś w nim kliknąć to Windows przypomni nam o drugim oknie za pomocą dobrze znanego efektu graficznego. Nie można, a przynajmniej mi się nie udało, wepchnąć żadnego "obcego" okna pomiędzy okno pierwsze a drugie.

Pod względem technicznim okno drugie nie jest tworzone w momencie żądania jego otwarcia, niszczone, a potem tworzone od nowa, ale istnieje od początku do końca działania programu i jest tylko ukrywane albo pokazywane. Gdyby zawierało jakieś pola tekstowe albo pola wyboru, to należałoby rozważyć przywrócenie ich wszystkich do jakiegoś domyślnego stanu przy pokazywaniu drugiego okna. Dzięki temu pokazanie okna jest szybsze, ale za to, gdyby zawierało ono jekieś hipotetyczne "ciężkie pamięciwo" komponenty to zajmowałyby one pamięć nawet, gdy nie byłyby potrzebne.

Czy jest to poprawny i przewidziany przez twórców systemu Windows sposób implementacji takiej aplikacji dwuoknowej, gdyby na przykład drugie okno służyło wyświetleniu wyników przetwarzania danych wprowadzonych w pierwszym albo umożliwieniu wprowadzenia szczegółowych parametrów polecenia wydanego przyciskiem w pierwszym oknie?

Kod w języku C:

Kopiuj
#include <windows.h>

#define TYT1 "Okno pierwsze"
#define TYT2 "Okno drugie"

HWND uok1, uok2;
HWND o1pcisk;
BOOL jest2;

LRESULT CALLBACK wndp1 (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        HWND hh;
        RECT rect;
        
        switch (uMsg) {
        case WM_DESTROY:
                PostQuitMessage (0);
                break;
        case WM_COMMAND:
                hh = (HWND) lParam;
                if (hh == o1pcisk) {
                        if (!jest2) {
                                jest2 = TRUE;
                                EnableWindow (uok1, FALSE);
                                GetWindowRect (uok1, &rect);
                                SetWindowPos (uok2, HWND_TOP,
                                                rect.left + 25, rect.top + 25,
                                                0, 0, SWP_NOSIZE);
                                ShowWindow (uok2, SW_RESTORE);
                                }
                        }
                break;                  
        default:
                return DefWindowProc (hwnd, uMsg, wParam, lParam);
                }
        }

LRESULT CALLBACK wndp2 (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        switch (uMsg) {
        case WM_CLOSE:
                ShowWindow (uok2, SW_HIDE);
                jest2 = FALSE;
                EnableWindow (uok1, TRUE);
                BringWindowToTop (uok1);
                break;
        default:
                return DefWindowProc (hwnd, uMsg, wParam, lParam);
                }
        }

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASS wndcla;
        MSG msg;
        
        jest2 = FALSE;
        
        wndcla.style = 0;
        wndcla.lpfnWndProc = wndp1;
        wndcla.cbClsExtra = wndcla.cbWndExtra = 0;
        wndcla.hInstance = hInstance;
        wndcla.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wndcla.hCursor = LoadCursor (NULL, IDC_CROSS);
        wndcla.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
        wndcla.lpszMenuName = NULL;
        wndcla.lpszClassName = "pierwsze";
        RegisterClass (&wndcla);
        
        wndcla.style = 0;
        wndcla.lpfnWndProc = wndp2;
        wndcla.cbClsExtra = wndcla.cbWndExtra = 0;
        wndcla.hInstance = hInstance;
        wndcla.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wndcla.hCursor = LoadCursor (NULL, IDC_ARROW);
        wndcla.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
        wndcla.lpszMenuName = NULL;
        wndcla.lpszClassName = "drugie";
        RegisterClass (&wndcla);
        
        uok1 = CreateWindow ("pierwsze", TYT1,
                        WS_OVERLAPPED | WS_SYSMENU,
                        CW_USEDEFAULT, 0, 320, 110, NULL,
                        NULL, hInstance, NULL);
        o1pcisk = CreateWindow ("BUTTON", "Otworz okno 2",
                        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                        10, 10, 300, 55, uok1,
                        NULL, hInstance, NULL);
        uok2 = CreateWindow ("drugie", TYT2,
                        WS_OVERLAPPED | WS_SYSMENU,
                        0, 0, 250, 250, uok1,
                        NULL, hInstance, NULL);
        CreateWindow ("STATIC", "Jestem drugim oknem.",
                        WS_CHILD | WS_VISIBLE | SS_CENTER,
                        0, 100, 250, 50, uok2,
                        NULL, hInstance, NULL);
        
        ShowWindow (uok1, SW_SHOW);
        while (GetMessage (&msg, NULL, 0, 0)) {
                TranslateMessage (&msg);
                DispatchMessage (&msg);
                }
        DestroyWindow (uok2);
        
        ExitProcess (0);
        }
Marius.Maximus
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2264
1

Ja nie widzę przeciwwskazań w tworzeniu wielu okien!
Nie wiem tylko czy w obecnych czasach warto używać winapi.

RD
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 77
0

Za mało danych. Jeśli to jest okno do np. dodawania nowego wpisu do tabeli to nie wiem, czy jest sens cachować je, tylko tworzył bym je i usuwał wedle potrzeby. Co więcej dodawanie nowego rekordu prawidłowo blokuje okno rodzica, bo dodajemy nowy rekord... ale jeśli np. mamy dwa okna, dwóch różnych kartotek to nie ma sensu ich blokować - one powinny być równorzędne. Za mało danych. No i ja w ogóle nie widzę sensu w zabawie WinApi, poza szczególnymi przypadkami - mamy MFC, WinForms, WPF, MAUI... Powiedział bym, że używanie WinApi do tworzenia okien to jak zabawa ASM - raczej nie powinno się tego robić, poza szczególnymi przypadkami.

Ściągnij sobie jakąś aplikacje z wieloma oknami i potestuj - np. Delphi, Lazarus, IBExpert, Gimp, Paint.NET, VisualStudio itd.

KS
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 748
0

O ile rozumiem pytanie to... Trzymanie drugiego okna cały czas w tle jest słabe.

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
0

No fajnie, jak działa to działa i tak projekt do kosza.
Winapi dobrze znać bo nadal nawet w nowoczesnych rozwiązaniach czasem trzeba do niego sięgać i ręcznie obsłużyć jakiś komunikat, ale nie skupiałbym się na tym zbyt dużo bo samym winapi nie dostajesz obsługi nowoczesnych featerów windowsa, np powiadomień toast, nowych file pickerów, media capture, geolokalizacja, czujników, background tasks, obsługa lifecycle, efektów fluent, animacji. No i tempo rozwoju aplikacji jest komiczne, zastanów się czy na pewno chcesz na to tracić życie.

Zastanów się jaki jest twój cel i do czego właściwie dążysz bo jeśli nie planujesz zbudować maszyny czasu, cofnąć się do lat 80-tych i aplikować do microsoftu to ten projekt pokazuje że twoja wiedza na temat winapi jest zupełnie wystarczająca na dzisiejsze standardy.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.