渴望飞翔
Fly in the C++ Sky...
posts - 9,  comments - 6,  trackbacks - 0

在做一个MFC软件的时候有一个这样的需求,就是要有类似与AutoCad的命令输入框,如下图所示:

 

本着不重写已有功能的原则,在MFC中发掘了一圈,没发现有可用的现成控件,上网搜了一下,发现有人做过,但竟然还收费出售,so faint,只能自己动手做一个。

其实思路还是蛮简单的,就是放个Edit控件处理它的键盘输入事件,防止删除之前的记录和提示信息,还要处理结束命令,比如回车、空格之类的。主要有以下几个步骤:

1. 将输入框内的字符串分段,比如分成三段log, tip和command,前两段都不能被修改,command的内容为可修改的。在结束了command输入后,要同步各字符串,示例代码如下:

void CMainFrame::InitCommand(CString tip)
{
 // 记录老字符串,类似于UpdateData(true)

 this->GetText();

 // 设置新的log
 if(this->m_log != "")
  this->m_log += "\r\n";
 this->m_log += tip;

 // 更新字符串,类似与UpdataData(false)
 this->SetText();

 // 将光标置于字符串的尾部(否则光标会在一开始的位置)

 ((CEdit *)m_commandDialogBar.GetDlgItem(IDC_COMMAND))->SetSel(this->m_log.GetLength(),
  this->m_log.GetLength());
}

2. 重载PreTranslateMessage事件,处理键盘信息,示例代码如下:

 BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
 if(pMsg->message == WM_KEYDOWN) // 处理键盘按下事件
 {
  // 判断是否是在脚本输入框上输入的

  if(GetFocus() == m_commandDialogBar.GetDlgItem(IDC_COMMAND))
  {
   // 如果选择的是非正在输入的文字,抛弃这个事件

   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()) // 阻止删除之前的文字
    return true;

   if(pMsg->wParam == 13 || pMsg->wParam == 32) // 当输入空格或回车是发送消息
    this->SendCommand();
  }
 }

 return CMDIFrameWnd::PreTranslateMessage(pMsg);
}

其中SendCommand的内容可自定义,处理完成后不要忘记执行1的操作,同步一下字符串就OK。实现效果如下:

当然,这是一个最简单的实现,还有很多问题没有处理,比如自定义菜单,屏蔽系统菜单等;还有很多工作可以做,比如封装成一个自定义控件,做更好的显示效果等等。但基本的思路还是一样的,恩,如果谁有更好的实现方案,也欢迎留言,谢谢先:)



duguguiyu 2007-07-21 21:43 发表评论

文章来源:http://www.cnblogs.com/duguguiyu/archive/2007/07/21/826901.html
posted @ 2007-07-21 21:43 duguguiyu 阅读(561) | 评论 (0)编辑 收藏

在VC++中有着一大把字符串类型。从传统的char*到std::string到CString,简直是多如牛毛。期间的转换相信也是绕晕了许多的人,我曾就是其中的一个。还好,MS还没有丧失功德心,msdn的一篇文章详细的解析了各种字符串的转换问题,链接如下:http://msdn2.microsoft.com/zh-cn/library/ms235631(VS.80).aspx

参照这篇文章,可以搞定同码制的字符串转换,如果有unicode到非unicode的转换问题,还需要另寻高招。我通常就是归一化,把工程属性-->General中的Character set统一选为no set或unicode。每每此时,我都会出奇的想念C#。。。



duguguiyu 2007-07-21 21:04 发表评论

文章来源:http://www.cnblogs.com/duguguiyu/archive/2007/07/21/826869.html
posted @ 2007-07-21 21:04 duguguiyu 阅读(439) | 评论 (0)编辑 收藏

非模态对话框比模态对话框更难使用这是众所周知的,这是由于模态对话框运行时,阻塞了其父窗口的消息循环,使其能自成一派,所以能够怡然自得。但非模态对话框只相当于一个由父窗体创建的一个同级的Hwnd,就像一个长大了的孩子,可以和父母并驾齐驱了,需要父母管又不能管的太厉害,其资源管理、通信都会比模态的更为复杂。  

很多时候,能用模态对话框的情况下,都会用模态的。虽然Copper 老先生指着鼻子苦口婆心的教导了我们,但有时候人懒脸皮也就厚了,无所谓了。但,世界总是很残酷,很多时候(比如需要在处理对话框事件的时候也能响应窗体事件),我们不得不去面对非模态对话框。其实了解了资源管理的模式,就像扒开了非模态对话框半遮的琵琶,可以很坦然的面对了。

模态对话框的资源分成两种,一种是内存资源,一种是非内存资源。单看非内存资源的管理,其实和内存资源的管理原理是一样的。在C++中,内存资源的管理讲究new和delete配对,同理,非内存资源的管理需要create和destroy出双入对。在这篇文章中,基本体现了非模态对话框资源管理的一个基本模式,即内存资源管理和非内存资源同步。

这样通过判断内存资源是否占用(即指针是否为空)就可以判断非内存资源的使用状况。当指针为空,说明对话框还未创建(非内存资源未申请);当指针不为空,对话框已创建,正处于可见或不可见状态。这样将两部分资源管理合并在一起了,只需要判断指针是否为空就可以了解对话框资源的状态。一些内存管理的手段,比如类管理思想(将delete和destroy放到类的析构函数中),可以实现资源的自动管理。

