Bagikan melalui


TN021: Perutean Perintah dan Pesan

Catatan

Catatan teknis berikut belum diperbarui sejak pertama kali disertakan dalam dokumentasi online. Akibatnya, beberapa prosedur dan topik mungkin kedaluarsa atau salah. Untuk informasi terbaru, disarankan agar Anda mencari topik yang menarik dalam indeks dokumentasi online.

Catatan ini menjelaskan perutean perintah dan arsitektur pengiriman serta topik tingkat lanjut dalam perutean pesan jendela umum.

Silakan lihat Visual C++ untuk detail umum tentang arsitektur yang dijelaskan di sini, terutama perbedaan antara pesan Windows, pemberitahuan kontrol, dan perintah. Catatan ini mengasumsikan Anda sangat terbiasa dengan masalah yang dijelaskan dalam dokumentasi cetak dan hanya membahas topik yang sangat canggih.

Perutean Perintah dan Pengiriman Fungsionalitas MFC 1.0 Berkembang ke Arsitektur MFC 2.0

Windows memiliki pesan WM_COMMAND yang kelebihan beban untuk memberikan pemberitahuan perintah menu, kunci akselerator, dan pemberitahuan kontrol dialog.

MFC 1.0 dibangun di atasnya sedikit dengan memungkinkan handler perintah (misalnya, "OnFileNew") di CWnd kelas turunan untuk dipanggil sebagai respons terhadap WM_COMMAND tertentu. Ini direkatkan bersama dengan struktur data yang disebut peta pesan, dan menghasilkan mekanisme perintah yang sangat hemat ruang.

MFC 1.0 juga menyediakan fungsionalitas tambahan untuk memisahkan pemberitahuan kontrol dari pesan perintah. Perintah diwakili oleh ID 16-bit, terkadang dikenal sebagai ID Perintah. Perintah biasanya dimulai dari CFrameWnd (yaitu, menu pilih atau akselerator yang diterjemahkan) dan dirutekan ke berbagai jendela lain.

MFC 1.0 menggunakan perutean perintah dalam arti terbatas untuk implementasi Multiple Document Interface (MDI). (Jendela bingkai MDI mendelegasikan perintah ke jendela MDI Child aktifnya.)

Fungsionalitas ini telah digeneralisasi dan diperluas di MFC 2.0 untuk memungkinkan perintah ditangani oleh rentang objek yang lebih luas (bukan hanya objek jendela). Ini menyediakan arsitektur yang lebih formal dan dapat diperluas untuk merutekan pesan dan menggunakan kembali perutean target perintah untuk tidak hanya menangani perintah, tetapi juga untuk memperbarui objek UI (seperti item menu dan tombol toolbar) untuk mencerminkan ketersediaan perintah saat ini.

ID Perintah

Lihat Visual C++ untuk penjelasan tentang proses perutean dan pengikatan perintah. Catatan Teknis 20 berisi informasi tentang penamaan ID.

Kami menggunakan awalan generik "ID_" untuk ID perintah. ID perintah adalah >= 0x8000. Baris pesan atau bilah status akan menampilkan string deskripsi perintah jika ada sumber daya STRINGTABLE dengan ID yang sama dengan ID perintah.

Di sumber daya aplikasi Anda, ID perintah dapat muncul di beberapa tempat:

  • Dalam satu sumber daya STRINGTABLE yang memiliki ID yang sama dengan prompt baris pesan.

  • Mungkin banyak sumber daya MENU yang dilampirkan ke item menu yang memanggil perintah yang sama.

  • (ADVANCED) dalam tombol dialog untuk perintah GOSUB.

Dalam kode sumber aplikasi Anda, ID perintah dapat muncul di beberapa tempat:

  • Di SUMBER DAYA Anda. H (atau file header simbol utama lainnya) untuk menentukan ID perintah khusus aplikasi.

  • MUNGKIN Dalam array ID yang digunakan untuk membuat toolbar.

  • Dalam makro ON_COMMAND.

  • MUNGKIN Dalam makro ON_UPDATE_COMMAND_UI.

Saat ini, satu-satunya implementasi dalam MFC yang memerlukan ID perintah adalah >= 0x8000 adalah implementasi dialog/perintah GOSUB.

Perintah GOSUB, Menggunakan Arsitektur Perintah dalam Dialog

Arsitektur perintah perutean dan pengaktifan perintah berfungsi dengan baik dengan jendela bingkai, item menu, tombol toolbar, tombol bilah dialog, bilah kontrol lainnya, dan elemen antarmuka pengguna lainnya yang dirancang untuk memperbarui perintah permintaan dan rute atau ID kontrol ke target perintah utama (biasanya jendela bingkai utama). Target perintah utama tersebut dapat merutekan pemberitahuan perintah atau kontrol ke objek target perintah lain yang sesuai.

