?? generic.html
字號:
// Small icon can be loaded from the same resource
_class.hIconSm = reinterpret_cast<HICON> (
::<font color="#000099"><b>LoadImage</b></font> (
_class.hInstance,
MAKEINTRESOURCE (resId),
IMAGE_ICON,
::<font color="#000099"><b>GetSystemMetrics</b></font> (SM_CXSMICON),
::<font color="#000099"><b>GetSystemMetrics</b></font> (SM_CYSMICON),
0));
}
void <font color="#cc0066"><b>WinClass::Register</b></font> ()
{
if (::<font color="#000099"><b>RegisterClassEx</b></font> (&_class) == 0)
throw WinException ("Internal error: RegisterClassEx failed.");
}
<hr>
class <font color="#cc0066"><b>TopWinClass</b></font>: public <font color="#cc0066"><b>WinClass</b></font>
{
public:
TopWinClass (int resId, HINSTANCE hInst, WNDPROC wndProc);
};
<font color="#cc0066"><b>TopWinClass::TopWinClass</b></font> (int resId,
HINSTANCE hInst, WNDPROC wndProc)
: WinClass (resId, hInst, wndProc)
{
SetResIcons (resId);
_class.lpszMenuName = MAKEINTRESOURCE (resId);
}</font></pre><!--End Code-->
<td width=20>
</table>
<!--End of yellow background-->
<hr><!--Text-->
Once the Windows class is registered with the system, you can create as many windows of this class as you wish. They will, of course, share the same Window procedure that was registered with the class. We'll see later how we can distinguish between various instances of the window inside the procedure.
<p>The class <font color="#cc0066"><b>WinMaker</b></font> works in much the same way as <font color="#cc0066"><b>WinClass</b></font>. Its constructor provides sensible defaults that may be overriden by calling particular methods. Once everything is set, you call the <font color="#cc0066"><b>Create</b></font> method to create the window and the <font color="#cc0066"><b>Show</b></font> method to display it. Notice that the moment you call Create, your Window procedure is called with the <font color="#009966">WM_CREATE</font> message.
<p>The top window is created using TopWinMaker class, which provides the appropriate style and caption.
<hr><!--End Text-->
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width="100%">
<tr>
<td width=20>
<td bgcolor="#e0e080">
<pre><font face="courier"><!--Code-->
class <font color="#cc0066"><b>WinMaker</b></font>
{
public:
WinMaker (WinClass & winClass);
operator HWND () { return _hwnd; }
void AddCaption (char const * caption)
{
_windowName = caption;
}
void AddSysMenu () { _style |= WS_SYSMENU; }
void AddVScrollBar () { _style |= WS_VSCROLL; }
void AddHScrollBar () { _style |= WS_HSCROLL; }
void Create ();
void Show (int nCmdShow = SW_SHOWNORMAL);
protected:
WinClass & _class;
HWND _hwnd;
DWORD _exStyle; // extended window style
char const * _windowName; // pointer to window name
DWORD _style; // window style
int _x; // horizontal position of window
int _y; // vertical position of window
int _width; // window width
int _height; // window height
HWND _hWndParent; // handle to parent or owner window
HMENU _hMenu; // handle to menu, or child-window identifier
void * _data; // pointer to window-creation data
};
<font color="#cc0066"><b>WinMaker::WinMaker</b></font> (WinClass & winClass)
: _hwnd (0),
_class (winClass),
_exStyle (0), // extended window style
_windowName (0), // pointer to window name
_style (WS_OVERLAPPED), // window style
_x (CW_USEDEFAULT), // horizontal position of window
_y (0), // vertical position of window
_width (CW_USEDEFAULT), // window width
_height (0), // window height
_hWndParent (0), // handle to parent or owner window
_hMenu (0), // handle to menu, or child-window identifier
_data (0) // pointer to window-creation data
{
}
void <font color="#cc0066"><b>WinMaker::Create</b></font> ()
{
_hwnd = ::<font color="#000099"><b>CreateWindowEx</b></font> (
_exStyle,
_class.GetName (),
_windowName,
_style,
_x,
_y,
_width,
_height,
_hWndParent,
_hMenu,
_class.GetInstance (),
_data);
if (_hwnd == 0)
throw WinException ("Internal error: Window Creation Failed.");
}
void <font color="#cc0066"><b>WinMaker::Show</b></font> (int nCmdShow)
{
::ShowWindow (_hwnd, nCmdShow);
::UpdateWindow (_hwnd);
}
// Makes top overlapped window with caption
<font color="#cc0066"><b>TopWinMaker::TopWinMaker</b></font> ((WinClass & winClass, char const * caption)
: WinMaker (winClass)
{
_style = <font color="#009966">WS_OVERLAPPEDWINDOW</font> | <font color="#009966">WS_VISIBLE</font>;
_windowName = caption;
}
</font></pre><!--End Code-->
<td width=20>
</table>
<!--End of yellow background-->
<hr><!--Text-->
Before we go any further, here are some light-weight classes of general interest. <font color="#cc0066"><b>WinException</b></font> is something that we want to throw any time a Windows API fails. It takes care of retrieving the Windows error code. (By the way, there is an easy way to convert the error code to a string using <font color="#000099"><b>FormatMessage</b></font> API.)
<p>The class <font color="#cc0066"><b>ResString</b></font> is a simple encapsulation of a string stored in the string resources of your application.
<hr><!--End Text-->
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width="100%">
<tr>
<td width=20>
<td bgcolor="#e0e080">
<pre><font face="courier">
<font color="#cc0066">// The exception class: stores the message and the error code</font>
class <font color="#cc0066"><b>WinException</b></font>
{
public:
WinException (char* msg)
: _err (::<font color="#000099"><b>GetLastError</b></font>()), _msg(msg)
{}
DWORD GetError() const { return _err; }
char const * GetMessage () const { return _msg; }
private:
DWORD _err;
char * _msg;
};
<font color="#cc0066">// The out-of-memory handler: throws exception</font>
int <font color="#cc0066"><b>NewHandler</b></font> (size_t size)
{
throw WinException ( "Out of memory" );
return 0;
}
class <font color="#cc0066"><b>ResString</b></font>
{
enum { MAX_RESSTRING = 255 };
public:
ResString (HINSTANCE hInst, int resId);
operator char const * () { return _buf; }
private:
char _buf [MAX_RESSTRING + 1];
};
<font color="#cc0066"><b>ResString::ResString</b></font> (HINSTANCE hInst, int resId)
{
if (!::<font color="#000099"><b>LoadString</b></font> (hInst, resId, _buf, MAX_RESSTRING + 1))
throw WinException ("Load String failed");
}</font></pre>
<!--End Code-->
<td width=20>
</table>
<!--End of yellow background-->
<hr><!--Text-->
The <b>Controller</b> is the nervous system of a particular <i>window instance</i>. It is created with that window, stored with it and finally destroyed with it. You can put any state information pertaining to a particular window instance in its controller. In general, the controller has a <i>view</i> that deals with painting on the window's surface and it has access to the <i>model</i>, which is the brain of your application (this is called the MVC, or Model-View-Controller pattern invented by Smalltalk programmers).
<p>If, as it often happens, your application has only one top-level window, you can directly embed the <i>model</i> in its controller. That simplifies the management of resources but at the cost of tight coupling of the controller with the model. In large projects one should avoid such couplings--the use of a (smart) pointer to the model inside the controller is then preferred.
<p>Most controller methods require a handle to the window on which behalf they are operating. This handle is passed with every Windows message, but it is simpler to store it once inside the controller object and use it whenever necessary. Remember--there is a one-to-one correspondence between window instances (and therefore window handles) and <i>controller</i> objects.
<hr><!--End Text-->
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width="100%">
<tr>
<td width=20>
<td bgcolor="#e0e080">
<pre><font face="courier">
class <font color="#cc0066"><b>Controller</b></font>
{
public:
Controller(HWND hwnd, CREATESTRUCT * pCreate);
~Controller ();
void Size (int x, int y);
void Paint ();
void Command (int cmd);
private:
HWND _hwnd;
Model _model;
View _view;
};</font></pre><!--End Code-->
<td width=20>
</table>
<!--End of yellow background-->
<hr><!--Text-->
The <b>Window Procedure</b> is the main switchboard of a Windows application. You don't call it from your program--Windows calls it! Every time something interesting happens, Windows sends your program a message. This message is passed to your window procedure. You may deal with it, or you may pass it on to the <i>default window procedure</i>.
<p>Window procedure is called with a handle to a window to which a given message is directed. This handle uniquely identifies an internal Windows' data structure that corresponds to a given window instance. It so happens that we can access this data structure and use it to store some instance-specific data. Here's the type safe way of accessing this structure. By the way, the <font color="#009966">GWL_USERDATA</font> member of this structure is guaranteed to be present in all windows, including message boxes, dialog boxes and even buttons.
<hr><!--End Text-->
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width="100%">
<tr>
<td width=20>
<td bgcolor="#e0e080">
<pre><font face="courier"><!--Code-->
template <class T>
inline T <font color="#cc0066"><b>WinGetLong</b></font> (HWND hwnd, int which = <font color="#009966">GWL_USERDATA</font>)
{
return reinterpret_cast<T> (::<font color="#000099"><b>GetWindowLong</b></font> (hwnd, which));
}
template <class T>
inline void <font color="#cc0066"><b>WinSetLong</b></font> (HWND hwnd, T value, int which = <font color="#009966">GWL_USERDATA</font>)
{
::<font color="#000099"><b>SetWindowLong</b></font> (hwnd, which, reinterpret_cast<long> (value));
}
</font></pre><!--End Code-->
<td width=20>
</table>
<!--End of yellow background-->
<hr><!--Text-->
Every time Windows calls our window procedure, we want to first retrieve its controller object. Remember, there may be several windows sharing the same window procedure and we want to have a separate controller for each window. How do we know which controller to use when we are called? We find out by looking at the window handle. In this handle we store the pointer to this particular window's controller using the Win[Set/Get]Long trick.
<p>The Window procedure is first called with the <font color="#009966">WM_CREATE</font> message. At that time we <b>create the controller</b> object and initialize it with the window handle and a special data structure called <font color="#009966">CREATESTRUCT</font> that is passed to us by Windows. Once we have the controller, we store the pointer to it in the corresponding internal Windows' data structure under the label of the current <b>hwnd</b>. Next time the Window procedure is called, with a message other than <font color="#009966">WM_CREATE</font>, we simply retrieve the pointer to our controller using the <b>hwnd</b>.
<p>The rest is easy. The Window procedure interprets message parameters and calls the appropriate methods of the controller.
<hr><!--End Text-->
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width="100%">
<tr>
<td width=20>
<td bgcolor="#e0e080">
<pre><font face="courier">
LRESULT CALLBACK <font color="#cc0066"><b>WndProc</b></font>
(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -