TN021: Komut ve İleti Yönlendirme
Dekont
Aşağıdaki teknik not, çevrimiçi belgelere ilk kez eklendiğinden beri güncelleştirilmemiştir. Sonuç olarak, bazı yordamlar ve konular güncel olmayabilir veya yanlış olabilir. En son bilgiler için, çevrimiçi belge dizininde ilgilendiğiniz konuyu aramanız önerilir.
Bu not, komut yönlendirme ve gönderme mimarisinin yanı sıra genel pencere iletisi yönlendirmesindeki gelişmiş konuları açıklar.
Burada açıklanan mimarilerle ilgili genel ayrıntılar için, özellikle Windows iletileri, denetim bildirimleri ve komutlar arasındaki ayrım için visual C++ bölümüne bakın. Bu not, yazdırılan belgelerde açıklanan sorunları çok iyi bildiğinizi varsayar ve yalnızca çok gelişmiş konuları ele alır.
Komut Yönlendirme ve Gönderme MFC 1.0 İşlevselliği MFC 2.0 Mimarisine Evriliyor
Windows menü komutlarının, hızlandırıcı tuşlarının ve iletişim kutusu denetimi bildirimlerinin bildirimlerini sağlamak için aşırı yüklenmiş WM_COMMAND iletisine sahiptir.
Türetilmiş bir sınıftaki bir komut işleyicinin (örneğin, "OnFileNew") belirli bir CWnd
WM_COMMAND yanıt olarak çağrılmasına izin vererek bunun üzerinde biraz yerleşik MFC 1.0. Bu, ileti eşlemesi adı verilen bir veri yapısıyla birlikte yapıştırılır ve çok alan açısından verimli bir komut mekanizmasına neden olur.
MFC 1.0, denetim bildirimlerini komut iletilerinden ayırmak için ek işlevsellik de sağladı. Komutlar, bazen Komut Kimliği olarak da bilinen 16 bit kimlikle temsil edilir. Komutlar normalde bir CFrameWnd
menü seçiminden veya çevrilmiş hızlandırıcıdan başlar ve çeşitli diğer pencerelere yönlendirilir.
MFC 1.0, Birden Çok Belge Arabiriminin (MDI) uygulanması için sınırlı bir anlamda komut yönlendirmeyi kullandı. (MDI çerçeve penceresi komutları etkin MDI Alt penceresine temsilci olarak verir.)
Bu işlev MFC 2.0'da komutların daha geniş bir nesne aralığı (yalnızca pencere nesneleri tarafından değil) tarafından işlenmesine izin vermek için genelleştirilmiş ve genişletilmiştir. İletileri yönlendirmek için daha resmi ve genişletilebilir bir mimari sağlar ve komut hedef yönlendirmesini yalnızca komutları işlemek için değil, aynı zamanda ui nesnelerini (menü öğeleri ve araç çubuğu düğmeleri gibi) komutun geçerli kullanılabilirliğini yansıtacak şekilde güncelleştirmek için de yeniden kullanır.
Komut Kimlikleri
Komut yönlendirme ve bağlama işleminin açıklaması için bkz. Visual C++ . Teknik Not 20 , kimlik adlandırma hakkında bilgi içerir.
Komut kimlikleri için "ID_" genel ön ekini kullanırız. Komut kimlikleri >= 0x8000. komut kimliğiyle aynı kimliklere sahip bir STRINGTABLE kaynağı varsa, ileti satırı veya durum çubuğu komut açıklaması dizesini gösterir.
Uygulamanızın kaynaklarında çeşitli yerlerde bir komut kimliği görüntüleyebilir:
İleti satırı istemiyle aynı kimliği olan bir STRINGTABLE kaynağında.
Aynı komutu çağıran menü öğelerine eklenmiş olan büyük olasılıkla birçok MENU kaynağında.
(GELİşMİş) bir GOSUB komutu için bir iletişim kutusu düğmesinde.
Uygulamanızın kaynak kodunda birkaç yerde bir komut kimliği görüntüleyebilir:
KAYNAĞınızda. Uygulamaya özgü komut kimliklerini tanımlamak için H (veya diğer ana simge üst bilgi dosyası).
BELKI araç çubuğu oluşturmak için kullanılan bir kimlik dizisinde.
ON_COMMAND makroda.
BELKI ON_UPDATE_COMMAND_UI bir makroda.
Şu anda MFC'de komut kimlikleri >gerektiren tek uygulama = 0x8000 GOSUB iletişim kutularının/komutlarının uygulanmasıdır.
GOSUB Komutları, İletişim Kutularındaki Komut Mimarisini Kullanma
Komutları yönlendirme ve etkinleştirme komut mimarisi, isteğe bağlı olarak güncelleştirmek ve komutları yönlendirmek veya kimlikleri bir ana komut hedefine (genellikle ana çerçeve penceresi) yönlendirmek için tasarlanmış çerçeve pencereleri, menü öğeleri, araç çubuğu düğmeleri, iletişim çubuğu düğmeleri, diğer denetim çubukları ve diğer kullanıcı arabirimi öğeleriyle iyi çalışır. Bu ana komut hedefi, komutu veya denetim bildirimlerini diğer komut hedef nesnelerine uygun şekilde yönlendirebilir.
İletişim kutusu denetiminin denetim kimliğini uygun komut kimliğine atarsanız, iletişim kutusu (kalıcı veya modsuz) komut mimarisinin bazı özelliklerinden yararlanabilir. İletişim kutuları için destek otomatik değildir, bu nedenle bazı ek kodlar yazmanız gerekebilir.
Tüm bu özelliklerin düzgün çalışması için komut kimliklerinizin = 0x8000 olması >gerektiğini unutmayın. Birçok iletişim kutusu aynı çerçeveye yönlendirilebileceği için, paylaşılan komutlar = 0x8000, >belirli bir iletişim kutusundaki paylaşılmayan IDC'ler ise = 0x7FFF olmalıdır <.
Normal bir düğmeyi, düğmenin IDC'si uygun komut kimliğine ayarlanmış şekilde normal bir kalıcı iletişim kutusuna yerleştirebilirsiniz. Kullanıcı düğmeyi seçtiğinde, iletişim kutusunun sahibi (genellikle ana çerçeve penceresi) komutu diğer komutlar gibi alır. Bu, genellikle başka bir iletişim kutusu (ilk iletişim kutusunun GOSUB'ı) getirmek için kullanıldığından goSUB komutu olarak adlandırılır.
ayrıca, iletişim kutunuzdaki işlevi CWnd::UpdateDialogControls
çağırabilir ve ana çerçeve pencerenizin adresini geçirebilirsiniz. Bu işlev, çerçevede komut işleyicileri olup olmadığına bağlı olarak iletişim kutusu denetimlerinizi etkinleştirir veya devre dışı bırakır. Bu işlev, uygulamanızın boşta döngüsündeki denetim çubukları için sizin için otomatik olarak çağrılır, ancak bu özelliğe sahip olmak istediğiniz normal iletişim kutuları için bunu doğrudan çağırmanız gerekir.
ON_UPDATE_COMMAND_UI Çağrıldığında
Bir programın menü öğelerinin her zaman etkin/denetlenmiş durumunu korumak, hesaplama açısından pahalı bir sorun olabilir. Yaygın bir teknik, menü öğelerini yalnızca kullanıcı POPUP'ı seçtiğinde etkinleştirmek/denetlemektir. MFC 2.0 uygulaması CFrameWnd
WM_INITMENUPOPUP iletisini işler ve ON_UPDATE_COMMAND_UI işleyiciler aracılığıyla menülerin durumlarını belirlemek için komut yönlendirme mimarisini kullanır.
CFrameWnd
ayrıca durum çubuğunda (ileti satırı olarak da bilinir) seçilen geçerli menü öğesini açıklamak için WM_ENTERIDLE iletisini işler.
Visual C++ tarafından düzenlenen bir uygulamanın menü yapısı, WM_INITMENUPOPUP anda kullanılabilen olası komutları temsil etmek için kullanılır. ON_UPDATE_COMMAND_UI işleyicileri bir menünün durumunu veya metnini değiştirebilir veya gelişmiş kullanımlar için (Dosya MRU listesi veya OLE Fiilleri açılır menüsü gibi) menü çizilmeden önce menü yapısını değiştirebilir.
Uygulama boşta döngüsüne girdiğinde araç çubukları (ve diğer denetim çubukları) için aynı tür ON_UPDATE_COMMAND_UI işleme yapılır. Denetim çubukları hakkında daha fazla bilgi için bkz. Sınıf Kitaplığı Başvurusu ve Teknik Not 31.
İç İçe Açılan Menüler
İç içe menü yapısı kullanıyorsanız, açılır menüdeki ilk menü öğesi için ON_UPDATE_COMMAND_UI işleyicisinin iki farklı durumda çağrıldığını fark edeceksiniz.
İlk olarak, açılır menünün kendisi için çağrılır. Açılır menülerde kimlik olmadığından ve açılır menünün tamamına başvurmak için açılır menünün ilk menü öğesinin kimliğini kullandığımızdan bu gereklidir. Bu durumda, nesnenin m_pSubMenu üye değişkeni CCmdUI
NULL olmayan bir değişken olur ve açılır menüye işaret eder.
İkincisi, açılır menüdeki menü öğelerinin çizilmesinden hemen önce çağrılır. Bu durumda, kimlik yalnızca ilk menü öğesine başvurur ve nesnenin m_pSubMenu üye değişkeni CCmdUI
NULL olur.
Bu, açılır menüyü menü öğelerinden ayrı olarak etkinleştirmenize olanak tanır, ancak menüye duyarlı bir kod yazmanızı gerektirir. Örneğin, aşağıdaki yapıya sahip iç içe yerleştirilmiş bir menüde:
File>
New>
Sheet (ID_NEW_SHEET)
Chart (ID_NEW_CHART)
ID_NEW_SHEET ve ID_NEW_CHART komutları bağımsız olarak etkinleştirilebilir veya devre dışı bırakılabilir. İki menüden biri etkinse Yeni açılır menüsü etkinleştirilmelidir.
ID_NEW_SHEET komut işleyicisi (açılır penceredeki ilk komut) şuna benzer olacaktır:
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);
}
ID_NEW_CHART için komut işleyicisi normal bir güncelleştirme komut işleyicisi olabilir ve şuna benzer olacaktır:
void CMyApp::OnUpdateNewChart(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bCanCreateChart);
}
ON_COMMAND ve ON_BN_CLICKED
ON_COMMAND ve ON_BN_CLICKED için ileti eşleme makroları aynıdır. MFC komut ve denetim bildirimi yönlendirme mekanizması, nereye yönlendirileceğine karar vermek için yalnızca komut kimliğini kullanır. Denetim bildirimi kodu sıfır (BN_CLICKED) olan denetim bildirimleri komut olarak yorumlanır.
Dekont
Aslında, tüm denetim bildirim iletileri komut işleyici zincirinden geçer. Örneğin, belge sınıfınızdaki EN_CHANGE için bir denetim bildirimi işleyicisi yazmanız teknik olarak mümkündür. Bu özelliğin pratik uygulamaları az olduğundan, bu özellik ClassWizard tarafından desteklenmediğinden ve özelliğin kullanılması kırılgan koda neden olabileceğinden bu genellikle önerilmez.
Düğme Denetimlerinin Otomatik Devre Dışı Bırakıldığında Devre Dışı Bırakılması
Bir iletişim kutusu çubuğuna veya CWnd::UpdateDialogControls'i kendi başınıza çağırdığınız bir iletişim kutusuna düğme denetimi yerleştirirseniz, ON_COMMAND veya ON_UPDATE_COMMAND_UI işleyicisi olmayan düğmelerin çerçeve tarafından sizin için otomatik olarak devre dışı bırakılacağını fark edeceksiniz. Bazı durumlarda işleyiciniz olması gerekmez, ancak düğmenin etkin kalmasını istersiniz. Bunu başarmak için en kolay yol, sahte bir komut işleyicisi eklemektir (ClassWizard ile kolayca yapılır) ve içinde hiçbir şey yapmaz.
Pencere İletisi Yönlendirme
Aşağıda, MFC sınıfları ve Windows ileti yönlendirmesi ile diğer konuların bunları nasıl etkilediği hakkında daha gelişmiş bazı konular açıklanmaktadır. Buradaki bilgiler yalnızca kısa bir süre açıklanmaktadır. Genel API'ler hakkındaki ayrıntılar için Sınıf Kitaplığı Başvurusu'na bakın. Uygulama ayrıntıları hakkında daha fazla bilgi için lütfen MFC kitaplığı kaynak koduna bakın.
Tüm CWnd türetilmiş sınıflar için çok önemli bir konu olan Pencere temizleme hakkındaki ayrıntılar için lütfen Teknik Not 17'ye bakın.
CWnd Sorunları
Uygulama üyesi işlevi CWnd::OnChildNotify , alt pencerelerin (denetimler olarak da bilinir) üst öğelerine (veya "sahiplerine") giden iletiler, komutlar ve denetim bildirimleri hakkında bilgilendirilmesi için güçlü ve genişletilebilir bir mimari sağlar. Alt pencere (/denetim) bir C++ CWnd nesnesiyse, ilk olarak özgün iletideki parametrelerle (bir MSG yapısı) OnChildNotify sanal işlevi çağrılır. Alt pencere iletiyi yalnız bırakabilir, yiyebilir veya üst öğe için iletiyi değiştirebilir (nadir).
Varsayılan CWnd uygulaması aşağıdaki iletileri işler ve alt pencerelerin (denetimler) iletiye ilk kez erişmesine izin vermek için OnChildNotify kancasını kullanır:
WM_MEASUREITEM ve WM_DRAWITEM (kendi kendine çizim için)
WM_COMPAREITEM ve WM_DELETEITEM (kendi kendine çizim için)
WM_HSCROLL ve WM_VSCROLL
WM_CTLCOLOR
WM_PARENTNOTIFY
OnChildNotify kancasının sahip çizim iletilerini kendi kendine çizen iletilere değiştirmek için kullanıldığını fark edeceksiniz.
OnChildNotify kancasına ek olarak, kaydırma iletilerinin daha fazla yönlendirme davranışı vardır. Kaydırma çubukları ve WM_HSCROLL ve WM_VSCROLL iletilerinin kaynakları hakkında daha fazla bilgi için lütfen aşağıya bakın.
CFrameWnd Sorunları
CFrameWnd sınıfı, komut yönlendirme ve kullanıcı arabirimi güncelleştirme uygulamasının çoğunu sağlar. Bu öncelikle uygulamanın ana çerçeve penceresi (CWinApp::m_pMainWnd) için kullanılır, ancak tüm çerçeve pencereleri için geçerlidir.
Ana çerçeve penceresi, menü çubuğunu içeren penceredir ve durum çubuğunun veya ileti satırının üst öğesidir. Lütfen komut yönlendirme ve WM_INITMENUPOPUP ile ilgili yukarıdaki tartışmaya bakın.
CFrameWnd sınıfı etkin görünümün yönetimini sağlar. Aşağıdaki iletiler etkin görünüm üzerinden yönlendirilir:
Tüm komut iletileri (etkin görünüm bunlara ilk erişimi alır).
Eşdüzey kaydırma çubuklarından iletileri WM_HSCROLL ve WM_VSCROLL (aşağıya bakın).
WM_ACTIVATE (ve MDI için WM_MDIACTIVATE) CView::OnActivateView sanal işlevine çağrılara dönüştürülür.
CMDIFrameWnd/CMDIChildWnd Sorunları
Her iki MDI çerçeve penceresi sınıfı da CFrameWnd'den türetilir ve bu nedenle her ikisi de CFrameWnd'de sağlanan aynı türdeki komut yönlendirme ve kullanıcı arabirimi güncelleştirmesi için etkinleştirilir. Tipik bir MDI uygulamasında, menü çubuğunu ve durum çubuğunu yalnızca ana çerçeve penceresi ( CMDIFrameWnd nesnesi) tutar ve bu nedenle komut yönlendirme uygulamasının ana kaynağıdır.
Genel yönlendirme düzeni, etkin MDI alt penceresinin komutlara ilk kez erişmesidir. Varsayılan PreTranslateMessage işlevleri, hem MDI alt pencereleri (ilk) hem de MDI çerçevesi (ikinci) için hızlandırıcı tablolarının yanı sıra normalde TranslateMDISysAccel (son) tarafından işlenen standart MDI sistem-komut hızlandırıcılarını işler.
Kaydırma Çubuğu Sorunları
Kaydırma iletisini işlerken (WM_HSCROLL/OnHScroll ve/veya WM_VSCROLL/OnVScroll), kaydırma çubuğu iletisinin nereden geldiğine güvenmemesi için işleyici kodunu yazmaya çalışmanız gerekir. Kaydırma iletileri gerçek kaydırma çubuğu denetimlerinden veya kaydırma çubuğu denetimleri olmayan WS_HSCROLL WS_VSCROLL/ kaydırma çubuklarından gelebildiğinden, bu yalnızca genel bir Windows sorunu değildir.
MFC, kaydırma çubuğu denetimlerinin kaydırılan pencerenin alt veya eşdüzeyleri olmasını sağlayacak şekilde genişletir (aslında, kaydırma çubuğu ile kaydırılan pencere arasındaki üst/alt ilişki herhangi bir şey olabilir). Bu özellikle bölünmüş pencereli paylaşılan kaydırma çubukları için önemlidir. Paylaşılan kaydırma çubuğu sorunları hakkında daha fazla bilgi de dahil olmak üzere CSplitterWnd uygulamasıyla ilgili ayrıntılar için lütfen Teknik Not 29'a bakın.
Yan notta, oluşturma zamanında belirtilen kaydırma çubuğu stillerinin tuzağa düşürüldüğü ve Windows'a geçirilmediği iki CWnd türetilmiş sınıfı vardır. Oluşturma yordamına geçirildiğinde, WS_HSCROLL ve WS_VSCROLL bağımsız olarak ayarlanabilir, ancak oluşturma işleminden sonra değiştirilemez. Elbette, oluşturdukları pencerenin WS_SCROLL stil bitlerini doğrudan test etmemelisiniz veya ayarlamamalısınız.
CMDIFrameWnd için, MDICLIENT oluşturmak için Create veya LoadFrame'e geçirdiğiniz kaydırma çubuğu stilleri kullanılır. Kaydırılabilir bir MDICLIENT alanı (Windows Program Yöneticisi gibi) istiyorsanız, CMDIFrameWnd oluşturmak için kullanılan stil için her iki kaydırma çubuğu stilini de (WS_HSCROLL | WS_VSCROLL
) ayarladığınızdan emin olun.
CSplitterWnd için kaydırma çubuğu stilleri bölücü bölgeleri için özel paylaşılan kaydırma çubuklarına uygulanır. Statik bölücü pencerelerde normalde kaydırma çubuğu stilini ayarlamazsınız. Dinamik bölücü pencerelerde genellikle böldüğünüz yön için kaydırma çubuğu stili ayarlanır; diğer bir ifadeyle satırları bölebiliyorsanız WS_HSCROLL sütunları bölebiliyorsanız WS_VSCROLL.