Dialog (modal atau modeless) dapat memperoleh manfaat dari beberapa fitur arsitektur perintah jika Anda menetapkan ID kontrol kontrol dialog ke ID perintah yang sesuai. Dukungan untuk dialog tidak otomatis, jadi Anda mungkin harus menulis beberapa kode tambahan.

Perhatikan bahwa agar semua fitur ini berfungsi dengan baik, ID perintah Anda harus >= 0x8000. Karena banyak dialog bisa dirutekan ke bingkai yang sama, perintah bersama harus >= 0x8000, sementara IDC yang tidak dibagikan dalam dialog tertentu harus <= 0x7FFF.

Anda dapat menempatkan tombol normal dalam dialog modal normal dengan IDC tombol yang diatur ke ID perintah yang sesuai. Saat pengguna memilih tombol , pemilik dialog (biasanya jendela bingkai utama) mendapatkan perintah seperti perintah lainnya. Ini disebut perintah GOSUB karena biasanya digunakan untuk memunculkan dialog lain (GOSUB dialog pertama).

Anda juga dapat memanggil fungsi CWnd::UpdateDialogControls pada dialog Anda dan meneruskannya alamat jendela bingkai utama Anda. Fungsi ini akan mengaktifkan atau menonaktifkan kontrol dialog Anda berdasarkan apakah mereka memiliki penangan perintah dalam bingkai. Fungsi ini dipanggil secara otomatis untuk Anda untuk bilah kontrol di perulangan diam aplikasi Anda, tetapi Anda harus memanggilnya secara langsung untuk dialog normal yang Anda inginkan untuk memiliki fitur ini.

Saat ON_UPDATE_COMMAND_UI Dipanggil

Mempertahankan status diaktifkan/diperiksa dari semua item menu program sepanjang waktu dapat menjadi masalah yang mahal secara komputasi. Teknik umumnya adalah mengaktifkan/memeriksa item menu hanya ketika pengguna memilih POPUP. Implementasi CFrameWnd MFC 2.0 menangani pesan WM_INITMENUPOPUP dan menggunakan arsitektur perutean perintah untuk menentukan status menu melalui penangan ON_UPDATE_COMMAND_UI.

CFrameWnd juga menangani pesan WM_ENTERIDLE untuk menjelaskan item menu saat ini yang dipilih pada bilah status (juga dikenal sebagai baris pesan).

Struktur menu aplikasi, yang diedit oleh Visual C++, digunakan untuk mewakili perintah potensial yang tersedia pada waktu WM_INITMENUPOPUP. ON_UPDATE_COMMAND_UI handler dapat memodifikasi status atau teks menu, atau untuk penggunaan tingkat lanjut (seperti daftar File MRU atau menu pop-up Kata Kerja OLE), benar-benar memodifikasi struktur menu sebelum menu digambar.

Jenis pemrosesan ON_UPDATE_COMMAND_UI yang sama dilakukan untuk toolbar (dan bilah kontrol lainnya) ketika aplikasi memasuki perulangan diamnya. Lihat Referensi Pustaka Kelas dan Catatan Teknis 31 untuk informasi selengkapnya tentang bilah kontrol.

Menu Pop-up Berlapis

Jika Anda menggunakan struktur menu berlapis, Anda akan melihat bahwa handler ON_UPDATE_COMMAND_UI untuk item menu pertama di menu pop-up dipanggil dalam dua kasus yang berbeda.

Pertama, dipanggil untuk menu pop-up itu sendiri. Ini diperlukan karena menu pop-up tidak memiliki ID dan kami menggunakan ID item menu pertama dari menu pop-up untuk merujuk ke seluruh menu pop-up. Dalam hal ini, variabel anggota m_pSubMenu objek CCmdUI akan menjadi non-NULL dan akan menunjuk ke menu pop-up.

Kedua, dipanggil tepat sebelum item menu di menu pop-up akan digambar. Dalam hal ini, ID hanya merujuk ke item menu pertama dan variabel anggota m_pSubMenu objek CCmdUI akan NULL.

Ini memungkinkan Anda mengaktifkan menu pop-up yang berbeda dari item menunya, tetapi mengharuskan Anda menulis beberapa kode sadar menu. Misalnya, di menu berlapis dengan struktur berikut:

File>
    New>
    Sheet (ID_NEW_SHEET)
    Chart (ID_NEW_CHART)

Perintah ID_NEW_SHEET dan ID_NEW_CHART dapat diaktifkan atau dinonaktifkan secara independen. Menu pop-up Baru harus diaktifkan jika salah satu dari keduanya diaktifkan.

Handler perintah untuk ID_NEW_SHEET (perintah pertama dalam pop-up) akan terlihat seperti:

