[转自小兵的工具箱]C++ Builder 基础
1. C++ Builder 网上资源
C++ Builder 研究
http://www.ccrun.com/
C++ Builder 程序员
http://mybcb.diy.myrice.com/
电脑学习 C++ Builder 版块
http://www.itcomputer.com.cn/Programs/C/CB/
c++ Builder 快捷键大全:
http://www.itcomputer.com.cn/Programs/C/CB/200512/11211.html
C++ Builder IDE 使用技巧:
http://www.itcomputer.com.cn/Programs/C/CB/200512/11199.html
---------------------------------------------------------------------------
2. C++ Builder 里的快捷键
这里只是一个摘要式的说明,摘出了我认为比较有用也比较常用的,详情请参考:
http://www.itcomputer.com.cn/Programs/C/CB/200512/11211.html
说明:其中的区块操作快捷键要在选定某块之后才能起作用。
名称 作用 备注
系统:
F3 查找下一个
F5 设置/取消断点
F7 跟踪(进入子程序)
F8 单步(不进入子程序)
F9 运行工程,包括了编译
F11 切换到对象查看器
F12 切换设计窗体和代码窗口 很有用,特别是在窗口互相覆盖的时候
Ctrl+F2 结束调试或运行
Ctrl+F5 设置Watch变量
Ctrl+F9 编译工程
Alt+F9 编译单元
代码自动完成类:
Ctrl+J 代码模板 很有用,尤其是对代码规范而言
Ctrl+Space 代码补全 很有用,可以省很多敲键盘和查错的时间
这个因为跟 Windows 默认的中英文输入法切换快捷键冲突,所以需要作如下设置:
1) 右键点击任务栏上的输入法图标,选择“设置”;
2) 在跳出的窗口中点击“键设置”按钮;
3) 在跳出的窗口中选中“中文(简体)输入法 - 输入法/非输入法切换”,勾选“启用按键顺序”,然后点“确定”即可;
编辑类:
Ctrl+C 复制 基本快捷键
Ctrl+V 粘贴 基本快捷键
粘贴大段代码时请特别注意,很多问题来源于复制粘贴。
Ctrl+X 剪切 基本快捷键
Ctrl+S 保存 基本快捷键
Ctrl+Home 跳到文件开头 较有用
Ctrl+End 跳到文件结尾 较有用
Ctrl+Left 跳到上一个字 较有用
Ctrl+Right 跳到下一个字 较有用
Ctrl+Up 鼠标位置不动,滚动条上移
Ctrl+Down 鼠标位置不动,滚动条下移
Ctrl+Y 删除一行 较有用,减少无谓的空行,有助于代码规范
Ctrl+Shift+Y 删除到行尾
Ctrl+T 删除到字尾
Ctrl+Shift+I 块增加一级缩进 很有用,有助于代码规范
Ctrl+Shift+U 块减少一级缩进 很有用,有助于代码规范
Ctrl+K+I 同 Ctrl+Shift+I
Ctrl+K+U 同 Ctrl+Shift+U
---------------------------------------------------------------------------
3. 编辑器选项设置
主菜单里的“Tools” -> “Editor Options...” 或者在编辑器窗口右键点击,在弹出的菜单里选择“Properties”。
---------------------------------------------------------------------------
4. 让 Form 不自动创建
在主菜单里的“Project” -> “Options...” -> “Forms”中,将不想要其自动启动的 Form 从“Auto-created forms”移到“Available forms”即可。
---------------------------------------------------------------------------
5. 关掉数据库的登录窗口
将 TADOConnection 控件的 LoginPrompt 属性设为 false。
---------------------------------------------------------------------------
6. 设置密码框
将文本框的 PasswordChar 属性设为 * (其他字符也行,除了 #0 之外)。这里设为啥,输入密码时就显示啥。
---------------------------------------------------------------------------
7. 登录窗口先于多文档(MDI)主窗口显示
在项目源文件(Project Source)里先 new 一个登录窗口,登录成功再创建主窗口。如下所示:
Application->Initialize();
frmLogin = new TfrmLogin(Application);
frmLogin->ShowModal();
Application->CreateForm(__classid(TfrmMain), &frmMain);
Application->Run();
剩下的事情就是在 TfrmLogin 的登录成功事件中 Close() 关闭登录窗口,登录失败事件中 Application->Terminate() 关闭主程序。
---------------------------------------------------------------------------
8. 字符串下标
AnsiString, String 的下标都是从一开始的,从 1 到 length()。
---------------------------------------------------------------------------
9. 关于 TEdit 控件的复合赋值
用 += 对 TEdit 控件的 Text 属性赋值无效。编译能通过,但赋不上值。
---------------------------------------------------------------------------
10. OnKeyPress 与 OnKeyDown 的区别
OnKeyPress是经过系统转换后的按键,根据ASCII码区分的,比方说你按了Shift+a,则相当于输入了A。这个函数相当于处理WM_CHAR消息。
OnKeyDown没有经过系统转换,是根据虚拟按键值来判断的,比方说你按下了Ctrl+F3,则相当于在控制字符方面输入了Control,并按下了VK_F3这个虚拟键。这个函数相当于处理WM_KEYDOWN消息。
---------------------------------------------------------------------------
11. 让MDI程序不重复打开相同子窗口
1) 先写一个 openForm 函数,作用是判断将要创建的子窗口是否已存在。若已存在,则显示之,并将鼠标焦点转移到该窗口,并返回 true;若不存在,则返回 false。
bool __fastcall TfrmMain::openForm(TForm *frm)
{
bool frmExist = false;
// 判断该窗口是否已经被创建。
if (frm == NULL)
{
return false;
}
for (int i = 0; i < Screen->FormCount; i++)
{
if (Screen->Forms[i]->ClassType() == frm->ClassType())
{
frmExist = true;
break;
}
}
if (false == frmExist)
{
return false;
}
// 如果此窗口已经被创建,则判断该窗口当前状态,
// 如果其当前状态为最小化状态或隐藏状态,则显示之。
if (wsMinimized == frm->WindowState)
{
ShowWindow(frm->Handle, SW_SHOWNORMAL);
}
else
{
ShowWindow(frm->Handle, SW_SHOWNA);
}
if (! frm->Visible)
{
frm->Visible = true;
}
// 将光标的焦点转移到此窗口上。
frm->BringToFront();
frm->SetFocus();
return true;
}
2) 在创建子窗口时,先调用该函数。若返回 false (即不存在),则创建之。
if (false == openForm(formName))
{
formName = new formType(Application);
}
---------------------------------------------------------------------------
12. ADOQuery 的使用
★ 一个例子
if (! ADOConnection1->Connected)
{
ADOConnection1->Connected = true;
}
ADOQuery1->Active = false;
ADOQuery1->SQL->Clear();
ADOQuery1->SQL->Add("SELECT * FROM table_name");
ADOQuery1->Active = true;
Edit1->Text = ADOQuery1->RecordCount;
Edit2->Text = ADOQuery1->Fields->Fields[1]->AsString;
ADOQuery1->Next();
Edit3->Text = ADOQuery1->Fields->Fields[1]->AsString;
★ 往数据集里添加纪录
TADOQuery1->Edit();
TADOQuery1->Append();
★ 从数据集里删除纪录
TADOQuery1->Edit();
TADOQuery1->Delete();
★ 保存修改到数据集
TADOQuery1->Edit();
TADOQuery1->Post();
★ 取消对数据集的修改
TADOQuery1->Edit();
TADOQuery1->Cancel();
TADOQuery1->Close();
---------------------------------------------------------------------------
13. 关闭窗口事件
OnClose 消息阶段的事件,控制关闭时执行什么动作。这里也可以控制不允许关闭窗口,方法是将 Action 参数设为 caNone。
OnCloseQuery 处理阶段的事件,控制是否允许关闭窗口。如果不允许关闭,将 CanClose 参数设为 false。
---------------------------------------------------------------------------
14. 如何让窗口右上角的按钮不可用?
1) 直接重载窗口过程将关闭功能屏蔽
头文件
class TForm1:public TForm
{
private:
public:
protected:
virtual void __fastcall WndProc(TMessage &Message)
}
源文件
void __fastcall TForm1::WndProc(TMessage &Message)
{
if(Message.Msg==WM_SYSCOMMAND)
{
if(Message.WParam==SC_CLOSE)
{
Message.WParam=0;
}
}
}
2)
★
HMENU hSysMenu = GetSystemMenu(Handle,0);//得到系统菜单句柄
EnableMenuItem(hSysMenu,SC_CLOSE,MF_DISABLED);//使"关闭"项不可用.此时"关闭"按钮变灰,但系统菜单中的"关闭"没有变灰,只是不能选择.
第二句也可以改为:
EnableMenuItem(hSysMenu,SC_CLOSE,MF_GRAYD);与上面不同的是,这时系统菜单中的"关闭"项也变灰了.
★
HWND m_hWnd = GetSystemMenu(Form_shuo->Handle,false); //关闭按钮
EnableMenuItem(m_hWnd,SC_CLOSE,MF_GRAYED);
---------------------------------------------------------------------------
15. 窗口最大化不正常
有时候在窗口界面设置里将 WindowState 属性直接设为 wsMaximized 得到的最大化效果不理想(比如控件没有像设想的那样靠右排列、树视图下边缘超出可视范围等),这时候可以通过在窗口的 Show 或 ShowModal 语句后加上设置 WindowState 属性为 wsMaximized 的语句来得到理想的效果。
---------------------------------------------------------------------------
16. 隐藏程序主窗体
程序一开始就隐藏窗口,在 工程名.cpp 中修改:
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->ShowMainForm = false;
ShowWindow(Application->Handle, SW_HIDE);
Application->Run();
想显示程序时执行下面这两行代码:
ShowWindow(Application->Handle, SW_SHOW);
Application->MainForm->Visible = true;
注:根据我的经验,要想在程序一开始隐藏主窗体,只要在 工程名.cpp 文件中加入
Application->ShowMainForm = false;
即可,其他地方想隐藏只要执行 Hide() 方法;而想要显示时,只要执行 Show() 方法即可。
---------------------------------------------------------------------------
17. 由弹出的右键菜单的菜单项获取触发弹出事件的组件(多个组件关联到一个右键菜单时有用)
例:
void __fastcall TSomeForm::pmiSomeClick(TObject *Sender)
{
TMenuItem *mi = static_cast<TMenuItem *>(Sender);
TPopupMenu *pm = static_cast<TPopupMenu *>(mi->GetParentComponent());
TComponent *comp = pm->PopupComponent;
// 接下来就可以对 comp 进行操作了。
...
}
---------------------------------------------------------------------------
18. 让右键也能选中 TreeView,并且正确响应相应节点(TreeView->Selected 节点自动设为右击的节点)
首先将 TreeView 的 RightClickSelect 属性设为 true,再设置其 OnContextPopup 事件如下所示:
void __fastcall TSomeForm::tvSomeContextPopup(
TObject *Sender, TPoint &MousePos, bool &Handled)
{
TTreeView *tv = static_cast<TTreeView *>(Sender);
// 取鼠标点的地方的TTreeNode
TTreeNode *node = tv->GetNodeAt(MousePos.x, MousePos.y);
if (NULL == node)
{
// 鼠标点的地方没有TTreeNode,菜单不显
Handled = true;
}
else
{
// 右键选中TTreeNode
tv->Selected = node;
}
}
---------------------------------------------------------------------------
19. 在右键菜单弹出之前对右键菜单进行设置
在 OnContextPopup 事件中设置。
---------------------------------------------------------------------------
20. 将英文提示文字(Yes, No, OK, Cancel...)修改为中文
1) 在 BCB6 安装目录的 Source/Vcl 目录下找到 consts.pas,并复制一份到你的工程目录下;
2) 将刚拷过来的 consts.pas 文件中提示文字由英文改为中文;
3) 在当前工程中加入刚才修改过的 dialogs.pas,具体操作:Project-->Add to Project-->找到该文件,添加进来即可;
4) 设置一下工程选项:Project-->Options-->Packages-->取消 Build with runtime packages 前面的对钩,确定;
5) 重新编译运行工程即可。
出处:http://pc.8s8s.com/pc/pc12990.htm
参考:http://www.ccrun.com/article.asp?d=w74av8&i=620(通过消息实现自定义输入框)
注:其他想改动源代码并且应用到现有的 BCB 工程中去的情况估计也类似,但没测试过。
---------------------------------------------------------------------------
21. 显示和屏蔽任务栏
// 屏蔽任务栏
ShowWindow(FindWindow("Shell_TrayWnd", NULL), SW_HIDE);
// 显示任务栏
ShowWindow(FindWindow("Shell_TrayWnd", NULL), SW_SHOW);
---------------------------------------------------------------------------
22. 禁用和启用任务栏
// 禁用任务栏
EnableWindow(FindWindow("Shell_traywnd", NULL), false);
// 启用任务栏
EnableWindow(FindWindow("Shell_traywnd", NULL), true);
---------------------------------------------------------------------------
23. 自动隐藏任务栏
先定义如下函数:
void __fastcall TfrmSomeForm::autohideTaskBar(bool isAutohide)
{
APPBARDATA AppBarData;
memset(&AppBarData, 0, sizeof(APPBARDATA));
AppBarData.cbSize = sizeof(APPBARDATA);
AppBarData.hWnd = FindWindow("Shell_TrayWnd", NULL);
if (isAutohide)
{
// 设定任务栏自动隐藏
AppBarData.lParam = ABS_AUTOHIDE;
}
else
{
// 取消任务栏的自动隐藏
AppBarData.lParam = ABS_ALWAYSONTOP;
}
SHAppBarMessage(ABM_SETSTATE, &AppBarData);
}
要自动隐藏任务栏时,如下调用该函数:
autohideTaskBar(true);
要取消任务栏的自动隐藏时,如下调用该函数:
autohideTaskBar(false);
---------------------------------------------------------------------------
24. 显示和屏蔽桌面图标
// 屏蔽桌面图标
ShowWindow(FindWindow("ProgMan", NULL), SW_HIDE);
// 显示桌面图标
ShowWindow(FindWindow("ProgMan", NULL), SW_SHOW);
---------------------------------------------------------------------------
25. 屏蔽系统热键
1) 方法一,比较彻底,除 Ctrl + Alt + Del 之外全被屏蔽。
在程序中设置如下全局变量和全局函数:
// 钩子句柄
HHOOK OldHook;
long CALLBACK KeyProc(int code, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *pkbhs;
if (code < 0)
{
return CallNextHookEx(OldHook, code, wParam, lParam);
}
pkbhs = (KBDLLHOOKSTRUCT*) lParam;
if (pkbhs->vkCode == 91 || pkbhs->vkCode == VK_RWIN)//封锁 win 键
{
return 1;
}
if (pkbhs->vkCode == VK_ESCAPE && GetAsyncKeyState(VK_CONTROL) < 0)
{
return 1; //封锁 CTRL+ESC
}
if (pkbhs->vkCode == VK_TAB && pkbhs->flags&LLKHF_ALTDOWN)
{
return 1; //封锁 ALT+TAB
}
if (pkbhs->vkCode == 115 && pkbhs->flags & LLKHF_ALTDOWN)
{
return 1; //封锁 ALT+F4
}
if (pkbhs->vkCode == VK_ESCAPE && pkbhs->flags & LLKHF_ALTDOWN)
{
return 1; //封锁 ALT+ESC
}
if (pkbhs->vkCode == VK_SPACE && GetAsyncKeyState(VK_CONTROL) < 0)
{
return 1; //封锁 CTRL+ ' '
}
return CallNextHookEx(OldHook, code, wParam, lParam);
}
然后就可以屏蔽和启用系统热键了:
// 屏蔽热键
OldHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyProc, HInstance, 0);
// 启用热键
UnhookWindowsHookEx(OldHook);
2) 方法二,只能屏蔽部分热键。
// 屏蔽系统热键
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 1, &temp, 0);
RegisterHotKey(frmSome->Handle, 1, MOD_ALT, VK_TAB);
RegisterHotKey(frmSome->Handle, 2, MOD_ALT, VK_F4);
RegisterHotKey(frmSome->Handle, 3, MOD_CONTROL, VK_ESCAPE);
RegisterHotKey(frmSome->Handle, 4, MOD_ALT, VK_ESCAPE);
// 恢复系统热键
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 0, &temp, 0);
UnregisterHotKey(frmSome->Handle, 1);
UnregisterHotKey(frmSome->Handle, 2);
UnregisterHotKey(frmSome->Handle, 3);
UnregisterHotKey(frmSome->Handle, 4);
---------------------------------------------------------------------------
26. 禁止拖动窗口
1) 在类定义的 public (不确定是不是一定要 public) 部分加上如下声明:
void __fastcall WMKillFocus(TMessage &msg);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_NCHITTEST, TMessage, WMKillFocus);
END_MESSAGE_MAP (TForm)
2) 函数 WMKillFocus 实现如下:
void __fastcall TfrmSomeForm::WMKillFocus(TMessage &msg)
{
TForm::Dispatch(&msg);
if (msg.Result == HTCAPTION)
msg.Result = HTCLIENT;
}
---------------------------------------------------------------------------
27. 关于画图
1) TPaintBox 加滚动条方法
将 TPaintBox 置于 TScrollBox 之上,然后在画图时设定 TScrollBox 的 HorzScrollBar->Range 和 VertScrollBar->Range 即可。
2) TImage 加滚动条方法
将 TImage 置于 TScrollBox 之上,然后在画图时设定 TScrollBox 的 HorzScrollBar->Range 和 VertScrollBar->Range,同时还需设定 TImage 的 Picture->Bitmap->Width 和 Picture->Bitmap->Height。
3) 保存 TPaintBox 图像的方法
Graphics::TBitmap *savePic = new Graphics::TBitmap;
TRect Dect, Sect;
Dect = Rect(0, 0, pb->Width, pb->Height);
Sect = Rect(0, 0, pb->Width, pb->Height);
pb->Canvas->Brush->Style = bsClear;
savePic->Width = pb->Width;
savePic->Height = pb->Height;
savePic->Canvas->CopyRect(Dect, pb->Canvas, Sect);
savePic->SaveToFile("c://abc.bmp");
delete savePic;
以上方法的缺点是无法保存超出屏幕大小的图像。
4) 保存 TImage 图像的方法
img->Picture->SaveToFile("c://a.jpg");
5) TImage 图像过大导致“空间不足”错误的处理方法
设一个小点的格式(默认值是 pfDevice):
img->Picture->Bitmap->PixelFormat = pf1bit;
6) 画笔颜色(上一条的副作用)
如下语句将使得 TImage 变成黑白图片:
img->Picture->Bitmap->PixelFormat = pf1bit;
如果想恢复彩色,应该设成大一些的格式(pf4bit 以上):
img->Picture->Bitmap->PixelFormat = pf4bit;
---------------------------------------------------------------------------
28. 压缩图片
将 BMP 格式图片压缩为 JPG 格式:
TJPEGImage *jpg = new TJPEGImage();
jpg->PixelFormat = jf8Bit; ;//设置像素格式
jpg->Assign(bmp);
jpg->Compress();
jpg->SaveToFile("C://a.jpg");
delete jpg;
要记得导入头文件:
#include <jpeg.hpp>
---------------------------------------------------------------------------
29. 配置备份
1. 将注册表下
HKEY_CURRENT_USER/Software/Borland/C++Builder/6.0
导出为 reg 文件,这里边包含了大部分的设置,包括语法颜色什么的。恢复时导入注册表文件即可。
2. 将 bcb 目录下 Bin 下的 Codding.dst 和 Debugging.dst 备份。这是 bcb 编写代码和 debug 时的窗体位置配置。恢复时将文件复制到 bin 目录即可。
[转自小兵的工具箱]C++ Builder 基础的更多相关文章
- 设计模式相关面试问题-Builder基础详解与代码解读
java的builder模式详解: 概念:建造者模式是较为复杂的创建型模式,它将客户端与多含多个组成部分(或部件)的复杂对象的创建过程分离. 使用场景:当构造一个对象需要很多参数的时候,并且参数的个数 ...
- <2014 08 29> MATLAB的软件结构与模块、工具箱简示
MATLAB的系统结构:三个层次.九个部分 ----------------------------------- 一.基础层 是整个系统的基础,核心内容是MATLAB部分. 1.软件主包MATLAB ...
- 07_利用pytorch的nn工具箱实现LeNet网络
07_利用pytorch的nn工具箱实现LeNet网络 目录 一.引言 二.定义网络 三.损失函数 四.优化器 五.数据加载和预处理 六.Hub模块简介 七.总结 pytorch完整教程目录:http ...
- Retrofit源码设计模式解析(上)
Retrofit通过注解的方法标记HTTP请求参数,支持常用HTTP方法,统一返回值解析,支持异步/同步的请求方式,将HTTP请求对象化,参数化.真正执行网络访问的是Okhttp,Okhttp支持HT ...
- esp8266(0) AT指令
http://www.shaoguoji.cn/2017/01/15/ESP8266-usage/ 注意事项 使用WiFi模块的一些细节及注意事项 模块使用3.3V供电,一定注意电源的稳定,一些USB ...
- 《iOS 7 应用开发实战详解》
<iOS 7 应用开发实战详解> 基本信息 作者: 朱元波 管蕾 出版社:人民邮电出版社 ISBN:9787115343697 上架时间:2014-4-25 出版日期:2014 年5 ...
- eBPF 安全项目 Tracee 初探
1. Tracee 介绍 1.1 Tracee 介绍 Tracee 是一个用 于 Linux 的运行时安全和取证工具.它使用 Linux eBPF 技术在运行时跟踪系统和应用程序,并分析收集的事件以检 ...
- salesforce 零基础学习(三十六)通过Process Builder以及Apex代码实现锁定记录( Lock Record)
上一篇内容是通过Process Builder和Approval Processes实现锁定记录的功能,有的时候,往往锁定一条记录需要很多的限制条件,如果通过Approval Processes的条件 ...
- salesforce 零基础学习(三十五) 通过Process Builder和Approval Processes锁定记录(Lock Record)
有的时候我们可能有这样的需求,当某个字段为特定的值情况下,便锁定此条记录,仅允许Profile为System Admin的用户修改或者解锁,其他的用户只能查看此条记录,不能修改此条记录,这种情况下我们 ...
随机推荐
- hdu 4280 最大流sap
模板套起来 1 5 7 //5个结点,7个边 3 3 //坐标 3 0 3 1 0 0 4 5 1 3 3 //相连的结点和流 2 3 4 2 4 3 1 5 6 4 5 3 1 4 4 3 4 2 ...
- cocos2dx游戏开发——微信打飞机学习笔记(五)——BackgroundLayer的搭建
一.创建文件~ 文件名:BackgroundLayer.h BackgroundLayer.cpp 架构就跟前面的一样,我就直接进入正题 啦,而且github有完整代码,欢迎下载~ 二.创建滚动的背景 ...
- MATLAB学习笔记(九)——MATLAB符号计算
(一)符号对象 一.建立符号对象 1.建立符号变量和符号常量(sym,syms): 只可以建立一个符号变量 可以一次性建立多个符号变量 PS:符号常量计算的结果是精确的数学表达式,而数值常量是进行约分 ...
- C#实现UTC时间与Datetime转换
为了便于传输,通信过程中传输的都是:当前时间跟标准时间相隔的秒数,并且是以16进制字节的形式传输的. public double ConvertDateTimeInt(System.DateTime ...
- WinForm点击按钮,访问百度
命名空间 using System.Diagnostics; button的click事件中写入如下 Process.Start("http://www.xxx.com"); 注: ...
- 【转】kylin优化
转自: http://www.bitstech.net/2016/01/04/kylin-olap/ http://www.csdn.net/article/2015-11-27/2826343 ht ...
- cocos2d CCDictionary
CCDictionary* dict=CCDictionary::create(); CCString* str1=CCString::create("); CCString* str2=C ...
- java.lang.RuntimeException: Invalid action class configuration that references an unknown class named [xxxAction]。
java.lang.RuntimeException: Invalid action class configuration that references an unknown class name ...
- 1^b+2^b+3^b+...+n^b数列
首先,这是我自己推出来的,O(n^2),常数巨大.所以无能为力优化!所以求此数列的公式!求优化!!! 主要思想:要算b次的,那么就要先算b+1次的. 首先,我用F(i, j)表示杨辉三角第i层第j个, ...
- CSS中a标签样式的“爱恨”原则
CSS为一些特殊效果准备了特定的工具,我们称之为“伪类”.其中有几项是我们经常用到的,下面我们就详细介绍一下经常用于定义链接样式的四个伪类,它们分别是: 1 :link 2 :visited 3 :h ...