Archive for 六月, 2007

插件式开发和系统引导(在线报道 )

Posted by 机器人 on 30th 六月 2007 in phpoo

    今晚的活动本来是应该在昨晚举行的,由于昨晚有点其它事情,所以推迟到今晚,好了,活动马上就了开始了,还有几分钟的时间,先让我来啰嗦几句吧。

    看到标题,相信大家都已经期待很久了吧,是的,我们都是这样的,所以才把这问题专门提出来,让小黑同志作为专题给我们讲解。对于什么是插件,插件有什么好处,怎么给自己的系统编写插件,又怎么让别人给我们自己的系统不回插入,这一连串的神秘的又似曾想问的疑问小黑今晚都会给我们一一道来,就让我们今晚一饱眼福吧。

  正式开始:

  今晚的话题就是插件,插件就是我们编程里的JS一样,我们可以把功能交出客户端去完成,如果我们开发一套系统的话,那么有很多因素都会影响我们的开发。如果我们把一些行为规约制定好,把开发工作让别人去做,效果就会很不一样。

插件原理:

 怎么样才能让它工作,我的系统工作的第一步就应该把插件给载入进入,让它能对以后的代码产生影响。换句话来说,一个插件要工作,我们首先应该给它一个引导装载程序,就好比我们的操作系统一样。

我们来模仿LINUX操作系统来给大家淙演示一下。

小黑给大家演示了一个使用插件的例子。。

……Omitted ………

下面是演示代码:

 

<?php
/**
 * 插件式的开发,使用了"注册模式"和"工厂模式"去完成的
 * 把风格引导做成插件,载入时调用它
 *
 * @author 小黑米
 * @package plugin
 */

/**
 * 注册类
 *
 */
class Registry{
	/**
	 * 用于存储注册对象
	 *
	 * @var array
	 */
	private $store = array();
	/**
	 * 检测对象是否存在
	 *
	 * @param string $type
	 * @param string $key
	 * @return bool
	 */
	private function isValid($type,$key){
		return array_key_exists($key,$this->store[$type]);
	}
	/**
	 * 获取已注册对象
	 *
	 * @param string $type
	 * @param string $key
	 * @return object
	 */
	public function get($type,$key){
		if ($this->isValid($type,$key)) {
			return $this->store[$type][$key];
		}
	}
	/**
	 * 注册对象
	 *
	 * @param string $type
	 * @param string $key
	 * @param object $obj
	 */
	public function set($type,$key,$obj){
		$this->store[$type][$key] = $obj;
	}
	/**
	 * 用于singleton
	 *
	 * @return object
	 */
	public static function getInstance(){
		static $instance = array();
		if(!$instance)$instance[0] = new Registry();
		return $instance[0];
	}
}
//模拟插件A B C
class PluginC{
	function __construct(){
		echo "PluginC被加载了";
	}
}

class PluginA{
	function __construct(){
		echo "PluginA被加载了";
	}
}

class PluginB{
	function __construct(){
		echo "PluginB被加载了";
	}
}

/**
 * 插件引导类 相当于一个注册插件的工厂,使用工厂模式
 *
 */
class PluginFactory{
	/**
	 * 开始引导
	 *
	 */
	public function __construct(){
		/*查库得出要加载的数组,并把已被加载的插件注册到注册类里面(模拟的情况),
begin是在程序开始的时候加载插件,end是在程序结束前加载插件,这个可能要用到对象模式中的 延迟模式 给个接口 以后方便更改 呵呵!
*/		$plugins = array("begin"=>array("PluginA","PluginC"),"end"=>array("PluginB"));
		$reg = Registry::getInstance();
		foreach ($plugins as $k => $v){
			foreach ($v as $vv){
				eval('$reg->set("'.$k.'","'.$vv.'",new '.$vv.'());');
			}
		}
	}
}
new PluginFactory();

//现在插件可以使用了,目前这个算是雏形,要用到实际当中还有很多需要考虑的,演示:周5晚给出
?>

 在这里会引入两个设计模式“工厂模式、注册模式”,当然这大家应该都很熟悉了,或者说不陌生,因为我们前段时间专门针对这话题讨论过。

