#
CCSprite的动作总结
1) CCAction如果是用具体类的actionWithFile(.....)或类似这样的接口的,则系统默认都是有自动维护引用计数的。即:这类对象创建出来后我们不需要释放。 2) CCSprite::runAction()接口请一定要与CCSprite::stopAction()或CCSprite::stopAllActions()配合使用。 3) CCSprite在执行CCMoveTo动作时,此时如果再对其设置帧动画动作,则这两个动作将是会共存执行的。 ------ 以上3点很重要。
在介绍前,先介绍说几点: 像opengl、directx是没有自带界面的,不过cocos可以为我们创建一个主界面,以用于显示游戏场景等。该主界面由CCDirector类实例实现。 cocos2d中所有需要用到的类,基本上都继承自CCObject类。它维护一个引用计数。所有从CCObject出来的类,都可以添加到自动释放池中进行自动维护。(与objective-c中的对象有点类似) 多数情况下,我们使用的对象,还是从CCNode出来的。CCNode是继承自CCObject的一个类。
下面简要介绍下各个主要类的功能 1) CCDirector 主要功能一:负责生成一个游戏主界面及游戏的初始化并。并在该界面上,渲染游戏场景。 主要功能二:负责控制各游戏场景间的相互切换。 主要功能三:负责初始化设置游戏的一些重要属性。如:游戏渲染的帧率。游戏视窗的尺寸(即:960 X 640 还是 480 X 320等) 主要功能四:负责清空游戏中的缓存数据(个人认为这个还是很重要的。因为到了手机上的游戏,硬内存不多,及时清除是很有必要的) 注意:该对象是单实例的,不需要用户维护。
2) CCScene 游戏中的场景类。当程序启动后,必须要为其设置一个场景,然后所有的对象在场景中场景。 主要功能:负责接收游戏中的任何对象。并将它们展现出来(如果可以展现的话,如:CCSprite等) 习惯上,用其维护多个的CCLayer
3) CCLayer 游戏中的层。习惯上,用其对资源展现进行布局管理。
4) CCSprite 游戏中的精灵对象。游戏中,必不可少的。会用到许多的可视对象。这些,多数都是精灵对象。
5) CCTargetedTouchDelegate 触摸响应类。提供响应触摸响应开始结束、触摸移动以及取消触摸几个接口。多是需要在其派生类中进行具体实现。如:一个精灵, 可支持触摸功能,则它需要自行实现这些接口。
以上只是简单介绍cocos2d中的几个较为主要及常用的类。初学,如有介绍不对的,请大家不吝指点。共同学习,共同进步。
编程时在C中需要用的类似Java的Split函数来解析一个长字符串,分割成子段使用,查函数库发现有strtok可用,看了示例却对它的用法有些疑惑为什么传值是NULL,于是上网查资料,找到这篇包含strtok函数源码的解释,转过来学习,仔细研读收获良多。
查函数库看到的strtok的解释和示例:
strtok
Syntax: #include <cstring> char *strtok( char *str1, const char *str2 );
The strtok() function returns a pointer to the next "token" in str1, where str2 contains the delimiters that determine the token. strtok() returns NULL if no token is found. In order to convert a string to tokens, the first call to strtok() should have str1 point to the string to be tokenized. All calls after this should have str1 be NULL.
For example: char str[] = "now # is the time for all # good men to come to the # aid of their country"; char delims[] = "#"; char *result = NULL; result = strtok( str, delims ); while( result != NULL ) { printf( "result is /"%s/"/n", result ); result = strtok( NULL, delims ); }
The above code will display the following output: result is "now " result is " is the time for all " result is " good men to come to the " result is " aid of their country"
下面是查到的网络文章对源码的解释:
原型:char * strtok(char * s,const char * ct)
用途:在s中找出以ct中的字符为分隔的字符串,即是源串中除去了含有分隔串中的所有字符后余下的一段段的字符串,每调用一次找到一串,找不到则返回空串。第一次调用必须传给它有效的字符串,第二次传NULL就可以了,每次调用返回找到的子串的时候都会把源串中该子串的尾部字符(原来是搜索串中的某一字符)修改成'/0'字符返回值为每次调用得到的字串。
下面看一下它的使用
char sbody[]= "Presetptz/r/nPreset1=hello/r/nPreset2=ttttt/r/nend/r/n";
///char *pbody= "Presetptz/r/nPreset1=hello/r/nPreset2=ttttt/r/nend/r/n";//errror char except[] = "12/r/n"; char *ptoken = NULL; ptoken = strtok(sbody,except); while(NULL!=ptoken) { printf("%s/n",ptoken); ptoken = strtok(NULL,except); }
输出为: Presetptz Preset =hello Preset =ttttt end 下面我们看一下它的源码:
char *___strtok;//关键这个全局指针变量
char * strtok(char * s,const char * ct) { char *sbegin, *send; sbegin = s ? s : ___strtok;//不等于NULL用原始字符串,否则用___strtok if (!sbegin) { return NULL;//结尾 } sbegin += strspn(sbegin,ct);// if (*sbegin == '/0') { ___strtok = NULL; return( NULL ); } send = strpbrk( sbegin, ct); if (send && *send != '/0') *send++ = '/0'; ___strtok = send; return (sbegin); }
其中: ssize_t strspn(const char* s,char*accept)// 返回accept中任一字符在s中第一次出现的位置
char * strpbrk(const char * cs,const char * ct)//返回指向ct中任一字符在cs中第一次出现的位置
这个函数不难分析,___strtok指针指向除去第一个有效字串后面的位置,到这里我们应该清楚为什么第二次调用时只要传NULL就可以了,当然这里也暴露了它的缺点,就是说不能有两个线程同时使用strtok否则就会出现错误。还有就是我在使用这个函数时碰到的问题,如上面的代码如果我把sbody换成 pbody,则编译没有问题,运行时就会出错,为什么?还是自己的基本功不扎实,pbody在是个静态字符串,说白了,它是在编译时就已经赋值而且相当于是一个const常量,不能被修改,而strtok是需要修改字符串的,所以产生问题不足为奇。
原文出处:http://www.cnblogs.com/finallyliuyu/archive/2010/10/11/1848130.html
一、C++中不能使用random()函数
==================================================================================
本文由青松原创并依GPL-V2及其后续版本发放,转载请注明出处且应包含本行声明。
C++中常用rand()函数生成随机数,但严格意义上来讲生成的只是伪随机数(pseudo-random integral number)。生成随机数时需要我们指定一个种子,如果在程序内循环,那么下一次生成随机数时调用上一次的结果作为种子。但如果分两次执行程序,那么由于种子相同,生成的“随机数”也是相同的。
在工程应用时,我们一般将系统当前时间(Unix时间)作为种子,这样生成的随机数更接近于实际意义上的随机数。给一下例程如下:
#include <iostream> #include <ctime> #include <cstdlib> using namespace std;
int main() { double random(double,double); srand(unsigned(time(0))); for(int icnt = 0; icnt != 10; ++icnt) cout << "No." << icnt+1 << ": " << int(random(0,10))<< endl; return 0; }
double random(double start, double end) { return start+(end-start)*rand()/(RAND_MAX + 1.0); } /* 运行结果 * No.1: 3 * No.2: 9 * No.3: 0 * No.4: 9 * No.5: 5 * No.6: 6 * No.7: 9 * No.8: 2 * No.9: 9 * No.10: 6 */ 利用这种方法能不能得到完全意义上的随机数呢?似乎9有点多哦?却没有1,4,7?!我们来做一个概率实验,生成1000万个随机数,看0-9这10个数出现的频率是不是大致相同的。程序如下: #include <iostream> #include <ctime> #include <cstdlib> #include <iomanip> using namespace std;
int main() { double random(double,double); int a[10] = {0}; const int Gen_max = 10000000; srand(unsigned(time(0))); for(int icnt = 0; icnt != Gen_max; ++icnt) switch(int(random(0,10))) { case 0: a[0]++; break; case 1: a[1]++; break; case 2: a[2]++; break; case 3: a[3]++; break; case 4: a[4]++; break; case 5: a[5]++; break; case 6: a[6]++; break; case 7: a[7]++; break; case 8: a[8]++; break; case 9: a[9]++; break; default: cerr << "Error!" << endl; exit(-1); } for(int icnt = 0; icnt != 10; ++icnt) cout << icnt << ": " << setw(6) << setiosflags(ios::fixed) << setprecision(2) << double(a[icnt])/Gen_max*100 << "%" << endl; return 0; }
double random(double start, double end) { return start+(end-start)*rand()/(RAND_MAX + 1.0); } /* 运行结果 * 0: 10.01% * 1: 9.99% * 2: 9.99% * 3: 9.99% * 4: 9.98% * 5: 10.01% * 6: 10.02% * 7: 10.01% * 8: 10.01% * 9: 9.99% */ 可知用这种方法得到的随机数是满足统计规律的。
另:在Linux下利用GCC编译程序,即使我执行了1000000次运算,是否将random函数定义了inline函数似乎对程序没有任何影响,有理由相信,GCC已经为我们做了优化。但是冥冥之中我又记得要做inline优化得加O3才行...
不行,于是我们把循环次数改为10亿次,用time命令查看执行时间: chinsung@gentoo ~/workspace/test/Debug $ time ./test 0: 10.00% 1: 10.00% 2: 10.00% 3: 10.00% 4: 10.00% 5: 10.00% 6: 10.00% 7: 10.00% 8: 10.00% 9: 10.00%
real 2m7.768s user 2m4.405s sys 0m0.038s chinsung@gentoo ~/workspace/test/Debug $ time ./test 0: 10.00% 1: 10.00% 2: 10.00% 3: 10.00% 4: 10.00% 5: 10.00% 6: 10.00% 7: 10.00% 8: 10.00% 9: 10.00%
real 2m7.269s user 2m4.077s sys 0m0.025s
前一次为进行inline优化的情形,后一次为没有作inline优化的情形,两次结果相差不大,甚至各项指标后者还要好一些,不知是何缘由...
=================================================================================
random函数不是ANSI C标准,不能在gcc,vc等编译器下编译通过。 可改用C++下的rand函数来实现。 1、C++标准函数库提供一随机数生成器rand,返回0-RAND_MAX之间均匀分布的伪随机整数。 RAND_MAX必须至少为32767。rand()函数不接受参数,默认以1为种子(即起始值)。 随机数生成器总是以相同的种子开始,所以形成的伪随机数列也相同,失去了随机意义。(但这样便于程序调试) 2、C++中另一函数srand(),可以指定不同的数(无符号整数变元)为种子。但是如果种子相同,伪随机数列也相同。一个办法是让用户输入种子,但是仍然不理想。 3、 比较理想的是用变化的数,比如时间来作为随机数生成器的种子。 time的值每时每刻都不同。所以种子不同,所以,产生的随机数也不同。 // C++随机函数(VC program) #include <stdio.h> #include <iostream> #include <time.h> using namespace std; #define MAX 100 int main(int argc, char* argv[]) { srand( (unsigned)time( NULL ) );//srand()函数产生一个以当前时间开始的随机种子.应该放在for等循环语句前面 不然要很长时间等待 for (int i=0;i<10;i++) cout<<rand()%MAX<<endl;//MAX为最大值,其随机域为0~MAX-1 return 0; } 二、rand()的用法 rand()不需要参数,它会返回一个从0到最大随机数的任意整数,最大随机数的大小通常是固定的一个大整数。 这样,如果你要产生0~10的10个整数,可以表达为: int N = rand() % 11; 这样,N的值就是一个0~10的随机数,如果要产生1~10,则是这样: int N = 1 + rand() % 10; 总结来说,可以表示为: a + rand() % n 其中的a是起始值,n是整数的范围。 a + rand() % (b-a+1) 就表示 a~b之间的一个随机数若要0~1的小数,则可以先取得0~10的整数,然后均除以10即可得到随机到十分位的10个随机小数,若要得到随机到百分位的随机小数,则需要先得到0~100的10个整数,然后均除以100,其它情况依此类推。 通常rand()产生的随机数在每次运行的时候都是与上一次相同的,这是有意这样设计的,是为了便于程序的调试。若要产生每次不同的随机数,可以使用srand( seed )函数进行随机化,随着seed的不同,就能够产生不同的随机数。 如大家所说,还可以包含time.h头文件,然后使用srand(time(0))来使用当前时间使随机数发生器随机化,这样就可以保证每两次运行时可以得到不同的随机数序列(只要两次运行的间隔超过1秒)。
原谅转载自:http://lukas06.blog.sohu.com/94010246.html
C++编译器在生成DLL时,会对导出函数进行名字改编,并且不同的编译器使用的改编规则不一样,因此改编的名字后的名字是不一样的。因此,如果利用不同的分别生成DLL文件和访问DLL文件的客户端,那么后者在访问该DLL文件的时候就会出现问题。例如:使用C++编写了一个DLL,而使用C语言编写的客户端进行访问就会出现问题。由于C++编译器已经对该导出函数名字进行了改编,所以用C语言编写的客户端就找不到DLL的导出函数。这就是DLL导出函数的名字改编问题。
如果希望动态链接库文件在编译时,导出函数的名称不要发生改变,那么在定义导出函数时,需要加上限定符:extern "C"。注意:双引号中的“C”一定要大写。
例如:Dll1.h头文件 #ifdef DLL1_API #else #define DLL1_API extern "C" _declspec(dllimport) #endif
DLL1_API int add(int a,int b); DLL1_API int subtract(int a,int b);
Dll1.cpp源文件 #define DLL1_API extern "C" _declspec(dllexport) #include "Dll1.h"
int add(int a,int b) { return a+b; } int subtract(int a,int b) { return a-b; } 这样利用dumpbin工具可以查看Dll2.dll的导出函数,>dumpbin -exports Dll2.dll,可以发现名字没有被改编。
利用限定符extern "C"可以解决C++和C语言之间相互调用时函数命名的问题。但是这种方法有一个缺陷:就是不能用于导出一个类的成员函数,而只能用于导出全局函数这种情况。
但是还有一个问题是,如果使用了标准调用约定,也就是pascal调用约定,WINAPI调用约定:_stdcall,此时即使使用了extern "C",仍然会出现导出函数名字被改编的问题。例如:使用C语言编写一个DLL文件,而客户端使用Delphi进行编写,那么在编写导出函数时,应该指定其使用标准的函数调用约定。此时,就会出现问题,即C语言编写的DLL文件的导出函数发生了名字改编。在这种情况下,可以使用一个称为模块定义文件(DEF)的方式解决名字改编问题。 例如:Dll2.def LIBRARY
EXPORTS add subtract 如果想使用与源文件中定义的不一样的函数名,可以按照以下语法:entryname=internalname 其中,entryname是要导出的符号名,而internalname是DLL中将要导出的函数名。
示例:
LIBRARY "VerifyLocalResType"
EXPORTS
VerifyDDSSize VerifyDDSType VerifyTGASize VerifyTGAType
无意中看到的一篇,挺不错的。呵呵。收藏下。
c++字符大小写转换
原为某著名软件公司试题,大意如下:请实现以下两个函数:char toupper(char c); char tolower(char c); 分别用于将传入的字母转为大写和小写。两个函数传入的参数取值范围都是[a-zA-Z],并且为ASCII编码,实现时不用检查参数合法性。两个函数的实现不能使用任何形式的分支、跳转等类型的语句或指令(特别说明:C/C++的条件操作符?:也是分支指令的一种形式,故而不能使用)。请尽可能多的写出你知道的办法。
分析解决:此题比较特别,限制严格,根据题目要求,排除if else、for、while、do while、switch case、?:外,能使用的语句就只有 =、+=、-=、&、|、^、++、--这些了,想要实现大小写转换,只能从这些语句中进行选择思考,由于字符集为ASCII编码,且范围明确为[a-zA-Z],我们知道,a-z对应ASCII值为97-122,A-Z对应ASCII为65-90,观察这些数字,可以发现97-122都大于96 ,65-90都大于64且小于96,进一步从二进制上考虑,则发现所有小写字母对应的二进制形式为011XXXXX,大写字母对应的二进制形式为010XXXXX,一到这里,哈哈,答案就出来了,通过位运算&和|就可实现了。代码描述如下
1char toupper(char c) 2{ 3 return c & 0x5F; 4} 5 6char tolower(char c) 7{ 8 //c | 0x60也行,但不太好,因为0x60会改变结果的第7位值,根据题目意思,改变第6位值为1,而其它位保持不变就够了。 9 return c | 0x20; 10} 至于其它方法,我就没多想了,还希望各位大侠多多分享一下哈。
#include <time.h> //* 方法一 time_t tt = time(NULL);//这句返回的只是一个时间cuo tm* t= localtime(&tt); printf("%d-%02d-%02d %02d:%02d:%02d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); //* 方法二 SYSTEMTIME st = {0}; GetLocalTime(&st); printf("%d-%02d-%02d %02d:%02d:%02d\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
下面几个,是网上找的:转载地址: http://apps.hi.baidu.com/share/detail/17815869
个人觉得第二种还是比较实用的,而且也是最常用的~
不过当计算算法耗时的时候,不要忘记second,不能只要用Milliseconds来减,不然后出现负值,若是算法耗时太长就得用minutes啦。再不然,就hours……
//方案— 优点:仅使用C标准库;缺点:只能精确到秒级 #include <time.h> #include <stdio.h> int main( void ) { time_t t = time(0); char tmp[64]; strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A 本年第%j天 %z",localtime(&t) ); puts( tmp ); return 0; } size_t strftime(char *strDest, size_t maxsize, const char *format, const struct tm *timeptr); 根据格式字符串生成字符串。 struct tm *localtime(const time_t *timer); 取得当地时间,localtime获取的结果由结构tm返回 返回的字符串可以依下列的格式而定: %a 星期几的缩写。Eg:Tue %A 星期几的全名。 Eg: Tuesday %b 月份名称的缩写。 %B 月份名称的全名。 %c 本地端日期时间较佳表示字符串。 %d 用数字表示本月的第几天 (范围为 00 至 31)。日期 %H 用 24 小时制数字表示小时数 (范围为 00 至 23)。 %I 用 12 小时制数字表示小时数 (范围为 01 至 12)。 %j 以数字表示当年度的第几天 (范围为 001 至 366)。 %m 月份的数字 (范围由 1 至 12)。 %M 分钟。 %p 以 ''AM'' 或 ''PM'' 表示本地端时间。 %S 秒数。 %U 数字表示为本年度的第几周,第一个星期由第一个周日开始。 %W 数字表示为本年度的第几周,第一个星期由第一个周一开始。 %w 用数字表示本周的第几天 ( 0 为周日)。 %x 不含时间的日期表示法。 %X 不含日期的时间表示法。 Eg: 15:26:30 %y 二位数字表示年份 (范围由 00 至 99)。 %Y 完整的年份数字表示,即四位数。 Eg:2008 %Z(%z) 时区或名称缩写。Eg:中国标准时间 %% % 字符。
//方案二 优点:能精确到毫秒级;缺点:使用了windows API #include <windows.h> #include <stdio.h> int main( void ) { SYSTEMTIME sys; GetLocalTime( &sys ); printf( "%4d/%02d/%02d %02d:%02d:%02d.%03d 星期%1d\n",sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute, sys.wSecond,sys.wMilliseconds,sys.wDayOfWeek); return 0; } //方案三,优点:利用系统函数,还能修改系统时间 //此文件必须是c++文件 #include<stdlib.h> #include<iostream> using namespace std; void main() { system("time"); } //方案四,将当前时间折算为秒级,再通过相应的时间换算即可 //此文件必须是c++文件 #include<iostream> #include<ctime> using namespace std; int main() { time_t now_time; now_time = time(NULL); cout<<now_time; return 0; } 注意:GetLocalTime()与GetSystemTime()是有区别的
http://www.4ucode.com/Study/Topic/1996448VS2008中创建DLL工程 文件->新建->项目->visual c++->win32->win32控制台应用程序(win32项目也可以) 填写项目名称MyDLL->确定->下一步->DLL(附加选项 对空项目打钩)->完成。 到这里DLL工程就创建完毕了,下面新建两个文件testDLL.cpp和testDLL.h。 C++ .h 文件 #define DLL_EXPORT __declspec(dllexport)
extern "C" DLL_EXPORT int MyMinus(int a,int b);
.cpp 文件 // testDLL.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "testDLL.h"
int MyMinus(int a,int b)
{
return (a-b);
}
delphi调用代码 unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
edt1: TEdit;
edt2: TEdit;
lbl1: TLabel;
lbl2: TLabel;
lbl3: TLabel;
btn1: TButton;
lbl4: TLabel;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
const
TestDll = 'testDLL.dll';
function MyMinus(a:Integer;b:Integer):Integer;cdecl;external TestDll;
implementation
{$R *.dfm}
procedure TForm1.btn1Click(Sender: TObject);
begin
lbl4.Caption := IntToStr(MyMinus(StrToInt(edt1.Text),StrToInt(edt2.Text)));
end;
end.
原谅转载自: http://tech.ddvip.com/2008-11/122662837992492.htmlVisual C++中提供的MFC类CtreeCtrl(树型控件)用来显示具有一定层次结构的数据项时方便、直观,所以它已经被广泛地应用在各种软件中,如资源管理器中的磁盘目录就用的是树型控件,我们在编程中也会经常用到这个控件,但是这个控件也有缺陷,那就是它并不直接支持拖动节点等高级特性,这使得程序员在编程时使用它受到了很大限制,同时又给软件用户带来了一些不便。为此,本实例通过从 CTreeCtrl 中派生了一个类 CXTreeCtrl ,实现树型控件中节点的拖动。这个类具有如下的功能:⑴ 基本项目条拖动的实现;⑵ 处理项目条的无意拖动;⑶ 能处理项目条拖动过程中的滚动问题;⑷ 拖动过程中节点会智能展开。程序编译运行后的效果如图所示:
570)?'570px':'auto';
}" src="http://img.ddvip.com/2008_11_14/1226628379_ddvip_9835.png">
图一:树型控件节点拖动示例
一、实现方法
我们针对上述自定义类的实现功能,介绍实现思路和方法。
(1)基本项目条拖动的实现
当我们要拖动树型控件的一个项目条时,树型控件会给它的父窗口发送一个TVN_BEGINDRAG通知消息,所以可以在此消息的响应函数中,调用 CTreeCtrl ::CreateDragImage ()函数创建表示当前项目条正处在拖动操作中的图象,该函数创建的图象由项目条的图象和标签文本组成。创建了拖动图象后,调用CImageList::BeginDrag()函数指定拖动图象的热点位置,然后调用CImageList::DragEnter()函数显示拖动图象。接下来处理 WM_MOUSEMOVE 消息用于更新拖动图象,我们想让移动中的图象经过某些项目时高亮度显示,这可以调用 CTreeCtrl ::SelectDropTarget() 来实现。在调用 SelectDropTarget()函数之前,需要先调用CImageList::DragShowNolock ( false )函数来隐藏图象列表,然后再调用CImageList::DragShowNolock ( true ) 函数来恢复图象列表的显示,这样就不会在拖动过程中留下难看的轨迹。最后我们处理 WM_LBUTTONUP 消息用于完成拖动操作,在该消息响应函数中,我们需要完成结束拖动图象的显示、删除拖动图象、释放鼠标、节点的拷贝/删除等操作。在节点的拷贝/删除操作中,如果是父节点拖到子节点上,我们可以先将父节点拷到根结点下的临时节点中,再从临时结点处拷到子节点,然后将根结点下的临时节点删除,这样做的目的是防止产生异常。
(2)处理项目条的无意拖动
牐犎绻在鼠标按下时不小心移动了鼠标,这时系统就认为产生了一个移动操作,这就产生了误操作。解决这个问题的方法是设置时间延迟,也就是说当用户按下鼠标后必须在原位置停留一段时间,才能激活拖动操作。
(3)处理拖动过程中的滚动问题
当我们拖动树型控件的项目条时,如果目的节点不可见,则需要拖动滚动条或收拢其它一些节点以使得目的节点显示出来,无疑,这会给我们带来很大的不便。为此就要给树型控件添加自动滚动支持。首先设置一个定时器,在 WM_TIMER 消息中检测鼠标的位置,如果靠近树型控件的下边缘,则使得控件向下滚动。靠近上边缘则向上滚动。滚动速度根据鼠标的位置确定。
(4)拖动过程中节点的智能展开
为了实现在拖动过程中鼠标停留在某个节点上一段时间后,该节点会自动展开的功能。设置一个定时器,当鼠标在拖动过程中停止在某个节点上时,定时器被启动,再设置一变量保存当前的鼠标位置。
二、编程步骤
1、 新建一对话框工程DragTree,编辑资源,在对话框中加入一树型控件IDC_TREE ,属性设置为:Has Buttons、Has Lines、Lines at root、Edit Labels、Border;
2、 使用Class Wizard给该控件添加一个成员变量 m_wndTree ,在代码部分将该控件的类型修改为CXTreeCtrl。
3、 在对话框的OnInitDialog()函数中添加代码,初始化树型控件的项目条;
4、 制作一个图像资源(ID为IDB_TREEIMAGE),其中包含两个小图标,用来作为树型控件项目条的显示图标;
5、 添加代码,编译运行程序。
三、程序代码
// XTreeCtrl.h : header file #if !defined(AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_) #define AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CXTreeCtrl : public CTreeCtrl { // Construction public: CXTreeCtrl(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CXTreeCtrl) //}}AFX_VIRTUAL // Implementation public: virtual ~CXTreeCtrl(); // Generated message map functions protected: UINT m_TimerTicks; //处理滚动的定时器所经过的时间 UINT m_nScrollTimerID; //处理滚动的定时器 CPoint m_HoverPoint; //鼠标位置 UINT m_nHoverTimerID; //鼠标敏感定时器 DWORD m_dwDragStart; //按下鼠标左键那一刻的时间 BOOL m_bDragging; //标识是否正在拖动过程中 CImageList* m_pDragImage; //拖动时显示的图象列表 HTREEITEM m_hItemDragS; //被拖动的标签 HTREEITEM m_hItemDragD; //接受拖动的标签 //{{AFX_MSG(CXTreeCtrl) afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnTimer(UINT nIDEvent); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: HTREEITEM CopyBranch(HTREEITEM htiBranch,HTREEITEM htiNewParent,HTREEITEM htiAfter); HTREEITEM CopyItem(HTREEITEM hItem,HTREEITEM htiNewParent,HTREEITEM htiAfter); }; #endif //////////////////////////////////////////////////////////// CXTreeCtrl #include "stdafx.h" #include "DragTree.h" #include "XTreeCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define DRAG_DELAY 60 CXTreeCtrl::CXTreeCtrl() { m_bDragging = false; } CXTreeCtrl::~CXTreeCtrl() {} BEGIN_MESSAGE_MAP(CXTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CXTreeCtrl) ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag) ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_LBUTTONDOWN() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() void CXTreeCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; *pResult = 0; //如果是无意拖曳,则放弃操作 if( (GetTickCount() - m_dwDragStart) < DRAG_DELAY ) return; m_hItemDragS = pNMTreeView->itemNew.hItem; m_hItemDragD = NULL; //得到用于拖动时显示的图象列表 m_pDragImage = CreateDragImage( m_hItemDragS ); if( !m_pDragImage ) return; m_bDragging = true; m_pDragImage->BeginDrag ( 0,CPoint(8,8) ); CPoint pt = pNMTreeView->ptDrag; ClientToScreen( &pt ); m_pDragImage->DragEnter ( this,pt ); //"this"将拖曳动作限制在该窗口 SetCapture(); m_nScrollTimerID = SetTimer( 2,40,NULL ); } void CXTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) { HTREEITEM hItem; UINT flags; //检测鼠标敏感定时器是否存在,如果存在则删除,删除后再定时 if( m_nHoverTimerID ) { KillTimer( m_nHoverTimerID ); m_nHoverTimerID = 0; } m_nHoverTimerID = SetTimer( 1,800,NULL ); //定时为 0.8 秒则自动展开 m_HoverPoint = point; if( m_bDragging ) { CPoint pt = point; CImageList::DragMove( pt ); //鼠标经过时高亮显示 CImageList::DragShowNolock( false ); //避免鼠标经过时留下难看的痕迹 if( (hItem = HitTest(point,&flags)) != NULL ) { SelectDropTarget( hItem ); m_hItemDragD = hItem; } CImageList::DragShowNolock( true ); //当条目被拖曳到左边缘时,将条目放在根下 CRect rect; GetClientRect( &rect ); if( point.x < rect.left + 20 ) m_hItemDragD = NULL; } CTreeCtrl::OnMouseMove(nFlags, point); } void CXTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point) { CTreeCtrl::OnLButtonUp(nFlags, point); if( m_bDragging ) { m_bDragging = FALSE; CImageList::DragLeave( this ); CImageList::EndDrag(); ReleaseCapture(); delete m_pDragImage; SelectDropTarget( NULL ); if( m_hItemDragS == m_hItemDragD ) { KillTimer( m_nScrollTimerID ); return; } Expand( m_hItemDragD,TVE_EXPAND ); HTREEITEM htiParent = m_hItemDragD; while( (htiParent = GetParentItem(htiParent)) != NULL ) { if( htiParent == m_hItemDragS ) { HTREEITEM htiNewTemp = CopyBranch( m_hItemDragS,NULL,TVI_LAST ); HTREEITEM htiNew = CopyBranch( htiNewTemp,m_hItemDragD,TVI_LAST ); DeleteItem( htiNewTemp ); SelectItem( htiNew ); KillTimer( m_nScrollTimerID ); return; } } HTREEITEM htiNew = CopyBranch( m_hItemDragS,m_hItemDragD,TVI_LAST ); DeleteItem( m_hItemDragS ); SelectItem( htiNew ); KillTimer( m_nScrollTimerID ); } } HTREEITEM CXTreeCtrl::CopyItem(HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷贝条目 { TV_INSERTSTRUCT tvstruct; HTREEITEM hNewItem; CString sText; //得到源条目的信息 tvstruct.item.hItem = hItem; tvstruct.item.mask=TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE; GetItem( &tvstruct.item ); sText = GetItemText( hItem ); tvstruct.item.cchTextMax = sText.GetLength (); tvstruct.item.pszText = sText.LockBuffer (); //将条目插入到合适的位置 tvstruct.hParent = htiNewParent; tvstruct.hInsertAfter = htiAfter; tvstruct.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT; hNewItem = InsertItem( &tvstruct ); sText.ReleaseBuffer (); //限制拷贝条目数据和条目状态 SetItemData( hNewItem,GetItemData(hItem) ); SetItemState( hNewItem,GetItemState(hItem,TVIS_STATEIMAGEMASK),TVIS_STATEIMAGEMASK); return hNewItem; } HTREEITEM CXTreeCtrl::CopyBranch(HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷贝分支 { HTREEITEM hChild; HTREEITEM hNewItem = CopyItem( htiBranch,htiNewParent,htiAfter ); hChild = GetChildItem( htiBranch ); while( hChild != NULL ) { CopyBranch( hChild,hNewItem,htiAfter ); hChild = GetNextSiblingItem( hChild ); } return hNewItem; } void CXTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) //处理无意拖曳 { m_dwDragStart = GetTickCount(); CTreeCtrl::OnLButtonDown(nFlags, point); } void CXTreeCtrl::OnTimer(UINT nIDEvent) { //鼠标敏感节点 if( nIDEvent == m_nHoverTimerID ) { KillTimer( m_nHoverTimerID ); m_nHoverTimerID = 0; HTREEITEM trItem = 0; UINT uFlag = 0; trItem = HitTest( m_HoverPoint,&uFlag ); if( trItem && m_bDragging ) { SelectItem( trItem ); Expand( trItem,TVE_EXPAND ); } } //处理拖曳过程中的滚动问题 else if( nIDEvent == m_nScrollTimerID ) { m_TimerTicks++; CPoint pt; GetCursorPos( &pt ); CRect rect; GetClientRect( &rect ); ClientToScreen( &rect ); HTREEITEM hItem = GetFirstVisibleItem(); if( pt.y < rect.top +10 ) { //向上滚动 int slowscroll = 6 - (rect.top + 10 - pt.y )/20; if( 0 == (m_TimerTicks % ((slowscroll > 0) ? slowscroll : 1)) ) { CImageList::DragShowNolock ( false ); SendMessage( WM_VSCROLL,SB_LINEUP ); SelectDropTarget( hItem ); m_hItemDragD = hItem; CImageList::DragShowNolock ( true ); } } else if( pt.y > rect.bottom - 10 ) { //向下滚动 int slowscroll = 6 - (pt.y - rect.bottom + 10)/20; if( 0 == (m_TimerTicks % ((slowscroll > 0) ? slowscroll : 1)) ) { CImageList::DragShowNolock ( false ); SendMessage( WM_VSCROLL,SB_LINEDOWN ); int nCount = GetVisibleCount(); for( int i=0 ; i<nCount-1 ; i++ ) hItem = GetNextVisibleItem( hItem ); if( hItem ) SelectDropTarget( hItem ); m_hItemDragD = hItem; CImageList::DragShowNolock ( true ); } } } else CTreeCtrl::OnTimer(nIDEvent); } //////////////////////////////////////////////////////////// BOOL CDragTreeDlg::OnInitDialog() { CDialog::OnInitDialog(); …………………….//此处代码省略 // TODO: Add extra initialization here m_image.Create ( IDB_TREEIMAGE,16,1,RGB(255,255,255) ); m_wndTree.SetImageList ( &m_image,TVSIL_NORMAL ); HTREEITEM hti1 = m_wndTree.InsertItem ( _T("唐詩"),0,1 ); HTREEITEM hti2 = m_wndTree.InsertItem ( _T("宋詞"),0,1 ); HTREEITEM hti3 = m_wndTree.InsertItem ( _T("元曲"),0,1 ); HTREEITEM hti4 = m_wndTree.InsertItem ( _T("李白"),0,1,hti1 ); m_wndTree.InsertItem ( _T("靜夜思(床前明月光)"),0,1,hti4 ); m_wndTree.InsertItem ( _T("將進酒(君不見黃河之水天上來)"),0,1,hti4 ); m_wndTree.InsertItem ( _T("望廬山瀑布(日照香爐生紫煙)"),0,1,hti4 ); m_wndTree.InsertItem ( _T("蜀道難(噫吁戲,危乎高哉)"),0,1,hti4 ); HTREEITEM hti5 = m_wndTree.InsertItem ( _T("杜甫"),0,1,hti1 ); m_wndTree.InsertItem ( _T("蜀相(丞相祠堂何處尋)"),0,1,hti5 ); m_wndTree.InsertItem ( _T("春望(國破山河在)"),0,1,hti5 ); m_wndTree.InsertItem ( _T("茅屋為秋風所破歌(八月秋高風怒號)"),0,1,hti5 ); HTREEITEM hti6 = m_wndTree.InsertItem ( _T("白居易"),0,1,hti1 ); m_wndTree.InsertItem ( _T("長恨歌(漢皇重色思傾國)"),0,1,hti6 ); m_wndTree.InsertItem ( _T("琵琶行並序(潯陽江頭夜送客)"),0,1,hti6 ); m_wndTree.InsertItem ( _T("李清照"),0,1,hti2 ); m_wndTree.InsertItem ( _T("柳永"),0,1,hti2 ); return TRUE; // return TRUE unless you set the focus to a control }
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_CLEAR, _T("剪切(&X)"));
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_COPY, _T("复制(&C)"));
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_PASTE, _T("粘贴(&P)"));
menu.AppendMenuW(MF_SEPARATOR);
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_CLEAR, _T("清除\tCtrl + C"));
ClientToScreen(&point);
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTALIGN, point.x, point.y, this);
CView::OnRButtonDown(nFlags, point);
}
|