|
|

楼主 |
发表于 2008-2-9 17:04:00
|
显示全部楼层
[LoserPower]子窗口控制(2008.2.3 - 2.9)
1、计时器类Timer
class Timer
{
public:
BaseClass(Timer);
//1)初始化时指定id以便绑定到指定消息处理函数
Type(uint id_)
:_Wnd(0), _Id(id_), _Elapse(0), _TimerFunc(0)
, counter(0)
{
if (_Id == 0)
throw std::underflow_error("The timer event identity can not equal to zero.");
}
//2)设定计时器
void Set(Window* wnd_, uint elapse_, TIMERPROC timerfunc_ = 0)
{
_Elapse = elapse_;
_Wnd = wnd_;
_TimerFunc = timerfunc_;
}
//3)启动计时器
uint Start()
{
counter = 0;
_Id = SetTimer(_Wnd->GetHwnd(), _Id, _Elapse, _TimerFunc);
return _Id;
}
//4)关闭计时器
void End()
{
counter = 0;
KillTimer(_Wnd->GetHwnd(), _Id);
}
};
5)不绑定到窗口,则创建应用程序计时器,分配唯一id,并需要指定回调函数。对于指定回调函数的情况,总是优先传递到回调函数而不是用消息。
2、处理按钮通知
struct id_type
{
union
{
struct
{
uint id : 16;
uint notify : 16;
};
uint value;
};
id_type(uint value_)
:value(value_)
{}
id_type(uint notify_, uint id_)
:notify(notify_), id(id_)
{}
operator uint()
{
return value;
}
};
该标识用于消息映射时转换到uint并登记到映射表。
Button类中的通知码枚举:
enum _NotifyMask
{
bnClicked = BN_CLICKED,
bnPaint = BN_PAINT,
bnPushed = BN_PUSHED,
bnUnPushed = BN_UNPUSHED,
bnDisable = BN_DISABLE,
bnDblClk = BN_DBLCLK,
bnSetFocus = BN_SETFOCUS,
bnKillFocus = BN_KILLFOCUS,
};
请求一个预定义属性(动态包装的数据):
wm_command_tag::id_type id(uint notify_ = bnClicked) const
{
wm_command_tag::id_type id_(notify_, _Id);
return id_;
}
利用一系列属性进行通知码映射:
invoke(btn.clicked(), hold(OnBtnClk));
通知码解释器:
LRESULT OnCommand(uint id, uint code, void* handle_)
{
wm_command_tag::id_type idty(code, id);
map_type::iterator itr = command_map.find(map_type::key_type(idty));
if (itr != command_map.end())
return (*itr->second)(WPARAM(idty), LPARAM(handle_));
return -1;
}
其根据WM_COMMAND消息重新包装参数,分发到命令路由。
3、虚析构函数
一旦一个基类登记了一个虚析构函数,则将在派生类形成完整的析构对象层次。其行为就如同自动对象的析构过程,自底向上。派生类是否显式使用析构函数,是否声明为虚析构函数,均不会对对象层次产生差异性影响。不过对于一个非虚析构派生的类型,声明为虚析构函数将总是能在该对象层次得到完整析构,使用该类型的指针,行为就如同它是最底基类那样。
4、单选按钮的互斥规则:
1)创建子窗口时,登记到父窗口类的表格中,手动处理单选按钮的互斥逻辑。
2)使用EnumChildWindow(HWND, WNDENUMPROC,LPARAM)完成动态子窗口表创建。
3)使用BS_AUTORADIOBUTTON由系统管理互斥,并加入到BS_GROUP中形成逻辑组。
4)使用CheckRadionButton(int first_, int last_, int check_)方法:
bool CheckRadioButton(int first_, int last_, int check_)
{
return ::CheckRadioButton(hwnd, first_, last_, check_);
}
int GetCheckedRadioButton(int first_, int last_)
{
for (int i = first_; i <= last_; ++i)
{
HWND hwndchild = GetDlgItem(hwnd, i);
if (hwndchild)
if (::SendMessage(hwndchild, BM_GETCHECK, 0, 0) == BST_CHECKED)
return i;
}
return -1;
}
以完成简单灵活的任务。
5、从句柄绑定到Window类
void FromHandle(HWND hwnd_)
{
if (!hwnd_)
return;
Release();
hwnd = hwnd_;
hinst = HINSTANCE(GetWindowLong(hwnd, GWL_HINSTANCE));
hmenu = HMENU(GetWindowLong(hwnd, GWL_ID));
los::graphics: C dc(hwnd);
hfont = dc.SelectStockFont(DEFAULT_GUI_FONT);
tchar buf[256];
GetClassName(hwnd, buf, 256 - 1);
clsname = buf;
WNDCLASSEX wce;
if (GetClassInfoEx(hinst, clsname.c_str(), &wce))
{
hbrush = wce.hbrBackground;
_ClassStyle = wce.style;
_ClsExist = true;
}
parent = app->GetWindow(::GetParent(hwnd));
Rect wndrc = this->GetWindowSize();
_DefaultX = wndrc.left;
_DefaultY = wndrc.top;
_DefaultW = wndrc.Width();
_DefaultH = wndrc.Height();
_ExStyle = uint(GetWindowLong(hwnd, GWL_EXSTYLE));
_Style = uint(GetWindowLong(hwnd, GWL_STYLE));
_FromHandle = true;
}
为句柄包装而创建的简单对象:
Type()
: hwnd(0)
, _IdFeedback(0), _IsModal(false), _Tracking(false), _NcTracking(false), _ClsExist(false), _FromHandle(false)
{
}
简单对象不能创建新窗口:
void _Create(Type* parent_ = 0)
{
if (clsname == t(""))
throw los::errortypes::invalid_object();
...
}
句柄包装对象不能释放原窗口资源:
bool _Release()
{
if (!hwnd || _FromHandle)
return false;
...
}
6、窗口析构、窗口资源释放、窗口响应消息队列的解构
1)窗口析构和窗口资源释放均访问_Release(),该方法是对应于每个对象层次的特定实现。
bool _Release()
{
if (BaseRelease())
{
...释放当前对象层次的资源
return true;
}
return false;
}
所不同的是,
1、窗口析构是由虚析构函数访问规则维护其调用形式,由对象生存期决定动作时间。并且每个对象层次的析构函数副本均可以执行。总是从对象层次最外层开始调用析构,所以将外层析构认定为接口的暴露。自上而下解析,底层对象的析构将可能什么也不做。一次析构之后hwnd被清除,不满足任何析构式。
2、手动释放资源是由Release()提供界面的。
virtual bool Release()
{
return _Release();
}
在需要手动释放资源而不应析构对象的情况下,这个接口就是必须的。如FromHandle(),该成员函数企图从现有窗口句柄选入窗口信息,包装成窗口对象以简化程序应用。特别是只能传递句柄的线程间通讯。
3、Window基类控制窗口UI的分解,所以_Release()应是和对象析构的方向完全相反,先分解基类,逐级上朔,唯一的要求是,完成析构任务后要返回true,这个真值的Window控制由(!hwnd || _FromHandle)辨别,无效窗口和包装对象都不应进行资源释放。
4、在消息队列中的事件响应:
派生类:
LRESULT OnClose()
{
if (MessageBox(t("收到关闭窗口请求,是否关闭"), MB_ICONQUESTION | MB_YESNO) == IDYES)
return BaseProc();
return 0;
}
基类:
LRESULT OnClose()
{
Release();
return 0;
}
通过消息映射覆盖和动态消息表来维护对象层次逻辑,这一般适用于非实时的用户界面互动,并可能执行一些逻辑性的清理动作,如更新一个数据库或者其它窗口的用户界面等。
5、除非有资源释放和事件响应需求,否则并不是每个对象层次均要显式提供以上界面和实现。如BaseRelease()和BaseProc()将访问最近的基类过程,对于不打算进一步派生的具体类,可以直接使用接口覆盖版本而不提供特定实现过程。如:
bool Release()
{
if (BaseRelease())
{
...
return true;
}
return false;
}
这样的类型即使被派生,也会丢失当前的处理过程。仅在用户界面简化编程时被推荐使用。
类似的:
void Create(Type* parent_)
{
BaseCreate(parent_);
}
void Invokes()
{
BaseInvokes();
invoke(wm_paint, hold(OnPaint));
}
没有唯一的方式在所有地方都优于其它方式,对应场合使用具体形式。 |
|