不过对于注册模式还是给大家强调了一上。“注册模式”就相当于我们在函数体内使用global来申明全局变量一样,不过使用注册模式我们这里就能启动global的作用,不过需要我们人为的注册,不然在类内部不能访问未注册的变量。

注册模式最核心的东西是使用一成员变量来保存需要注册的变量。

接下来是一个验证函数,验证该变量是否被注册。

下面为了不创建多个实例,我们使用了一个单键模式来创建对象。

 

	public static function getInstance(){
		static $instance = array();
		if(!$instance)$instance[0] = new Registry();
		return $instance[0];
	}

 PluginFactory这个类是一个引导装载器。

关键在什么时候调用,一旦程序引导完了以后,那么就通过eval()来动态调用所需要的插件。

一般情况,都会通过eval()来动态装载我们所需要的程序,即,动态创建新的类等操作。

 

插件的基本制作流程就这样,接下来我们结合smarty来就说smarty类的插件是怎么工作了。

 

$plugins_dir

插件目录变量

 

该变量定义Smarty寻找所需插件的目录。默认是在SMARTY目录下的“plugins”目录。如果提供了一个相对路径,Smarty将首先在SMARTY目录下寻找,然后到当前工作目录下寻找,继而到php包含路径中的每个路径中寻找。

接下来是小黑对smarty源码进行解析,这一过程中,听得大家津津有味。

想不到smarty居然还有这么多功能。哎……看来今天收获不小哟。。小黑,一个字“强”。。

今天得演示就到这里了,如果大家有什么不清楚的,可以上小黑博客去和它交流 http://hey-m.phpoo.cn

 

为什么我们要选择学习C++,为什么要选择现在

Posted by 机器人 on 30th 六月 2007 in phpoo

     我们大家在一起的时间也不短了,掐下手指头算一下,也差不多快有一年了,在这一年里,可能有的同学学到了不少东西,但有的同学介于我们这边的学习和其它事情之间,比如,学校考试等,所以没有集中精力在技术方面进行突破,在进度上存在着一些不同步,似乎我们的资源没有得到有效的利用.

    我们能聚在同一个地方进行互相学习,本来,从环境上讲,我们是很幸运的,应该是令人羡慕的,但实际上,并不是这样的,我们并没有好好的利用它,从很大层度上来讲存在着一定的浪费,当然并不是说大家整天什么都没做,其实,大家也都很努力,经过这一年,我们无论是在知识上,还是在人际交往上,还是同学之间的友谊上都有很大的提高,最直接的表现为(当然是我个人的感觉)每当我看见我们之的任何一个人时,我都会有一种和其它同学不一样的感觉,这种相信大家也会有同样的感觉,虽然我们取得了这样的成就,这只能证明我们没有失败,而不能证明我们是成功,所以按照我们现有的资源来讲,我们应该收获的更多,这只能说明我们的努力程度不够,应该加倍努力才可能到达我们实际的目的地.

    眼下快放暑假了,正好可以集中精力学习,为了充分的利用这两个多月的时间,所以我制定了"在暑假进行C++统一学习"这一计划,防止没有计划和进度监督,懒散、气氛下滑等现象的出现,从而导致宝贵时间的浪费,因为两个月的时间太关键了,我们很难再有这样的两个月了.

    说到这里,可能大家会问了,我们PHP刚上手,还没有一个很好的提高,怎么又让我们去进行C++的学习,而且还是选择MFC来开发,我不知道下面我所罗列的理由能不能说服你,如果认为有道理,那么说明我们的题目是正确的.

1.从语言特性上来讲,C++是一个综合语言.在继承了面向过程设计模型的基本上加入了许多新的特点,是一个面向对象和面向过程的结合,所以它能做当前所流行的语言能做的所有事情.

2.C++鼓励我们用抽象,分解的思想去思考问题,故所提供给我们的所有库是以抽象、分解的原则(对象的思想)去设计的,所以当我们在反复使用这些库的过程中,是很容易体到这些设计思想的存在,并将其吸收,用到我们的的设计中去.

3.和其它语言相比,C++标准是比较复杂的,其它的语言标准基本上是它的一个瘦身,

所以在我们掌握了C++以后,想去掌握其它语言,会变得很容易.

4.那么为什么要选择MFC框架来开发呢?

    C++只是一种语言的标准,标准的存在只是以最原始的方式定义了这门语言能做什么?而没有指出每个特性应该如果去使用,如果去把它做好.所以我们只学C++(学习它的标准),对我们是没有任何提高的.

    MFCMicrosoft公司对window API函数的封装,从而使我们在创建window程序时,不会那么复杂.而微软公司当初在设计MFC框架时,不是随便得把API函数进行了组合,而是他们的软件架构大师们精心设计后的成果,所以里面蕴含了大量的,不为常人所知道的,优秀的设计思想.

    所以如果我们能使用C++结合MFC开发一个系统的话,那么不但能使我们能够掌握C++,更重要的是能从中学习伴随我们一生的设计思想.

5.这可能也是现实的一点.

    掌握一门语言,不集中一定的时间和精力去学习,是很难掌握的,或者说会花掉很长的时间才能对其进行掌握.当我们参加工作以后,整天都会为自己的工作而忙,如果再想去学习C++或者其它什么语言,介于精力和时间方面,是很难办到的.而且大家也知道,在公司里面,为了使用一门新的技术,去另外聘一位员工,和你在最快的时间内去掌握这门技术,相信我们都明白,后者对我们来讲,是很有利的.

    我们现在,有两个多月时间, 如果我们能加以好好利用,(要知道像现在这样的时间也不多了,很可能这就是我们的最后一个暑假)在我们大家的共同的努力下,完成我们的计划(大家注意:不是各忙各得,而是在大家互相学习,互相交流的基础上完成我们的计划).

    很可能我们这里面的大部分人会一直把程序给写下去,也算是对以后道路的一个铺垫,争取进行我们这个暑假的努力,在以后的某一天,突然老板叫到我们说现在需要使用C++开发的一个项目,或者使用JAVA语言开发一个项目,那么我们只需要对其语言的细节进行复习,或者进行快速参考一个,就能很容易的使用该语言去编写相应的代码.在那个时候,大家可能会感叹一下:”幸好以前学习C++,不然就……”.

    不知道大家平时有没有这样的感觉.当我们需要去解决一个问题的时间,如:需要去了解一个算法,我们使用搜索引擎,找到了一个实现该算法的网页,假设该算法的实现不是用我们所熟悉的语言(当然我们最熟的莫过于PHP了)来进行描述的,很可能我们会直接关掉该网页,同样也可能它是好的算法的实现,这样,我们最终可能会与如此好的算法思想无缘,对我们来讲,无疑是一种遗憾.如果我们掌握了C++语言,遇到类似的问题的话,大家会怎么样呢?很显示,会采取截然不同的处理方式(该语言无论怎么样也只是C++的一个子集,C++都掌握了,我还搞定不了他吗?),所以这对我们来讲,会避免我们错过阅读优秀程序的机会,从而迅速提升自我的能力.

    不知道上面的理由是否能够说服大家,我想,应该差不多了.可能大家还会问,那我们的PHP呢?我们的PHP还有很多都不懂,就更不用谈你们平时所说的什么设计模式之类的东西了.其实这一些大家都可能从C++中学到.现在大家先把PHP放一下,在经过一个暑假的C++学习后,那时候再回过头来看PHP,到那时,你会发现,原来程序还可以这样写,没准儿一个和MFC一样的PHP框架就被你给写出来了.

机器人 2007-6-27 于 北京

本周五晚话题:插件式开发和系统引导

Posted by 机器人 on 24th 六月 2007 in phpoo

功能都已经实现了,就是还有些小问题得细心的修修补补呵呵~

呀。。。算是把我学计算机一年里面最最最宝贵的东西拿出来和大家分享了!

     并不是最先进,但是我感觉有个性;并不是最好,因为我想做到更好;

如无意外 周5晚 7点

分享者:小黑

论坛地址::http://www.phpoo.cn/….#pid22

动态链接库程序的编写(VC++学习笔记)

Posted by 机器人 on 24th 六月 2007 in c/c++

