??xml version="1.0" encoding="utf-8" standalone="yes"?>
本着不重写已有功能的原则Q在MFC中发掘了(jin)一圈,没发现有可用的现成控Ӟ上网搜了(jin)一下,发现有h做过Q但竟然q收费出售,so faintQ只能自己动手做一个?/p>
其实思\q是蛮简单的Q就是放个Edit控g处理它的键盘输入事gQ防止删除之前的记录和提CZ息,q要处理l束命o(h)Q比如回车、空gcȝ。主要有以下几个步骤Q?/p>
1. 输入框内的字符串分D,比如分成三段log, tip和commandQ前两段都不能被修改Qcommand的内容ؓ(f)可修改的。在l束?jin)command输入后,要同步各字符ԌCZ代码如下Q?/p>
void CMainFrame::InitCommand(CString tip)
{
// 记录老字W串Q类gUpdateData(true)this->GetText();
// 讄新的log
if(this->m_log != "")
this->m_log += "\r\n";
this->m_log += tip;// 更新字符ԌcM与UpdataData(false)
this->SetText();// 光标置于字W串的尾部(否则光标?x)在一开始的位置Q?/p>
((CEdit *)m_commandDialogBar.GetDlgItem(IDC_COMMAND))->SetSel(this->m_log.GetLength(),
this->m_log.GetLength());
}
2. 重蝲PreTranslateMessage事gQ处理键盘信息,CZ代码如下Q?/p>
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN) // 处理键盘按下事g
{
// 判断是否是在脚本输入框上输入?/p>if(GetFocus() == m_commandDialogBar.GetDlgItem(IDC_COMMAND))
{
// 如果选择的是非正在输入的文字Q抛弃这个事?/p>DWORD selectedRegion = ((CEdit *)m_commandDialogBar.GetDlgItem(IDC_COMMAND))->GetSel();
int selectedStart = LOWORD(selectedRegion);
int selectedEnd = HIWORD(selectedRegion);if(selectedStart != selectedEnd && selectedStart < m_log.GetLength())
return true;if(pMsg->wParam == 8 && selectedStart <= m_log.GetLength()) // L删除之前的文?br goog_ds_charIndex="1307"> return true;
if(pMsg->wParam == 13 || pMsg->wParam == 32) // 当输入空格或回R是发送消?br goog_ds_charIndex="1390"> this->SendCommand();
}
}return CMDIFrameWnd::PreTranslateMessage(pMsg);
}
其中SendCommand的内容可自定义,处理完成后不要忘记执?的操作,同步一下字W串O(jin)K。实现效果如下:(x)
当然Q这是一个最单的实现Q还有很多问题没有处理,比如自定义菜单,屏蔽pȝ菜单{;q有很多工作可以做,比如装成一个自定义控gQ做更好的显C效果等{。但基本的思\q是一L(fng)Q恩Q如果谁有更好的实现Ҏ(gu)Q也Ƣ迎留言Q谢谢先Q)(j)
参照q篇文章Q可以搞定同码制的字W串转换Q如果有unicode到非unicode的{换问题,q需要另寻高招。我通常是归一化,把工E属?->General中的Character setl一选ؓ(f)no set或unicode。每每此Ӟ我都?x)出奇的惛_C#。。?/p>
很多时候,能用模态对话框的情况下Q都?x)用模态的。虽?a title=Copper target=_blank>Copper 老先生指着d苦口婆心(j)的教g(jin)我们Q但有时候h懒脸皮也厚?jin),无所谓了(jin)。但Q世界L很残P很多时候(比如需要在处理对话框事件的时候也能响应窗体事Ӟ(j)Q我们不得不去面寚w模态对话框。其实了(jin)解了(jin)资源理的模式,像扒开?jin)非模态对话框半遮的琵Ӟ可以很坦然的面对?jin)?/p>
模态对话框的资源分成两U,一U是内存资源Q一U是非内存资源。单看非内存资源的管理,其实和内存资源的理原理是一L(fng)。在C++中,内存资源的管理讲Inew和delete配对Q同理,非内存资源的理需要create和destroy出双入对。在q篇文章中,基本体现?jin)非模态对话框资源理的一个基本模式,卛_存资源管理和非内存资源同步?
q样通过判断内存资源是否占用Q即指针是否为空Q就可以判断非内存资源的使用状况。当指针为空Q说明对话框q未创徏Q非内存资源未申P(j)Q当指针不ؓ(f)I,对话框已创徏Q正处于可见或不可见状态。这样将两部分资源管理合q在一起了(jin)Q只需要判断指针是否ؓ(f)I就可以?jin)解对话框资源的状态。一些内存管理的手段Q比如类理思想Q将delete和destroy攑ֈcȝ析构函数中)(j)Q可以实现资源的自动理?
Z(jin)实现q种理模式Q要注意以下几点Q?
1. 在堆上分配非模态对话框的内存资源,通俗一点的描述是不要用这U方式:(x)CXXDialog t;而是用这U方式:(x)CXXDialog *t = new CXXDialog();来分配内存?
2. 同步构造和析构q程Q就是说有new一定配上个createQdelete一定要勾搭一个destroy?
3. 被delete的内存指针一定要|空Q也是下面两句要接t而至Qdelete xx;和xx == null;。其实这也是普通的内存理需要遵循的一个良好习(fn)惯?
?jin)解了(jin)这些,非模态对话框也会(x)只有温柔没有狰狞?/p>
首先Q要对头文gq行处理Q保证不?x)出现重定义的错误。这个应该每个h都会(x)Q通常有两U做法:(x)
1. ?cpp文g中添加保护,比如?cpp文g中添加:(x)
#ifndef _XX_H_
#define _XX_H_
#include "xx.h"
#endif
2. ?h中添加保护,比如在xx.h文g中添加:(x)
#ifndef _XX_H_
#define _XX_H_
// 头文件声明内?br goog_ds_charIndex="284">#endif
_XX_H_是我比较?fn)惯的命名方式,其他的命名方式,比如__XX__H__QXX_H{等Q只要够Unique好。徏议用第二种方式q行重定义的保护Q一x逸而且h通用性,M人拿来就能用Q不需要考虑保护问题。当?dng)如果在VSQ?3以上吧?Q下Q最好的解决Ҏ(gu)是用#pragma onceQ更为简单有效?/p>
其次Q最好将所有头文g需要用到的自定义类Q或函数Q都在定义前声明一下,比如在xx.h的类xx中需要用到yy.h中的yyc,那么最好做以下的处理:(x)
class yy;
class xx
{
// 实现内容
};
q样可以保证头文g引用的次序不?x)对l果造成影响?/p>
通常Q保证以上两点,通常涉及(qing)到类互指的问题都可以解决。当然如果天生就有设计问题,无论如何都是没有办法的,比如Q?/p>
// xx.h
class xx
{
yy t;
};// yy.h
class yy
{
xx t;
};
不难看出Q这是个递归定义Q编译器无法定cxx和yy的大,无法通过~译。一U解决策略是采用指针Q比如:(x)
// xx.h
class xx
{
yy* t;
};// yy.h
class yy
{
xx* t;
};
当然Q具体情况具体分析,提取一个更高层的类{手D都可以考虑?/p>
q有一个问题,我一直也?j)存疑问Q就是头文gl织问题。在VS中徏立一个MFC工程Q都?x)生一对stdafx文gQ按照这U思想我们把工E下通用的头文g都放入stdafx.h中,?cpp文g的最开始统一#include "stdafx.h"Q这样就可以?h文g中引用很的头文件?/p>
q种{略在单工程时很好用Q相当于做了(jin)头文件组l别的重用Q但它违反了(jin)我一直恪守的一个原则即谁的头文件谁负责Q指.h?cpp各自负责各自所需的头文g。于是在跨工E的时候会(x)出现一些问题。比如在B工程B的某?h中引用了(jin)工程B中的一个头文gQ由于编译次序问题,q个头文件可能无法被~译。不知道大家都如何处理头文gl织问题的,望指教?/p>
其实知道?jin)有q么个东西,剩下的问题都不能U做问题?jin),其用和CToolBarcMQ可以通过http://msdn2.microsoft.com/zh-cn/library/wc9sxcw1(VS.80).aspx下蝲MSDNCZQ当然也可以在本机的MSDN搜烦(ch)CDialogBar获得?/p>
动态创单需要先?strong style="FONT-WEIGHT: normal" goog_ds_charIndex="207">?/strong>CMenucR通常我们利用工具l制一个菜单,每一个菜单项下都可以视ؓ(f)有一个CMenucR它们联pd一P形成?wi)状。典型的一个菜单对应过来是如下图这个样子:(x)
如上QCMenu可以分成三种Q一个是PopupQ黄Ԍ(j)Q一个是SeparatorQ灰Ԍ(j)Q一个是ItemQ红Ԍ(j)。前两种都是没有ID信息的,Popup有一个指针,指向其SubMenuQItem保存各种信息有ID可以响应事gQSeparatorQ恩Q基本是一I二白的?/p>
CMenu的CreateMenuҎ(gu)可以创徏一个菜单资源,用DeleteMenuQ包含所有子菜单Q或DestoryMenu可以销毁菜单资源,用AppendMenu可以d一个菜单。了(jin)解这些内容,可以开工了(jin)Q现实现上图所C的MainSubMenu1下菜单的动态创建,代码如下Q?br goog_ds_charIndex="566">
// 假设在ChildFrm中,调用该方法获得当前的主菜单指?br goog_ds_charIndex="605"> CMenu* mainMenu = AfxGetMainWnd()->GetMenu();
CMenu* subMenu = NULL;
// 遍历主菜单下的各U菜单寻扑为MainSubMenu1的菜?br goog_ds_charIndex="724">
int menuCount = mainMenu->GetMenuItemCount();
for(int i = 0; i < menuCount; i++)
{
CString menuName;
if(mainMenu->GetMenuStringA(i, menuName, MF_BYPOSITION)
&& menuName == "&MainSubMenu1")
{
drawingMenu = mainMenu->GetSubMenu(i);
break;
}
}
// U除原有的菜单项
int subMenu1Count = subMenu->GetMenuItemCount();
for(int i = subMenu1Count - 1; i >= 0; i--)
{
subMenu->DeleteMenu(i, MF_BYPOSITION);
}
// 动态添加I(yng)tem菜单?br goog_ds_charIndex="1248"> for(int i = 0; i < 2; i++)
{CString message = "";
subMenu->AppendMenuA(MF_STRING, ID_BEGIN + i, message.Format("SubSubMenu%i", i);
}
// d分隔W?br goog_ds_charIndex="1429">
subMenu->AppendMenuA(MF_SEPARATOR);
// d弹出式子菜单
CMenu * popupMenu = new CMenu();
popupMenu->CreateMenu();
for(int i = 0; i < 2; i++)
{CString message = "";
popupMenu->AppendMenuA(MF_STRING, ID_BEGIN + 2 + i, message.Format("PopupSubMenu%i", i));
}
subMenu->AppendMenuA(MF_POPUP, (UINT_PTR)popupMenu->operator HMENU(), "PopupMenu");
有几个需要注意的地方Q一个是主菜单的指针获得Q可以参?a target=_blank>《MFC框架各部分指针获取方式?/a>一文。另一个是Popup的菜单徏立,{略是分成两部分Q先new出内存在Create?gu)源,~Z不可。最后一个是为每个Item菜单合理分配IDQ这些IDM先预留出来,在MFC中,臛_40000?9000通常都是没h用?/p>
q也引Z一个问题,卌单事件的动态绑定。我们知道在.net中,事g是真正动态绑定的Q而MFC中的事g都是只能?rn)态绑定,q是׃者的~译方式军_的。所以,在MFC中需要定义菜单事Ӟ你需要先挖好坑(预留_IDQ,规定每个坑种什么罗卜(不同类型的IDl定C同类别的事g处理函数上)(j)Q最后才能按坑种|卜Qؓ(f)执行相应事g的菜单设|相应的IDQ?/p>
可以有两U方式来l定对应ID处理的事Ӟ一个是通过ON_COMMAND_RANGE宏(想一下ON_COMMAND宏会(x)不会(x)z上用场Q)(j)在MessageMap里绑定批量处理事件的函数Q另一个是重蝲PreTranslateMessage函数Q截获ƈ判断ID来进行处理。思想都是cM的。值得注意的是Q通常q需要配套用ON_UPDATE_COMMAND_UI_RANGE来保证动态创建的菜单Enable为TrueQ否则很可能菜单不可以点击(我就被郁闯很久Q(Q?br goog_ds_charIndex="2382">
|
获得CWinApp |
获得CMainFrame |
获得CChildFrame |
获得CDocument |
获得CView |
在CWinApp?/strong> |
|
AfxGetMainWnd() m_pMainWnd |
AfxGetMainWnd()->MDIGetActive() AfxGetMainWnd()->GetActiveFrame() |
SDI:AfxGetMainWnd()->GetActiveView()->GetDocument() MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView()->GetDocument() |
SDI:AfxGetMainWnd()->GetActiveView() MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView() |
在CMainFrame?/strong> |
AfxGetApp() theApp |
MDIGetActive() GetActiveFrame() |
SDI:GetActiveView()->GetDocument() MDI:MDIGetActive()->GetActiveView()->GetDocument() |
SDI:GetActiveView() MDI:MDIGetActive()->GetActiveView() |
|
在CChildFrame?/strong> |
AfxGetApp() theApp |
GetParentFrame() |
|
GetActiveView()->GetDocument() | GetActiveView() |
在CDocument?/strong> |
AfxGetApp() theApp |
AfxGetMainWnd() |
AfxGetMainWnd()->MDIGetActive() AfxGetMainWnd()->GetActiveFrame() |
POSITION pos = GetFirstViewPosition();GetNextView(pos) | |
在CView?/strong> |
AfxGetApp() theApp |
AfxGetMainWnd() | GetParentFrame() | GetDocument() | |
在其他类?/strong> |
AfxGetApp() |
AfxGetMainWnd() |
AfxGetMainWnd()->MDIGetActive() AfxGetMainWnd()->GetActiveFrame() |
SDI:AfxGetMainWnd()->GetActiveView()->GetDocument() MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView()->GetDocument() |
SDI:AfxGetMainWnd()->GetActiveView() MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView() |
1. 在构造一个自定义的Dialog对象Ӟ我们?x)传入一个CWnd的指针进去,那个名ؓ(f)pParent的对象。但当我在Dialog中调用GetParent函数的时候,无论传进的是什么指针(比如CYourViewQ,得到的返回值都是指向CMainFrame的指针。曾l由于这个我调了(jin)N久的E序Q我承认q是׃我对MFC的了(jin)解不够造成的,但这名字也太hqh性了(jin)吧?/p>
2. 当然q有臭名昭著的UpdateData函数Q很荣幸在这点上我和大师一P每次用到的时候都要停下来查看一下才能分清楚那个诡异的bool变量的意义。也许这在有IDE的时候不是问题,但问题是我看书的时候就?x)很ȝ?ch)?jin)。分L两个明确命名的函敎ͼ也许?x)好很多?/p>
3. ShowWindow。恩是它。看到这个名字,不熟(zhn)MFC的h应该都会(x)觉得它是用来昄一个窗口的。但...Q其实它兼有昄和隐藏窗口的功能Q决定这一切的又是一个诡异的bool?..
4. MoveWindow和SetWindowPos。这一对不够专注的兄弟。它们太强大?jin),所能做的事q不止Move和SetPos那么单,当我x变一个窗体大的时候,我翻来覆ȝ扑և敎ͼ万万没想刎ͼ原来是这对名不副实的兄弟的工作,Faint...
5. GetWindowRect和GetClientRect。其实他们也许没有问题,有问题的是我Q我不喜Ƣ用引用量代替一个貌似应该由q回值完成的东西...
6. SelectObject。ؓ(f)什么你传入一个新的ObjectQ它q回一个老的Object呢?不止我一个hQ很多h都被q个搞晕q,其实我也不明白,一个函数做两个函数的事Q这实有点热心(j)q度?jin)?..
7. afx_msg。一个永q都没被用到的预留量Q这L(fng)东西在MFC中有很多...
在配|一个基于OpenCasCade的程序中Q我遇C(jin)很多ȝ(ch)。MFC向导在它所生成的View, Document{架构类中都d?jin)一D如下代码:(x)
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
在Debug状态下QVS?x)?f)你默认添加一个_DEBUG的预~译)(j)Q你在该cM调用的new操作W都?x)被DEBUG_NEW所取代Q请警惕q个行ؓ(f)Q如果你重蝲q某个类的newQ很可能׃(x)׃它导致无法编译通过或运行不正确?/p>
除此之外一些默认的讄也要注意Q在VS2005中是默认支持Unicode的,它会(x)在你的编译选项中加?D "_UNICODE" /D "UNICODE"。这׃(x)使得CString和你可能用到的std::string存在很麻?ch)的转换问题。你需要修攚w目属性中General-->Character Set为not setQ将其设为ununicodeQ保证与std::string的一_(d)当然你还可以q用其他的解x法满你的需求)(j)?/p>
有时候IDE也会(x)“好心(j)办坏?#8221;Q比如在一个解x案中有两个工E,你ؓ(f)AdB的编译依赖,在A的链接选项中就?x)(zhn)?zhn)加上对B生成的dll的引用。当你某天整理代码取消了(jin)q个依赖的时候,你突然发现莫名的出现?jin)很多link错误。不要慌张,在A中添加上B链接就好了(jin)Q这工作其实是你必自己做的,只是你添加了(jin)依赖~译器非怸动的帮你完成?jin)?/p>
也许你看上面的错误都很简单,但如果不心(j)Q也许有天也?x)像我一h陷其中半天爬不出来。MQ在天天用VS2005建MFC工程的时候,提前做好两g事。一件是通读一遍系l默认生成的代码Q做到心(j)中有敎ͼ每一条莫名其妙的东西都要?jin)解一下它的用途;另一件是在刚开始和改变?jin)工E属性之后查看一下你的编译和链接命o(h)Q搞清楚它做?jin)什么事Q有时候命令行虽然难记一点,但确实是一目了(jin)?dng)你可以不必每天用命o(h)行编译程序,但一定要对这些命令心(j)如明镜,?jin)如指掌才好?/p>