为了实现这种管理模式,要注意以下几点:

1. 在堆上分配非模态对话框的内存资源,通俗一点的描述就是不要用这种方式:CXXDialog t;而是用这种方式:CXXDialog *t = new CXXDialog();来分配内存。

2. 同步构造和析构过程,就是说有new一定配上个create,delete一定要勾搭一个destroy。

3. 被delete的内存指针一定要置空,也就是下面两句要接踵而至:delete xx;和xx == null;。其实这也是普通的内存管理需要遵循的一个良好习惯。

了解了这些,非模态对话框也会只有温柔没有狰狞。



duguguiyu 2007-07-21 20:56 发表评论

文章来源:http://www.cnblogs.com/duguguiyu/archive/2007/07/21/826858.html
posted @ 2007-07-21 20:56 duguguiyu 阅读(1323) | 评论 (1)编辑 收藏

在C++中经常会涉及到处于不同头文件的类互相引用的情况,有时候头文件引用(include)会搞得很乱,导致报一堆的错。其实遵循一定规则,可以避免大部分的混乱。

首先,要对头文件进行处理,保证不会出现重定义的错误。这个应该每个人都会,通常有两种做法:

1. 在.cpp文件中添加保护,比如在.cpp文件中添加:

#ifndef _XX_H_
#define _XX_H_
#include "xx.h"
#endif

2. 在.h中添加保护,比如在xx.h文件中添加:

#ifndef _XX_H_
#define _XX_H_
// 头文件声明内容
#endif

_XX_H_是我比较习惯的命名方式,其他的命名方式,比如__XX__H__,XX_H等等,只要足够Unique就好。建议使用第二种方式进行重定义的保护,一劳永逸而且具有通用性,任何人拿来就能用,不需要考虑保护问题。当然,如果在VS(03以上吧?)下,最好的解决方案是用#pragma once,更为简单有效。

其次,最好将所有头文件需要用到的自定义类(或函数)都在定义前声明一下,比如在xx.h的类xx中需要用到yy.h中的yy类,那么最好做以下的处理:

class yy;

class xx
{
 // 实现内容
};

这样就可以保证头文件引用的次序不会对结果造成影响。

通常,保证以上两点,通常涉及到类互指的问题都可以解决。当然如果天生就有设计问题,无论如何都是没有办法的,比如:

// xx.h
class xx
{
 yy t;
};

// yy.h
class yy
{
 xx t;
};

不难看出,这是个递归定义,编译器无法确定类xx和yy的大小,就无法通过编译。一种解决策略是采用指针,比如:

// xx.h
class xx
{
 yy* t;
};

// yy.h
class yy
{
 xx* t;
};

当然,具体情况具体分析,提取一个更高层的类等手段都可以考虑。

还有一个问题,我一直也心存疑问,就是头文件组织问题。在VS中建立一个MFC工程,都会产生一对stdafx文件,按照这种思想我们把工程下通用的头文件都放入stdafx.h中,在.cpp文件的最开始统一#include "stdafx.h",这样就可以在.h文件中引用很少的头文件。

这种策略在单工程时很好用,相当于做了头文件组织级别的重用,但它违反了我一直恪守的一个原则即谁的头文件谁负责,指.h和.cpp各自负责各自所需的头文件。于是在跨工程的时候会出现一些问题。比如在B工程B的某个.h中引用了工程B中的一个头文件,由于编译次序问题,这个头文件可能无法被编译。不知道大家都如何处理头文件组织问题的,望指教。



duguguiyu 2007-07-21 19:37 发表评论

文章来源:http://www.cnblogs.com/duguguiyu/archive/2007/07/21/826821.html
posted @ 2007-07-21 19:37 duguguiyu 阅读(463) | 评论 (1)编辑 收藏

有时候我们经常把对话框和视图结合起来,做成AutoCAD命令输入框、PhotoShop浮动框之类的效果。但很奇怪的是我看过的MFC的书上都没有特别说明过这样的工作该如何去做,我刚接触MFC的时候都是通过控制非模态对话框来模拟的,后来才知道这些工作是通过CControlBar的派生类来完成的。比如CDialogBar就是加载一个已有的对话框资源,嵌入Frame中,和视图配合使用。

其实知道了有这么个东西,剩下的问题都不能称做问题了,其使用和CToolBar类似,可以通过http://msdn2.microsoft.com/zh-cn/library/wc9sxcw1(VS.80).aspx下载MSDN示例,当然也可以在本机的MSDN搜索CDialogBar获得。



duguguiyu 2007-07-21 19:35 发表评论

文章来源:http://www.cnblogs.com/duguguiyu/archive/2007/07/21/826819.html
posted @ 2007-07-21 19:35 duguguiyu 阅读(2364) | 评论 (0)编辑 收藏
仅列出标题  下一页
Welcome to my c++ home...

<2019年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

留言簿(1)

随笔分类(9)

随笔档案(9)

搜索

  •  

积分与排名

  • 积分 - 9389
  • 排名 - 1066

最新评论

阅读排行榜

评论排行榜