使用动态链接库的好处:

1.        可以采用多种编程语言来编写.

2.        增强产品的功能.

3.        提供二次开发的平台.

4.        简化项目管理.

5.        可以节省资源的共享.

6.        有助于实现应用程序的本地化.

 

编写一个DLL(动态链接库),和编写一个我们自己的函数库一样,唯一不同的是,它被编译器编译成了DLL文件.而这个文件,可以被我们日后的系统所调用,不再需要对其重新编译.

 

使用VC++开发平台来创建一个动态库非常方便,方法如下:

新建工程->Win32 Dynamic-Link Library 然后输入工程名字.

然后新建C++ source 文件来编写我们的函数.

最后编译链接即可得到我们所编写的DLL.

我们发现生成了两个文件.一个dll文件和一个lib文件.lib(输入库)文件给我们,这个输入库文件它并没有包括实现的代码,而只是为我们的链接程序提供信息,以便在我们的可执行文件,也就是exe文件建立动态链接时,要用到的重定位表.

注意:我们在编写函数代码时,需要在函数前加上_declspec(dllimport).

规定此函数将被导出.

Code Section:

_declspec(dllimport) int add(int a, int b){
	return a + b;
}
_declspec(dllimport) int subtract(int a, int b){
	return a - b;
}

 

使用一个动态链接库:

方法1.

将我们所需要使用的dll文件放在客户代码工程目录下,或者放在系统的环境变量所指定的目录中.

然后打开Project->Setting->LinkObject/library modules处输入随DLL库提供给我们的lib文件.

显示声明一下我们所使用的函数是外部导入的.代码如下:

Code Section:

extern add(int a, int b);
extern subtract(int a, int b);

最后,我们就可以使用DLL给我们提供的addsubtract函数了.

 

方法二.

在创建动态链接库时,为所有的函数在头文件进行声明.

然后把头文件提供给用户,当然我们可以在头文件对库函数进行详细的描述.

这里我们就不需要在外部进行extern声明了,只把头文件进来即可..

头文件格式如下:

CodeSection:

#ifdef DLL_API
#else
//防止在C语言客户端访问C++编译器编译而成的DLL,因为C++编译器会对DLL
//内的函数进行改编,对C编译来说是不可识别的,所以在这里我们在显示的
//申明一下使用C规则来进行导入,但这样不能对类成员进行导入进出
//#define DLL_API extern "C" _declspec(dllimport)
#define DLL_API _declspec(dllimport)
#endif

#include <windows.h>
#include <stdio.h>

DLL_API int add(int a, int b);
DLL_API int subtract(int a, int b);

class DLL_API Point
{
public:
	void output(int x, int y);
};

方法三:

此方法采用动态的方法来对DLL进行加载,不需要在进行加载时将所有的库一次性的全部加载进来,而是在需要使用时,才进行动态的增加.如果不采用动态加载的办法,而是像我们上面所介绍的方法那样,在加载的库比较多的情况下,这样会增加进程的启动时间,导致软件启动减慢.

在使用动态加载中,不需要任何的外部声明,也不需要增加在添加任何的输入库文件,就可完成DLL库的动态加载.

Code Section:

 

HINSTANCE hInst;
	hInst = LoadLibrary("Dll2.dll");//Dynamice loading Dll2.dll
	typedef int (*ADDPROC)(int a, int b);//Define pointer function
	ADDPROC add = (ADDPROC)GetProcAddress(hInst, "add");//Get function address of add
	if(!add){
		MessageBox("获取函数失败");
		return;
	}

注意::如果在这里我们没有通过模块定义文件来定义函数改编后的名称,那么我们这里所要获取的函数名不是DLL的函数名,而应该是经过编译器改变后的函数名.: ?add@@YAHHH@Z

GetProcAddress() 第二个参数,除了指定是函数名外,也可以是所需调用函数的序号,这个序号我们可以使用dumpbin -exports DLLNAME(微软提供给我们的工具)来查看,由于第二个参数是需要一个指向常量的字符串,而这时辰 我们所得到的是一个整形,所以我们可以使用MAKEINTRESOURCE(INT)这个宏来进行转变.

 

当我们不再需要动态链接库的时候,那么可以使用FreeLibrary(hinst)来释放我们所加载的动态链接库资源.

 

机器人 2007-06-24 北京

 

软件设计常见概念总结(UML学习笔记)

Posted by 机器人 on 20th 六月 2007 in 其它

UML学习笔记:

面向对象技术不仅是一种程序设计方法,更重要的是,它是一种对真实世界的抽象思维方式.

UML是一种定义良好,易于表达,功能强大,且普遍适用的建模语言.

UML是标准的建模语言,而不是标准的开发过程.

标准建模语言定义了

采用面向对象的方法设计系统的一般步骤是:

1.首先 描述需求.

2.根据需要描述系统的静态模型,以构造系统的结构.

3.描述系统的行为.

标准建模语言的主要内容归纳为静态建模机制和动态建模机制.

对于大多数人而言,学习面向对象语言并不难,难是是充分利用面向对象语言提供给我们的优势.面向对象语言提供了优良的特性,但并不代表我们能很好的利用它.

对于大型项目最大的错误是只见树木,不见森林.

模型是对事物的一种抽象,人们常常在正式建造实物之前,首先建立一个简化的模型,以便更透彻地了解它的本质,抓住问题的要害.

所谓的抽象是指为了达到某种目的,将写问题有关的某些特性抽取出来,而略出与问题无关的方法.抽象的目的在于明确地展示和描述那些对某种目的有重要影响的特征,并避免那些不重要因素的缠绕.抽象总是为某种目的服务的,由目的来决定什么重要,什么不重要.因此,同一事物可以有不同的抽象,如何选择取决于构造它的目

的.

模式是用来描述做某类事所具有的一些共性特征的技术.

每当我们对问题有了一定的了解的时候,最易陷入细节,因此应规定工作期限,并进行严格的评审.

一份好的文档至少应用包括三部分:

1.1-2页类图,至少应该包含少量类.

2.几幅交互图,描述类之间的协作关系.

3.一些文字说明,将几幅图连续起来.

优化代码最终会降低系统的可读性和可扩展能力.最终系统应当足够快,以满足用户的要求,但需要把握时机,如果做的过早就会给开发带来麻烦,所以优化是一件应当留待开发后期再做的事情.

达尔文使用类的概念来描述人的种族,他应用类的继承概念在描述他著名的进化论.

交互图主要用来描述对象之间的动态合作关系以及合作过程中的行为次序.

  以下摘自<<Professional C++>> Nicholas A. Solter & Scott J. Kleper

  学习以C++语言和成为一个好的C++程序员完全是两码事,如果坐下来阅读C++标准,把每一条都记下来,那么你对C++的了解与别人并没有两样.不过,如果你不查看代码并编写自己的程序,由此来获得一些精验,你肯定成不了一个好的程序员.原因在于,C++语法只是以最原始的方式定义了这种语言能够做什么,而没有指出每个特性应该如何使用.

  设计技术只是用业解决C++中某个特定问题的标准方法,通常,设计技术目的是为了克服一个不好的特性,或者解决C++中存在的语言缺陷.

把拷贝过的SQL Server 数据库恢复的正确方法

Posted by 机器人 on 19th 六月 2007 in database

在SQL Server 7中由于MS重新设计了数据库文件的存储方式,取消了新建设备再建数据库这一繁琐的过程。新的存储格式,一个数据库包括两个文件,mdf数据库文件和 ldf日志文件。所以我们在重装机器备份时可以把你要备份的数据库的这两个文件拷贝出来,重新安装之后再恢复。

在SQL Server中提供了这种恢复方式的存储过程。