void CMyApp::OnUpdateNewSheet(CCmdUI* pCmdUI)
{
    if (pCmdUI->m_pSubMenu != NULL)
    {
        // enable entire pop-up for "New" sheet and chart
        BOOL bEnable = m_bCanCreateSheet || m_bCanCreateChart;
        // CCmdUI::Enable is a no-op for this case, so we
        // must do what it would have done.
        pCmdUI->m_pMenu->EnableMenuItem(pCmdUI->m_nIndex,
            MF_BYPOSITION |
            (bEnable  MF_ENABLED : (MF_DISABLED | MF_GRAYED)));

        return;
    }
    // otherwise just the New Sheet command
    pCmdUI->Enable(m_bCanCreateSheet);
}

Handler perintah untuk ID_NEW_CHART akan menjadi handler perintah pembaruan normal dan terlihat seperti:

void CMyApp::OnUpdateNewChart(CCmdUI* pCmdUI)
{
    pCmdUI->Enable(m_bCanCreateChart);
}

ON_COMMAND dan ON_BN_CLICKED

Makro peta pesan untuk ON_COMMAND dan ON_BN_CLICKED sama. Perintah MFC dan mekanisme perutean pemberitahuan kontrol hanya menggunakan ID perintah untuk memutuskan ke mana harus merutekan. Pemberitahuan kontrol dengan kode pemberitahuan kontrol nol (BN_CLICKED) ditafsirkan sebagai perintah.

Catatan

Bahkan, semua pesan pemberitahuan kontrol melalui rantai handler perintah. Misalnya, secara teknis dimungkinkan bagi Anda untuk menulis penangan pemberitahuan kontrol untuk EN_CHANGE di kelas dokumen Anda. Ini umumnya tidak disarankan karena aplikasi praktis fitur ini sedikit, fitur ini tidak didukung oleh ClassWizard, dan penggunaan fitur dapat mengakibatkan kode yang rapuh.

Menonaktifkan Penonaktifan Otomatis Kontrol Tombol

Jika Anda menempatkan kontrol tombol pada bilah dialog, atau dalam dialog menggunakan tempat Anda memanggil CWnd::UpdateDialogControls sendiri, Anda akan melihat bahwa tombol yang tidak memiliki penangan ON_COMMAND atau ON_UPDATE_COMMAND_UI akan secara otomatis dinonaktifkan untuk Anda oleh kerangka kerja. Dalam beberapa kasus, Anda tidak perlu memiliki handler, tetapi Anda akan ingin tombol tetap diaktifkan. Cara term mudah untuk mencapainya adalah dengan menambahkan handler perintah dummy (mudah dilakukan dengan ClassWizard) dan tidak melakukan apa pun di dalamnya.

Perutean Pesan Jendela

Berikut ini menjelaskan beberapa topik yang lebih canggih pada kelas MFC dan bagaimana perutean pesan Windows dan topik lainnya memengaruhinya. Informasi di sini hanya dijelaskan secara singkat. Lihat Referensi Pustaka Kelas untuk detail tentang API publik. Silakan lihat kode sumber pustaka MFC untuk informasi selengkapnya tentang detail implementasi.

Silakan merujuk ke Catatan Teknis 17 untuk detail tentang pembersihan Jendela, topik yang sangat penting untuk semua kelas turunan CWnd.

Masalah CWnd

Fungsi anggota implementasi CWnd::OnChildNotify menyediakan arsitektur yang kuat dan dapat diperluas untuk jendela anak (juga dikenal sebagai kontrol) untuk menghubungkan atau diberi tahu tentang pesan, perintah, dan pemberitahuan kontrol yang masuk ke induk mereka (atau "pemilik"). Jika jendela anak (/control) adalah objek C++ CWnd itu sendiri, fungsi virtual OnChildNotify dipanggil terlebih dahulu dengan parameter dari pesan asli (yaitu, struktur MSG). Jendela anak dapat meninggalkan pesan sendirian, memakannya, atau memodifikasi pesan untuk induk (langka).

Implementasi CWnd default menangani pesan berikut dan menggunakan hook OnChildNotify untuk memungkinkan jendela anak (kontrol) mengakses terlebih dahulu pada pesan:

  • WM_MEASUREITEM dan WM_DRAWITEM (untuk menggambar sendiri)

  • WM_COMPAREITEM dan WM_DELETEITEM (untuk menggambar sendiri)

  • WM_HSCROLL dan WM_VSCROLL

  • WM_CTLCOLOR

  • WM_PARENTNOTIFY

Anda akan melihat hook OnChildNotify digunakan untuk mengubah pesan gambar pemilik menjadi pesan gambar mandiri.

