Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Bagian ini menjelaskan cara melakukan tugas berikut:
- Mendaftarkan Kelas Jendela Anak dan Jendela Kerangka
- Membuat Bingkai dan Jendela Anak
- Menuliskan Perulangan Pesan Utama
- Menulis Prosedur Jendela Kerangka
- Menulis Prosedur Jendela Anak
- Membuat Jendela Anak
Untuk mengilustrasikan tugas-tugas ini, bagian ini menyertakan contoh dari Multipad, aplikasi antarmuka multi-dokumen (MDI) biasa.
Mendaftarkan Kelas Jendela Anak dan Rangka
Aplikasi MDI yang khas harus mendaftarkan dua kelas jendela: satu untuk jendela bingkainya dan satu untuk jendela anaknya. Jika aplikasi mendukung lebih dari satu jenis dokumen (misalnya, spreadsheet dan bagan), aplikasi harus mendaftarkan kelas jendela untuk setiap jenis.
Struktur kelas untuk jendela bingkai mirip dengan struktur kelas untuk jendela utama dalam aplikasi non-MDI. Struktur kelas untuk jendela anak MDI sedikit berbeda dari struktur untuk jendela anak dalam aplikasi non-MDI sebagai berikut:
- Struktur kelas harus memiliki ikon, karena pengguna dapat meminimalkan jendela anak MDI seolah-olah itu adalah jendela aplikasi normal.
- Nama menu harus NULL, karena jendela anak MDI tidak dapat memiliki menunya sendiri.
- Struktur kelas harus mencadangkan ruang ekstra dalam struktur jendela. Dengan ruang ini, aplikasi dapat mengaitkan data, seperti nama file, dengan jendela anak tertentu.
Contoh berikut menunjukkan bagaimana Multipad mendaftarkan kelas jendela bingkai dan anak jendelanya.
BOOL WINAPI InitializeApplication()
{
WNDCLASS wc;
// Register the frame window class.
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MPFrameWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, IDMULTIPAD);
wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
wc.lpszMenuName = IDMULTIPAD;
wc.lpszClassName = szFrame;
if (!RegisterClass (&wc) )
return FALSE;
// Register the MDI child window class.
wc.lpfnWndProc = (WNDPROC) MPMDIChildWndProc;
wc.hIcon = LoadIcon(hInst, IDNOTE);
wc.lpszMenuName = (LPCTSTR) NULL;
wc.cbWndExtra = CBWNDEXTRA;
wc.lpszClassName = szChild;
if (!RegisterClass(&wc))
return FALSE;
return TRUE;
}
Membuat Bingkai dan Jendela Anak
Setelah mendaftarkan kelas jendelanya, aplikasi MDI dapat membuat jendelanya. Pertama, ini membuat jendela bingkainya dengan menggunakan fungsi CreateWindow atau CreateWindowEx. Setelah membuat jendela bingkainya, aplikasi membuat jendela kliennya, sekali lagi dengan menggunakan CreateWindow atau CreateWindowEx. Aplikasi harus menentukan MDICLIENT sebagai nama kelas jendela klien; MDICLIENT adalah kelas jendela yang telah terdaftar sebelumnya yang ditentukan oleh sistem. Parameter lpvParamCreateWindow atau CreateWindowEx harus menunjuk ke strukturCLIENTCREATESTRUCT. Struktur ini berisi anggota yang dijelaskan dalam tabel berikut:
| Anggota | Deskripsi |
|---|---|
| hWindowMenu | Tangani ke menu jendela yang digunakan untuk mengontrol jendela anak MDI. Saat jendela anak dibuat, aplikasi menambahkan judulnya ke menu jendela sebagai item menu. Pengguna kemudian dapat mengaktifkan jendela anak dengan mengklik judulnya pada menu jendela. |
| idFirstChild | Menentukan ID jendela anak MDI pertama. Jendela anak MDI pertama yang dibuat diberi identifikasi ini. Jendela tambahan dibuat dengan pengidentifikasi jendela yang ditingkatkan. Ketika jendela anak dihancurkan, sistem segera menetapkan ulang identifikasi jendela untuk menjaga agar rentangnya tetap berdekatan. |
Saat judul jendela anak ditambahkan ke menu jendela, sistem menetapkan ID ke jendela anak. Saat pengguna mengklik judul jendela anak, jendela bingkai menerima pesan WM_COMMAND dengan pengidentifikasi di parameter wParam. Anda harus menentukan nilai untuk idFirstChild anggota sehingga tidak berbenturan dengan pengidentifikasi item menu pada menu jendela bingkai.
Prosedur bingkai jendela Multipad membuat jendela klien MDI saat memproses pesan WM_CREATE. Contoh berikut menunjukkan bagaimana jendela klien dibuat.
case WM_CREATE:
{
CLIENTCREATESTRUCT ccs;
// Retrieve the handle to the window menu and assign the
// first child window identifier.
ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), WINDOWMENU);
ccs.idFirstChild = IDM_WINDOWCHILD;
// Create the MDI client window.
hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR) NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs);
ShowWindow(hwndMDIClient, SW_SHOW);
}
break;
Judul jendela anak ditambahkan ke bagian bawah menu jendela. Jika aplikasi menambahkan string ke menu jendela dengan menggunakan fungsi AppendMenu , string tersebut dapat ditimpa oleh judul jendela turunan ketika menu jendela dicat ulang (setiap kali jendela turunan dibuat atau dihancurkan). Aplikasi MDI yang menambahkan string ke menu jendelanya harus menggunakan fungsi InsertMenu dan memverifikasi bahwa judul jendela anak belum menimpa string baru ini.
Gunakan gaya WS_CLIPCHILDREN untuk membuat jendela klien MDI guna mencegah jendela menutupi jendela anaknya.
Menulis Perulangan Pesan Utama
Perulangan pesan utama dalam aplikasi MDI mirip dengan perulangan dalam aplikasi non-MDI ketika menangani kunci akselerator. Perbedaannya adalah bahwa perulangan pesan MDI memanggil fungsi TranslateMDISysAccel sebelum memeriksa kunci akselerator yang ditentukan aplikasi atau sebelum mengirimkan pesan.
Contoh berikut menunjukkan perulangan pesan dari aplikasi MDI biasa. Perhatikan bahwa GetMessage dapat mengembalikan -1 jika ada kesalahan.
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&
!TranslateAccelerator(hwndFrame, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
FungsiTranslateMDISysAccel menerjemahkan pesan WM_KEYDOWN ke dalam pesan WM_SYSCOMMAND dan mengirimkannya ke jendela anak MDI aktif. Jika pesan bukan pesan Akselerator MDI, fungsi mengembalikan FALSE, dalam hal ini aplikasi menggunakan fungsiTranslateAccelerator untuk menentukan apakah salah satu tombol akselerator yang ditentukan aplikasi ditekan. Jika tidak, loop tersebut mengirimkan pesan ke prosedur jendela yang sesuai.
Penulisan Prosedur Bingkai Jendela
Prosedur jendela untuk jendela bingkai MDI mirip dengan jendela utama aplikasi non-MDI. Perbedaannya adalah bahwa prosedur jendela bingkai meneruskan semua pesan yang tidak ditanganinya ke fungsiDefFrameProc daripada ke fungsiDefWindowProc. Selain itu, prosedur jendela bingkai juga harus meneruskan beberapa pesan yang ditanganinya, termasuk yang tercantum dalam tabel berikut.
| Pesan | Jawaban |
|---|---|
| WM_COMMAND | Mengaktifkan jendela anak MDI yang dipilih pengguna. Pesan ini dikirim ketika pengguna memilih jendela anak MDI dari menu jendela jendela bingkai MDI. Pengidentifikasi jendela yang menyertai pesan ini mengidentifikasi jendela anak MDI yang akan diaktifkan. |
| WM_MENUCHAR | Membuka menu jendela anak MDI yang sedang aktif saat pengguna menekan kombinasi tombol ALT+ – (minus). |
| WM_SETFOCUS | Mengarahkan fokus keyboard ke jendela klien MDI, yang kemudian meneruskannya ke jendela anak MDI yang aktif. |
| WM_SIZE | Mengubah ukuran jendela klien MDI agar pas di ruang kerja jendela kerangka baru. Jika prosedur jendela bingkai mengukur jendela klien MDI ke ukuran lain, jangan meneruskan pesan ke fungsi DefWindowProc. |
Prosedur jendela bingkai di Multipad disebut MPFrameWndProc. Penanganan pesan lain oleh MPFrameWndProc mirip dengan aplikasi non-MDI. WM_COMMAND pesan di Multipad ditangani oleh fungsi CommandHandler yang ditentukan secara lokal. Untuk pesan perintah yang tidak ditangani Multipad, CommandHandler memanggil fungsi DefFrameProc. Jika Multipad tidak menggunakan DefFrameProc secara default, pengguna tidak dapat mengaktifkan jendela anak dari menu jendela, karena pesan WM_COMMAND yang dikirim dengan mengklik item menu jendela akan hilang.
Menulis Prosedur Jendela Anak
Seperti prosedur jendela bingkai, prosedur jendela anak MDI menggunakan fungsi khusus untuk memproses pesan secara default. Semua pesan yang tidak ditangani prosedur jendela anak harus diteruskan ke fungsiDefMDIChildProc daripada ke fungsiDefWindowProc. Selain itu, beberapa pesan manajemen jendela harus diteruskan ke DefMDIChildProc, bahkan jika aplikasi menangani pesan, agar MDI berfungsi dengan benar. Berikut adalah pesan yang harus diteruskan aplikasi ke DefMDIChildProc.
| Pesan | Jawaban |
|---|---|
| WM_CHILDACTIVATE | Melakukan pemrosesan aktivasi ketika jendela anak MDI diubah ukurannya, dipindahkan, atau ditampilkan. Pesan ini harus diteruskan. |
| WM_GETMINMAXINFO | Menghitung ukuran jendela anak MDI yang dimaksimalkan, berdasarkan ukuran jendela klien MDI saat ini. |
| WM_MENUCHAR | Meneruskan pesan ke jendela frame MDI. |
| WM_MOVE | Menghitung ulang bilah gulir jendela klien MDI, jika ada. |
| WM_SETFOCUS | Mengaktifkan jendela anak, jika jendela anak MDI tersebut tidak aktif. |
| WM_SIZE | Melakukan operasi yang diperlukan untuk mengubah ukuran jendela, terutama untuk memaksimalkan atau memulihkan jendela anak MDI. Gagal meneruskan pesan ini ke fungsiDefMDIChildProc menghasilkan hasil yang sangat tidak diinginkan. |
| WM_SYSCOMMAND | Menangani perintah menu jendela (sebelumnya dikenal sebagai sistem): SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZE, dan SC_MAXIMIZE. |
Membuat Jendela Anak
Untuk membuat jendela anak MDI, aplikasi dapat memanggil fungsiCreateMDIWindow atau mengirim pesan WM_MDICREATE ke jendela klien MDI. (Aplikasi dapat menggunakan fungsiCreateWindowEx dengan gaya WS_EX_MDICHILD untuk membuat jendela anak MDI.) Aplikasi MDI utas tunggal dapat menggunakan salah satu metode untuk membuat jendela anak. Sebuah thread dalam aplikasi MDI multithreaded harus menggunakan fungsi CreateMDIWindow atau CreateWindowEx untuk membuat jendela anak dalam thread yang berbeda.
Parameter lParam dari pesan WM_MDICREATE adalah pointer jauh ke strukturMDICREATESTRUCT. Struktur mencakup empat anggota dimensi: x dan y, yang menunjukkan posisi horizontal dan vertikal jendela, dan cx dan cy, yang menunjukkan tingkat horizontal dan vertikal jendela. Salah satu anggota ini dapat ditetapkan secara eksplisit oleh aplikasi, atau mereka dapat diatur ke CW_USEDEFAULT, dalam hal ini sistem memilih posisi, ukuran, atau keduanya, sesuai dengan algoritma bertingkat. Bagaimanapun, keempat anggota harus diinisialisasi. Multipad menggunakan CW_USEDEFAULT untuk semua dimensi.
Anggota terakhir dari struktur MDICREATESTRUCT adalah anggota gaya, yang mungkin berisi bit gaya untuk jendela. Untuk membuat jendela anak MDI yang dapat memiliki kombinasi gaya jendela apa pun, tentukan gaya jendela MDIS_ALLCHILDSTYLES. Ketika gaya ini tidak ditentukan, jendela anak MDI memiliki gaya WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLL, dan WS_VSCROLL sebagai pengaturan default.
Multipad membuat jendela anak MDI-nya dengan menggunakan fungsi AddFile yang ditentukan secara lokal (terletak di file sumber MPFILE. C). Fungsi AddFile menetapkan judul jendela anak dengan menetapkan anggota szTitle dari struktur MDICREATESTRUCT ke nama file yang sedang diedit atau "Untitled." Anggota szClass diatur ke nama kelas jendela anak MDI yang terdaftar dalam fungsi InitializeApplication dari Multipad. Anggota hOwner diatur ke handle instans aplikasi.
Contoh berikut menunjukkan fungsi AddFile di Multipad.
HWND APIENTRY AddFile(pName)
TCHAR * pName;
{
HWND hwnd;
TCHAR sz[160];
MDICREATESTRUCT mcs;
if (!pName)
{
// If the pName parameter is NULL, load the "Untitled"
// string from the STRINGTABLE resource and set the szTitle
// member of MDICREATESTRUCT.
LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz)/sizeof(TCHAR));
mcs.szTitle = (LPCTSTR) sz;
}
else
// Title the window with the full path and filename,
// obtained by calling the OpenFile function with the
// OF_PARSE flag, which is called before AddFile().
mcs.szTitle = of.szPathName;
mcs.szClass = szChild;
mcs.hOwner = hInst;
// Use the default size for the child window.
mcs.x = mcs.cx = CW_USEDEFAULT;
mcs.y = mcs.cy = CW_USEDEFAULT;
// Give the child window the default style. The styleDefault
// variable is defined in MULTIPAD.C.
mcs.style = styleDefault;
// Tell the MDI client window to create the child window.
hwnd = (HWND) SendMessage (hwndMDIClient, WM_MDICREATE, 0,
(LONG) (LPMDICREATESTRUCT) &mcs);
// If the file is found, read its contents into the child
// window's client area.
if (pName)
{
if (!LoadFile(hwnd, pName))
{
// Cannot load the file; close the window.
SendMessage(hwndMDIClient, WM_MDIDESTROY,
(DWORD) hwnd, 0L);
}
}
return hwnd;
}
Pointer yang diteruskan dalam parameter lParam dari pesan WM_MDICREATE diteruskan ke fungsi CreateWindow dan muncul sebagai anggota pertama dalam struktur CREATESTRUCT, yang diteruskan dalam pesan WM_CREATE. Di Multipad, jendela anak menginisialisasi dirinya sendiri selama pemrosesan pesan WM_CREATE dengan menginisialisasi variabel dokumen dalam data tambahannya dan dengan membuat jendela anak kontrol edit.