1.sp_attach_db [

在SQL Server 7中由于MS重新设计了数据库文件的存储方式,取消了新建设备再建数据库这一繁琐的过程。新的存储格式,一个数据库包括两个文件,mdf数据库文件和 ldf日志文件。所以我们在重装机器备份时可以把你要备份的数据库的这两个文件拷贝出来,重新安装之后再恢复。

在SQL Server中提供了这种恢复方式的存储过程。

1.sp_attach_db [@dbname =] ’dbname’,[@filename1 =] ’filename_n’

给系统添加一个数据库,在dbname指定数据库名称,filename_n指定数据库的文件和日志文件。比如我有一个voogiya的库,停止SQL Server服务备份voogiya_data.mdf,voogiya_log.ldf,启动SQL server,删除掉这个库,然后再把这两上文件拷到sql server DATA目录中,在Query Analyzer中执行如下语句:

EXEC sp_attach_db @dbname = N’voogiya’,

@filename1 = N’d:\mssql7\data\voogiya_data.mdf’,

@filename2 = N’d:\mssql7\data\voogiya_log.ldf’

就会把这个库加入到SQL Server Group中.

2.sp_attach_single_file_db [@dbname =] ’dbname’,

[@physname =] ’physical_name’

这个命令和上面的功能一样,在physical_name中只要写上据库的物理文件名就可以了,日志文件SQL server会重新建立。这个存储过程的运行要先执行下面的存储过程:

sp_detach_db @dbname = ’dbname’

同样以上面的为例:

EXEC sp_detach_db @dbname = ’voogiya’

EXEC sp_attach_single_file_db @dbname = ’voogiya’,

@physname = ’d:\mssql7\data\voogiya_data.mdf’

要注意执行以上存储过程的用户要在sysadmin中.

以上方法在windows Nt 4.0,service pack5,sql server 7.0上运行通过。

An Inside Look at Google China

Posted by 机器人 on 16th 六月 2007 in mylife

An Inside Look at Google China – part 1 of 6

 

 

An Inside Look at Google China – part 2 of 6

 

 

An Inside Look at Google China – part 3 of 6

 

An Inside Look at Google China – part 4 of 6

An Inside Look at Google China – part 5 of 6

An Inside Look at Google China – part 6 of 6

 

2007-6-16 机器人 于 北京

PHP日常使用小tips

Posted by 机器人 on 15th 六月 2007 in php/javascript

1.简易判断ip地址合法性

2.email的正则判断

3.检测ip地址和mask是否合法的例子

1.简易判断ip地址合法性

if(!strcmp(long2ip(sprintf("%u",ip2long($ip))),$ip)) echo "is ip\n";

—-

2.email的正则判断

eregi("^[_\.0-9a-zA-Z-]+@([0-9a-zA-Z][0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,6}$", $email);

—-

3.检测ip地址和mask是否合法的例子

$ip = ’192.168.0.84′;

$mask = ’255.255.255.0′;

$network = ’192.168.0′;

$ip = ip2long($ip);

$mask = ip2long($mask);

$network = ip2long($network);

if( ($ip & $mask) == $network) echo "valid ip and mask\n";

?>

—-

4.今天解决了一个巨郁闷的问题

ipb的添加用户页面toadduser.php似乎会重复提交,导致在添加新用户的时候总是报该用户已经存在…已经郁闷了我3天了,终于搞定,大快人心!

—-

5.关于表单刷新

问:为什么我在点击浏览器的后退按钮后,所有字段的信息都被清空了?

答:这是由于你在你的表单提交页面中使用了 session_start 函数。该函数会强制当前页面不被缓存。解决办法为,在你的 Session_start 函数后加入 header("Cache-control: private"); 注意在本行之前你的PHP程序不能有任何输出。

补充:还有基于session的解决方法,在session_start前加上

session_cache_limiter(‘nocache’);// 清空表单

session_cache_limiter(‘private’); //不清空表单,只在session生效期间

session_cache_limiter(‘public’); //不清空表单,如同没使用session一般

可以在session_start();前加上    session_cache_limiter("private,max-age=10800");

摘自phpe.net

—-

6.快速搞定文件下载头部输出

header("Content-type: application/x-download");

header("Content-Disposition: attachment; filename=$file_download_name;");

header("Accept-Ranges: bytes");

header("Content-Length: $download_size");

echo ‘xxx’

…….2004-08-19 11:50:30

—-

7.用header输出ftp下载方式,并且支持断点续传

一个例子:

header(‘Pragma: public’);

header(‘Cache-Control: private’);

header(‘Cache-Control: no-cache, must-revalidate’);

header(‘Accept-Ranges: bytes’);

header(‘Connection: close’);

header("Content-Type: audio/mpeg");

header("Location:ftp://download:1bk3l4s3k9s2@218.30.116.103/1001/咖哩辣椒/咖喱辣椒.rmvb");

…….2004-10-08 13:26:45

—-

8. 时间处理集锦

1.善用strtotime()

strtotime(‘now’);

strtotime(‘+1 day’);

strtotime(‘-3 hour’);

怎么使用CTabCtrl控件创建一个简单的属性应用程序

Posted by 机器人 on 15th 六月 2007 in c/c++

程序运行效果图:

 

 首先把CMyTabCtrl.cpp 和 CMyTabCtrl.h这两个文件添加你的工程.

然后使用VC开发工具为你的对话框添加一个CTabCtrl控件,并为该控件关联一个成员变量,注意:变量类型为CMyTabCtrl,而不是普通的CTabCtrl,其实CMyTabCtrl是从CTabCtrl派生而来的,所以它同样拥有CTabCtrl类的所有特性.

你可以不使用VC向导工具,而是简单的增加成员变量,并且手动编辑它,把类型设成CMyTabCtrl.

现在为你的每个页设置相应的对话框,这里特别注意:这里的对话框类型需要设置成子窗口,并且没有边框,否则这些对话框将不会成了标签控件的一部分,即会脱离主窗口.

接下来为刚新建的每个对话框创建一个类,在我们的实例中为:CPageOne,CPageTwo,CPageThree.

在CMyTabCtrl.cpp中大家会看见如下代码:设置一个指向CDialog*类型的成员变量数组.  m_tabPages[3],用于存入前面创建的对话框的指针,

m_nNumberOfPages保存在属性标签上被点击的页索引号,如果每一个被点击,那么其值将被设为0,同样在初始化函数中,指定所有的标签对话框,除了第一个标签对话框外的所有标签对话框都隐藏.

 

在这个例子中OnLButtonDown()这个消息响应函数用来显示或者隐藏不同的标签对话框,当然所被选中的标签的序号将保存在m_tabCurrent

 这个变量中,即就可以通过该变量的值来判断哪些对话框需要显示,那些对话框需要隐藏.SetRectangle()调整标签对话框的大小,使其和主对话框尺寸相匹配.

接下在主对话框的OnInitDialog消息响应函数中为所创建的标签控件增加所需的标签选项,在这个例子中为PageOne,PageTwo,PageThree.

 添加代码如下:

 

BOOL CMyTabExampleDlg::OnInitDialog()
{
	//comitted for brevity

	m_tabMyTabCtrl.InsertItem(0, _T("Tab One"));
	m_tabMyTabCtrl.InsertItem(1, _T("Tab Two"));
	m_tabMyTabCtrl.InsertItem(2, _T("Tab Three"));

	m_tabMyTabCtrl.Init();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

 MyTabCtrl.h代码如下:

 

#if !defined(AFX_MYTABCTRL_H__F3E8650F_019C_479F_9E0F_60FE1181F49F__INCLUDED_)
#define AFX_MYTABCTRL_H__F3E8650F_019C_479F_9E0F_60FE1181F49F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// MyTabCtrl.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CMyTabCtrl window

class CMyTabCtrl : public CTabCtrl
{
// Construction
public:
	CMyTabCtrl();
	CDialog *m_tabPages[3];
	int m_tabCurrent;
	int m_nNumberOfPages;

// Attributes
public:

// Operations
public:
	void Init();
	void SetRectangle();

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMyTabCtrl)
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CMyTabCtrl();

	// Generated message map functions
protected:
	//{{AFX_MSG(CMyTabCtrl)
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYTABCTRL_H__F3E8650F_019C_479F_9E0F_60FE1181F49F__INCLUDED_)

MyTabCtrl.cpp代码如下:

 

// MyTabCtrl.cpp : implementation file
//
/////////////////////////////////////////////////////
// This class is provided as is and Ben Hill takes no
// responsibility for any loss of any kind in connection
// to this code.
/////////////////////////////////////////////////////
// Is is meant purely as a educational tool and may
// contain bugs.
/////////////////////////////////////////////////////
// ben@shido.fsnet.co.uk
// http://www.shido.fsnet.co.uk
/////////////////////////////////////////////////////
// Thanks to a mystery poster in the C++ forum on
// www.codeguru.com I can't find your name to say thanks
// for your Control drawing code. If you are that person
// thank you very much. I have been able to use some of
// you ideas to produce this sample application.
/////////////////////////////////////////////////////

#include "stdafx.h"
#include "MyTabExample.h"
#include "MyTabCtrl.h"

#include "TabOne.h"
#include "TabTwo.h"
#include "TabThree.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMyTabCtrl

CMyTabCtrl::CMyTabCtrl()
{
	m_tabPages[0]=new CTabOne;
	m_tabPages[1]=new CTabTwo;
	m_tabPages[2]=new CTabThree;

	m_nNumberOfPages=3;
}

CMyTabCtrl::~CMyTabCtrl()
{
	for(int nCount=0; nCount < m_nNumberOfPages; nCount++){
		delete m_tabPages[nCount];
	}
}

void CMyTabCtrl::Init()
{
	m_tabCurrent=0;

	m_tabPages[0]->Create(IDD_TAB_ONE, this);
	m_tabPages[1]->Create(IDD_TAB_TWO, this);
	m_tabPages[2]->Create(IDD_TAB_THREE, this);

	m_tabPages[0]->ShowWindow(SW_SHOW);
	m_tabPages[1]->ShowWindow(SW_HIDE);
	m_tabPages[2]->ShowWindow(SW_HIDE);

	SetRectangle();
}

void CMyTabCtrl::SetRectangle()
{
	CRect tabRect, itemRect;
	int nX, nY, nXc, nYc;

	GetClientRect(&tabRect);
	GetItemRect(0, &itemRect);

	nX=itemRect.left;
	nY=itemRect.bottom+1;
	nXc=tabRect.right-itemRect.left-1;
	nYc=tabRect.bottom-nY-1;

	m_tabPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW);
	for(int nCount=1; nCount < m_nNumberOfPages; nCount++){
		m_tabPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW);
	}
}

