_AFXCVIEW_INLINE CListCtrl& CListView::GetListCtrl() const
{ return *(CListCtrl*)this; }
从上面代码来看,CListView的指针是可以强制转换成CListCtrl指针的
但是看过MFC中代码后发现,CListView派生自CWnd->CView->CCtrlView
而CListCtrl直接派生自CWnd,CWnd中没有GetListCtrl这样的函数,而且CListView的实现中也没有重载CListCtrl强制转换操作符
现在郁闷中.....求解!
{ return *(CListCtrl*)this; }
从上面代码来看,CListView的指针是可以强制转换成CListCtrl指针的
但是看过MFC中代码后发现,CListView派生自CWnd->CView->CCtrlView
而CListCtrl直接派生自CWnd,CWnd中没有GetListCtrl这样的函数,而且CListView的实现中也没有重载CListCtrl强制转换操作符
现在郁闷中.....求解!
BOOL CListCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd,
UINT nID)
{
// initialize common controls
VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTL_LISTVIEW_REG)); CWnd* pWnd = this;
return pWnd->Create(WC_LISTVIEW, NULL, dwStyle, rect, pParentWnd, nID);
}
这是CListCtrl的创建方法啊.CListCtrl也是从CWnd派生来的啊.//而最重要的就是WC_LISTVIEW 这个参数
{
ASSERT(cs.lpszClass == NULL);
cs.lpszClass = m_strClass; // initialize common controls
VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG); // map default CView style to default style
// WS_BORDER is insignificant
if ((cs.style | WS_BORDER) == AFX_WS_DEFAULT_VIEW)
cs.style = m_dwDefaultStyle & (cs.style | ~WS_BORDER); return CView::PreCreateWindow(cs);
}
{ }CCtrlView::CCtrlView(LPCTSTR lpszClass, DWORD dwStyle)
{
m_strClass = lpszClass;
m_dwDefaultStyle = dwStyle;
}BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs)
{
ASSERT(cs.lpszClass == NULL);
cs.lpszClass = m_strClass;
...其实 CListView 直接就是 CListCtrl 同一个类型的 WindowClass 的
再直接的说 CListView 就是 CListCtrl,只是封装不同WC_LISTVIEW的定义,搜索可以看到#ifndef NOLISTVIEW#ifdef _WIN32#define WC_LISTVIEWA "SysListView32"
#define WC_LISTVIEWW L"SysListView32"#ifdef UNICODE
#define WC_LISTVIEW WC_LISTVIEWW
#else
#define WC_LISTVIEW WC_LISTVIEWA
#endif#else
#define WC_LISTVIEW "SysListView"
#endif
我提出不存在CWnd::GetListCtrl是指不是通过虚函数实现,而某些时候类型转换的操作符也可以被重载,但正如你所说,要求内存空间中的排列吻合,但在CListView和CListCtrl代码中我没有看到这样的处理过程to etre:
正如happyparrot所说,内存空间中的内容太大不同了to DentistryDoctor:
获取CListCtrl的引用表明可以调用CListCtrl类的成员函数,但是在CListView仅仅和CListCtrl在上面的代码中仅仅是创建了一个类名相同的"SysListView"窗口,在指针强制转换后,CListView实现的内存空间中和CListCtrl肯定是不相同的,调用CListCtrl的成员函数岂不是会出错了?
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
virtual BOOL OnChildNotify(UINT, WPARAM, LPARAM, LRESULT*);
而这两个都是由FrameWork来调用的,一般程序中不需要直接调用这两个函数因为CListView的m_hWnd其实就是"ListCtrl window",所以当强行转换成CListCtrl后,
m_hWnd是正确的,但也仅需要该成员变量正确而已,因为看看CListCtrl的所有非虚成员函数,
基本上都是通过m_hWnd对listctrl window消息的封装,而非虚成员函数在exe文件中其实等同于
全局函数或者静态函数,只是一段代码,当调用时需传入this指针而已因此虽然CListView和CListCtrl对象的内存内容不一样,但是因为m_hWnd的内存地址是一样的(都是继承于CWnd),所以正确的使用转换后的CListCtrl指针
注意,这些class中都没有成员变量。所以,MFC对窗口的包装都是假的。
尤其是钻石型多继承:
A
↑ ↑
B C
↑ ↑
D
(这个图有点难看,不好意思)
查看MFC源码,多继承的类非常少(嗯,M$那帮程序员看来也害怕多继承),
但是却有很多类间的关系带有明显的多继承特征:
1)CEdit继承自CWnd
2)CEditView继承自CCtrlView,CCtrlView继承自CWnd
实际使用中显然可以发现CEditView有很多CEdit的行为,难道CEditView重复
了CEdit的代码?其实不然
下面这篇文章转自微软的网站,可以发现其中一个很聪明的技巧,借此技巧也
可以告诉我们如何实现自己的CtrlView
Q
I made a custom control derived from CWnd, and now I want to use it as a
view. My first solution was to embed the control into the view and handle
OnSize in the view to position the control over the client area. The
problem is that mouse messages go to the control and cannot be overridden
in the view. The keystroke messages go to the view and must be manually
forwarded to the control.
I read about CCtrlView as a base class for common controls. I've even
managed to write the view around it (I believe that you wrote about this in
an issue of MSJ), but I could not get it to work with my CWnd-based
control. Can this be done, and how?Mateo AndersonA
CCtrlView is a trick that MFC uses to convert control classes to view
classes. For example, from CTreeCtrl to CTreeView and from CListCtrl to
CListView. The comment in the documentation for CCtrlView says, "CCtrlView
allows almost any control to be a view." Unfortunately, the "almost" is a
bit of an exaggeration, unless by "any control" the author was thinking
about any of the built-in Windows? controls like CEdit, CTreeCtrl, and so
on. CCtrlView uses a trick that works only in special circumstances.
To understand how CCtrlView works, let's take a look at CTreeView, which is
derived from CCtrlView. There are three important functions to consider:
the constructor, PreCreateWindow, and GetTreeCtrl.
The constructor tells CCtrlView which kind of Windows control to create.CTreeView::CTreeView() :
CCtrlView(WC_TREEVIEW, dwStyle)
{
}In this case, WC_TREEVIEW (#defined in commctrl.h) is the name of the
(Windows) tree control class: namely, SysTreeView32. CCtrlView stores this
name in a data member for later use.CCtrlView::CCtrlView(LPCTSTR lpszClass,
DWORD dwStyle)
{
m_strClass = lpszClass;
m_dwDefaultStyle = dwStyle;
}The next function that comes into play is PreCreateWindow, which CTreeCtrl
inherits from CCtrlView. CCtrlView::PreCreateWindow uses m_strClass to set
the class name in the CREATESTRUCT just before the window is created.// CCtrlView uses stored class name
BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = m_strClass;
???
return CView::PreCreateWindow(cs);
}Now the window created is of the desired class—in this case,
SysTreeView32. So far, so good. But if CTreeCtrl is derived from CCtrlView,
which is derived from CView, how can it also be derived from CTreeCtrl, the
MFC class that wraps the tree control? CTreeView and CTreeCtrl are
completely independent, with different inheritance chains. CTreeCtrl is
derived from CWnd directly, whereas CTreeView is derived from
CCtrlView/CView! This is where the trick comes in.
To manipulate the tree view as a tree control, CTreeView provides a special
function, GetTreeCtrl, to get the tree control.CTreeCtrl& CTreeView::GetTreeCtrl() const
{
return *(CTreeCtrl*)this;
}GetTreeCtrl simply casts the CTreeView to a CTreeCtrl. But wait a minute—how
on earth can this work? The two classes are entirely different, with
different data members and virtual function tables—you can't just cast one
class to another and expect it to work!
The answer is that CTreeCtrl has no virtual functions and no member data.
You could call it a pure wrapper class. CTreeCtrl doesn't add anything
(data or virtual functions) to its base class, CWnd; all it adds is a bunch
of wrapper functions, concrete functions that send messages to the
underlying HWND. For example:HTREEITEM CTreeCtrl::InsertItem(...)
{
return (HTREEITEM)::SendMessage(m_hWnd,
TVM_INSERTITEM, ...);
}The only data member that InsertItem accesses is m_hWnd, which all
CWnd-derived classes have. InsertItem and all the other wrapper functions
simply pass their arguments to the underlying HWND, converting C++-style
member functions to Windows-style SendMessage calls. The object itself
("this" pointer) could be an instance of any CWnd-derived class, as long as
m_hWnd is in the right place (that is, the first data member of the class)
and the HWND is, in fact, a handle to a tree control. It's the same reason
you can writepEdit = (CEdit*)GetDlgItem(ID_FOO);even though GetDlgItem returns a pointer to a CWnd, not a CEdit: because
CEdit is also a pure wrapper class, with no extra data or virtual functions
beyond what it inherits from CWnd.
So the "almost any" in the statement "CCtrlView allows almost any control
to be a view" means specifically any control that adds no member data and
no virtual functions to CWnd, what I am calling a "pure wrapper class." If
your control class has its own data or virtual functions, you can't use
CCtrlView because the extra data/virtual functions won't exist in
CCtrlView/CView.
For example, the first virtual function in CView is CView::IsSelected. If
your control class has some other virtual function, then things will
certainly bomb when you cast CCtrlView to your CFooCtrl and try to call
that virtual function. The function simply doesn't exist. Likewise, the
first data member in CView is m_pDocument. If your control class expects
some other data member, your code will bite the bag when it tries to access
it, if the object called is really a CCtrlView, not a CFooCtrl. Too bad, so
sad.
In short, the only time you can use the CCtrlView trick is when your
CWnd-derived control class has no virtual functions and no member data of
its own. C'est la vie.
your head on the table and weep? Of course not! Your first approach was
dandy: create your control as a child of the view and use OnSize to
position it exactly over the view's client area.CFooView::OnSize(..., cx, cy)
{
m_wndFooCtrl.SetWindowPos(NULL,
0,0,cx,cy,SWP_NOZORDER);
}Those input problems you encountered are easily overcome. Consider the
mouse. If you want to let the parent view handle mouse messages sent to
your control, the thing to do is abstract the messages into higher-level
events. That's a highfalutin way of saying something familiar to us all.
Consider, for example, a button. When the user clicks a button, the button
notifies its parent with a BN_CLICKED event. It does not send
WM_LBUTTONDOWN; it sends a WM_COMMAND message with subcode = BN_CLICKED.
The button is telling its parent window: the user clicked me. Likewise,
list controls don't broadcast WM_LBUTTONDOWN; they do a little processing
and notify their parents with LBN_SELCHANGE. (In the case of a
double-click, list controls do propagate LBN_DBLCLK, which is little more
than WM_LBUTTONDBLCK.) In general, the idea is that controls convert raw
events into higher-level events that are meaningful in the context of the
control.
If you're doing this at home, you should probably use the more modern way,
which is WM_NOTIFY, instead of WM_COMMAND. WM_NOTIFY lets you pass a whole
struct of information instead of trying to squish everything into half a
DWORD. You can decide which mouse messages your control should propagate.
For example, buttons don't normally send BN_DOUBLECLICKED unless they have
the BS_NOTIFY style.
So much for mousing. Now, what about the keyboard? That's even easier. When
the user activates your app by clicking on the caption or Alt-TABing to it,
Windows normally gives focus to the main frame. MFC, in turn, passes focus
to your view:void CFrameWnd::OnSetFocus(...)
{
if (m_pViewActive != NULL)
m_pViewActive->SetFocus();
else
CWnd::OnSetFocus(...);
}All you have to do is pass the focus, in turn, to your control:CFooView::OnSetFocus(...)
{
m_wndFooCtrl.SetFocus();
}Now keystrokes go directly to your control. Do not pass view. I told you it
was easy! This is the age-old Windows way of doing things, but with all
those frameworks doing so much for you nowadays, it's easy to miss the
basics.
The upshot is this: if your custom control view class is not a pure wrapper
function, that is, if it has so much as one data member or virtual function
of its own, then the way to convert your control into a view is to
instantiate it as a child window of the view and integrate it in three
simple steps.Handle WM_SIZE in the view to position your control exactly over the view's
client area.Convert mouse messages in the control to higher-level parent WM_NOTIFY
notifications.Handle WM_FOCUS in the view to set focus to your control.Incidentally—if I may be permitted to muse for just a paragraph or two—this
example illustrates one of the drawbacks of the MFC object model, which
doesn't allow multiple inheritance. You can't say, "my class is a view and
a foo control," which is really what you want to do. It also shows why some
programmers may choose to implement custom controls using C only, and not
C++. It is possible, you know. All you have to do is register your own
window class (in the Windows sense), allocate a little memory block to hold
your window's instance data, and implement all your functions as messages—WM
FOO_GETTHIS and WMFOO_SETTHAT. This was the only way to implement custom
controls before C++ came along, and it still has many benefits.
For example, if you do it this way, some other C++ programmer could come
along and write a C++ pure wrapper class for your window, with simple
wrapper functions CFooCtrl::GetThis and CFooCtrl::SetThat, which merely
passed the parameters to and fro using the proper WMFOO_XXX messages, and
then such a programmer could in fact use CCtrlView to convert your control
to a view! Or, to put it differently, one way to use CCtrlView is to
reimplement your custom control using pure C and the Windows API with
messages and subclassing instead of MFC! This would require a bit more
typing and type-casting (for all those WPARAMs and LPARAMs), but would
leave you feeling satisfied and pure.
class A
{
public:
A(){
a1=0;
a2=0 ;}
~A(){}
protected:
private:
int a1,a2 ;
};
class B
{
public:
B() {b1=1 ; b2=1 ; b3=1 ;}
~B(){}
protected:
private:
int b1,b2,b3 ;};
int main(int argc, char* argv[])
{
printf("Hello World!\n");
A a,*pa ;
pa = &a ;
B b,*pb ;
pb = &b ;
pa =(A *)(void *)pb ; return 0;
}
可以得到两个不相干得类的信息,但是你自己要明白那个内存区是你要取得的内存区 。<可以把, {a1,a2}和{b1,b2} 看作是CListCtrl>
以上个人理解