Selain hook OnChildNotify , pesan gulir memiliki perilaku perutean lebih lanjut. Silakan lihat di bawah ini untuk detail selengkapnya tentang bilah gulir dan sumber pesan WM_HSCROLL dan WM_VSCROLL .

Masalah CFrameWnd

Kelas CFrameWnd menyediakan sebagian besar perutean perintah dan implementasi pembaruan antarmuka pengguna. Ini terutama digunakan untuk jendela bingkai utama aplikasi (CWinApp::m_pMainWnd) tetapi berlaku untuk semua jendela bingkai.

Jendela bingkai utama adalah jendela dengan bilah menu dan merupakan induk bilah status atau baris pesan. Silakan lihat diskusi di atas tentang perutean perintah dan WM_INITMENUPOPUP.

Kelas CFrameWnd menyediakan manajemen tampilan aktif. Pesan berikut dirutekan melalui tampilan aktif:

  • Semua pesan perintah (tampilan aktif mendapatkan akses pertama ke pesan tersebut).

  • WM_HSCROLL dan WM_VSCROLL pesan dari bilah gulir saudara (lihat di bawah).

  • WM_ACTIVATE (dan WM_MDIACTIVATE untuk MDI) diubah menjadi panggilan ke fungsi virtual CView::OnActivateView.

Masalah CMDIFrameWnd/CMDIChildWnd

Kedua kelas jendela bingkai MDI berasal dari CFrameWnd dan oleh karena itu keduanya diaktifkan untuk jenis perutean perintah yang sama dan pembaruan antarmuka pengguna yang disediakan di CFrameWnd. Dalam aplikasi MDI biasa, hanya jendela bingkai utama (yaitu, objek CMDIFrameWnd ) yang memegang bilah menu dan bilah status dan oleh karena itu adalah sumber utama implementasi perutean perintah.

Skema perutean umum adalah bahwa jendela anak MDI aktif mendapatkan akses pertama ke perintah. Fungsi PreTranslateMessage default menangani tabel akselerator untuk jendela anak MDI (pertama) dan bingkai MDI (kedua) serta akselerator perintah sistem MDI standar yang biasanya ditangani oleh TranslateMDISysAccel (terakhir).

Masalah Bilah Gulir

Saat menangani pesan gulir (WM_HSCROLL/OnHScroll dan/atau WM_VSCROLL/OnVScroll), Anda harus mencoba menulis kode handler sehingga tidak bergantung pada dari mana pesan bilah gulir berasal. Ini bukan hanya masalah Windows umum, karena pesan gulir dapat berasal dari kontrol bilah gulir benar atau dari WS_HSCROLL/WS_VSCROLL bilah gulir yang bukan kontrol bilah gulir.

MFC memperluasnya untuk memungkinkan kontrol bilah gulir menjadi anak atau saudara dari jendela yang sedang digulir (sebenarnya, hubungan induk/anak antara bilah gulir dan jendela yang digulir bisa apa saja). Ini sangat penting untuk bilah gulir bersama dengan jendela pemisah. Silakan lihat Catatan Teknis 29 untuk detail tentang implementasi CSplitterWnd termasuk informasi selengkapnya tentang masalah bilah gulir bersama.

Di catatan samping, ada dua kelas turunan CWnd di mana gaya bilah gulir yang ditentukan pada waktu pembuatan terperangkap dan tidak diteruskan ke Windows. Ketika diteruskan ke rutinitas pembuatan, WS_HSCROLL dan WS_VSCROLL dapat diatur secara independen, tetapi setelah pembuatan tidak dapat diubah. Tentu saja, Anda tidak boleh langsung menguji atau mengatur bit gaya WS_SCROLL jendela yang mereka buat.

Untuk CMDIFrameWdi gaya bilah gulir yang Anda teruskan ke Buat atau LoadFrame digunakan untuk membuat MDICLIENT. Jika Anda ingin memiliki area MDICLIENT yang dapat digulir (seperti Manajer Program Windows) pastikan untuk mengatur kedua gaya bilah gulir (WS_HSCROLL | WS_VSCROLL) untuk gaya yang digunakan untuk membuat CMDIFrameWnd.

Untuk CSplitterWnd gaya bilah gulir berlaku untuk bilah gulir bersama khusus untuk wilayah pemisah. Untuk jendela pemisah statis, Anda biasanya tidak akan mengatur gaya bilah gulir. Untuk jendela pemisah dinamis, Anda biasanya akan mengatur gaya bilah gulir untuk arah yang akan Anda pisahkan, Yaitu, WS_HSCROLL jika Anda dapat memisahkan baris, WS_VSCROLL jika Anda dapat membagi kolom.

Baca juga

Catatan Teknis menurut Angka
Catatan Teknis menurut Kategori