BEGIN_MESSAGE_MAP(CMyTabCtrl, CTabCtrl)
	//{{AFX_MSG_MAP(CMyTabCtrl)
	ON_WM_LBUTTONDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyTabCtrl message handlers

void CMyTabCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
	CTabCtrl::OnLButtonDown(nFlags, point);

	if(m_tabCurrent != GetCurFocus()){
		m_tabPages[m_tabCurrent]->ShowWindow(SW_HIDE);
		m_tabCurrent=GetCurFocus();
		m_tabPages[m_tabCurrent]->ShowWindow(SW_SHOW);
		m_tabPages[m_tabCurrent]->SetFocus();
	}
}

Downloads

代码下载

如有什么不清楚的地方,可以给我留言:

机器人 2007-6-15 于北京

自绘按钮时,DoDataExchange函数不能被自动调用的问题.

Posted by 机器人 on 13th 六月 2007 in c/c++

当在控件编辑器里我们手动拖动一个按钮,将其风格设置成Ower draw(自绘),如果这里想通过VC++给我们提供的向导工具将其与一变量关联,发现关联的变量值为空,最后经过单步跟踪发现,原来DoDataExchange()函数没有被调用.

解决方法:

Described as follows from MSDN.

Never call this function directly. It is called by the UpdateData member function. Call UpdateData to initialize a dialog box’s controls or retrievedata from a dialog box.

所有在这个时候我们可以显示的调用DoDataExchange()这个函数,于是可以在对话框被初始化时显示的调用UpdateData()这个函数.

即:

 

BOOL CBaDlg::OnInitDialog()
{
	//Omitted for brevity
	UpdateData(FALSE); //注意这里,参数为FALSE
	//Omitted for brevity
}

 

当对话框在被初始化时,就可以显示的调用DoDataExchange()这个函数了.

注意:UpdataData()此时的参数为FALSE,而不是TRUE,这里我们的控件根本不存在,不能从其得到数据,所以会出现一个断言..

机器于 2007-6-13  下午 于 北京.