From: Luigino on
Hello everyone!!

I'm going to make a custom control which can be loaded on Toolbox to
add in a MFC Application as a component. Before I tried creating a MFC
ActiveX Control but it's an OCX and plus it uses COM.
So I'm trying to create a regular DLL (which could have more than one
control...) and I started with creating a new MFC DLL choosing Use MFC
in a Shared DLL.
Then I added a MFC Class where at the start I want to show a rectangle
that redraws every time I resize the control on a resource window in a
MFC application.

I'd like to know how I can initialize the MFC DLL in the main .CPP
file of the DLL in the way I can load the DLL in the Toolbox and have
a custom control to apply in a MFC application? Or maybe I shall
change the CWnd base in my class with, for example, CDialog?

Thanks to everyone in advance for the suggests..... :-)

Ciao
Luigi

Here's the code of custom control's class:

//
// CaChart.cpp : implementation file
//

#include "stdafx.h"
#include "CACharts.h"
#include "CaChart.h"

// CCaChart

IMPLEMENT_DYNAMIC(CCaChart, CWnd)

CCaChart::CCaChart()
: iGraphType(GRAPH_BARS)
{
RegisterWindowClass();
}

CCaChart::~CCaChart()
{
}

// Register the window class if it has not already been registered.
BOOL CCaChart::RegisterWindowClass()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();

if (!(::GetClassInfo(hInst, CACHART_CLASSNAME, &wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW |
CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = AfxGetApp()->LoadStandardCursor
(IDC_ARROW);
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = CACHART_CLASSNAME;

if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return FALSE;
}
}

return TRUE;
}

BEGIN_MESSAGE_MAP(CCaChart, CWnd)
//{{AFX_MSG_MAP(CCaChart)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// CCaChart message handlers
void CCaChart::OnPaint()
{
CPaintDC dc(this); // device context for painting

// Create memory DC
CDC MemDC;
if (!MemDC.CreateCompatibleDC(&dc))
return;

CRect rect;
GetClientRect(rect);

MemDC.FillRect(rect, CBrush::FromHandle((HBRUSH)GetStockObject
(BLACK_BRUSH)));
MemDC.Ellipse(rect);
}

BOOL CCaChart::OnEraseBkgnd(CDC* pDC)
{
return CWnd::OnEraseBkgnd(pDC);
}

void CCaChart::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base
class
// In our case this is not needed - yet - so just drop through
to
// the base class

// Get Size of Display area

CWnd::PreSubclassWindow();
}

BOOL CCaChart::Create(CWnd* pParentWnd, const RECT& rect, UINT nID,
DWORD dwStyle /*=WS_VISIBLE*/)
{
return CWnd::Create(CACHART_CLASSNAME, _T(""), dwStyle, rect,
pParentWnd, nID);
}

void CCaChart::set_GraphType(GRAPH_TYPE graphtype)
{
iGraphType = graphtype;
}

GRAPH_TYPE CCaChart::get_GraphType()
{
return (GRAPH_TYPE)iGraphType;
}


From: Joseph M. Newcomer on
If you create a custom control in a DLL, you cannot make it something that goes into the
ToolBox in VS. You can use it by creating a user-defined resource (a blank rectangle on
the dialog template) and assigning it the class name of your control and setting the style
bits manually. But if you want a control you can place in the toolbox, it has to be an
OCX and use COM.

Question: what's the Big Deal with an OCX control that uses COM? Given you've already
done this work, stripping it back to be a pure DLL doesn't seem to be a productive thing
to do.

I usually create a CStatic and give it an ID, then at OnInitialUpdate/OnInitDialog I will
create the control with the same ID in the same place and destroy the old control.

For example, if I have a CStatic called IDC_MYCONTROL, then I will create a control
variable for it, c_MyControlPlace, and add to my class definition

CMyControl c_MyControl;

In the OnInitialUpdate/OnInitDialog handler, I will do

CRect r;
c_MyControlPlace.GetWindowRect(&r);
ScreenToClient(&r);

c_MyControl.Create(r, c_MyControlPlace);

c_MyControlPlace.DestroyWindow();

where I have defined

BOOL CMyControl::Create(const CRect & r, CWnd wnd)
{
if(!CWnd::Create(..., &r, ... wnd.GetDlgCtrlId()))
return FALSE;
SetWindowPos(&wnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
return TRUE;
}

(I didn't take the time to look up all the parameters, so I've just used ... here and the
rest is left as an Exercise For The Reader)

Note that this is clumsier than using an ActiveX control.
joe

On Wed, 14 Oct 2009 02:00:07 -0700 (PDT), Luigino <npuleio(a)rocketmail.com> wrote:

>Hello everyone!!
>
>I'm going to make a custom control which can be loaded on Toolbox to
>add in a MFC Application as a component. Before I tried creating a MFC
>ActiveX Control but it's an OCX and plus it uses COM.
>So I'm trying to create a regular DLL (which could have more than one
>control...) and I started with creating a new MFC DLL choosing Use MFC
>in a Shared DLL.
>Then I added a MFC Class where at the start I want to show a rectangle
>that redraws every time I resize the control on a resource window in a
>MFC application.
>
>I'd like to know how I can initialize the MFC DLL in the main .CPP
>file of the DLL in the way I can load the DLL in the Toolbox and have
>a custom control to apply in a MFC application? Or maybe I shall
>change the CWnd base in my class with, for example, CDialog?
>
>Thanks to everyone in advance for the suggests..... :-)
>
>Ciao
>Luigi
>
>Here's the code of custom control's class:
>
>//
>// CaChart.cpp : implementation file
>//
>
>#include "stdafx.h"
>#include "CACharts.h"
>#include "CaChart.h"
>
>// CCaChart
>
>IMPLEMENT_DYNAMIC(CCaChart, CWnd)
>
>CCaChart::CCaChart()
>: iGraphType(GRAPH_BARS)
>{
> RegisterWindowClass();
>}
>
>CCaChart::~CCaChart()
>{
>}
>
>// Register the window class if it has not already been registered.
>BOOL CCaChart::RegisterWindowClass()
>{
> WNDCLASS wndcls;
> HINSTANCE hInst = AfxGetInstanceHandle();
>
> if (!(::GetClassInfo(hInst, CACHART_CLASSNAME, &wndcls)))
> {
> // otherwise we need to register a new class
> wndcls.style = CS_DBLCLKS | CS_HREDRAW |
>CS_VREDRAW;
> wndcls.lpfnWndProc = ::DefWindowProc;
> wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
> wndcls.hInstance = hInst;
> wndcls.hIcon = NULL;
> wndcls.hCursor = AfxGetApp()->LoadStandardCursor
>(IDC_ARROW);
> wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
> wndcls.lpszMenuName = NULL;
> wndcls.lpszClassName = CACHART_CLASSNAME;
>
> if (!AfxRegisterClass(&wndcls))
> {
> AfxThrowResourceException();
> return FALSE;
> }
> }
>
> return TRUE;
>}
>
>BEGIN_MESSAGE_MAP(CCaChart, CWnd)
> //{{AFX_MSG_MAP(CCaChart)
> ON_WM_PAINT()
> ON_WM_ERASEBKGND()
> //}}AFX_MSG_MAP
>END_MESSAGE_MAP()
>
>// CCaChart message handlers
>void CCaChart::OnPaint()
>{
> CPaintDC dc(this); // device context for painting
>
> // Create memory DC
> CDC MemDC;
> if (!MemDC.CreateCompatibleDC(&dc))
> return;
>
> CRect rect;
> GetClientRect(rect);
>
> MemDC.FillRect(rect, CBrush::FromHandle((HBRUSH)GetStockObject
>(BLACK_BRUSH)));
> MemDC.Ellipse(rect);
>}
>
>BOOL CCaChart::OnEraseBkgnd(CDC* pDC)
>{
> return CWnd::OnEraseBkgnd(pDC);
>}
>
>void CCaChart::PreSubclassWindow()
>{
> // TODO: Add your specialized code here and/or call the base
>class
> // In our case this is not needed - yet - so just drop through
>to
> // the base class
>
> // Get Size of Display area
>
> CWnd::PreSubclassWindow();
>}
>
>BOOL CCaChart::Create(CWnd* pParentWnd, const RECT& rect, UINT nID,
>DWORD dwStyle /*=WS_VISIBLE*/)
>{
> return CWnd::Create(CACHART_CLASSNAME, _T(""), dwStyle, rect,
>pParentWnd, nID);
>}
>
>void CCaChart::set_GraphType(GRAPH_TYPE graphtype)
>{
> iGraphType = graphtype;
>}
>
>GRAPH_TYPE CCaChart::get_GraphType()
>{
> return (GRAPH_TYPE)iGraphType;
>}
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Luigino on
On 14 Ott, 20:39, Joseph M. Newcomer <newco...(a)flounder.com> wrote:

OK now I finalized to create a Toolbox component, it has to be an OCX
Activex COM...

> I usually create a CStatic and give it an ID, then at OnInitialUpdate/OnInitDialog I will
> create the control with the same ID in the same place and destroy the old control.
>
> For example, if I have a CStatic called IDC_MYCONTROL, then I will create a control
> variable for it, c_MyControlPlace, and add to my class definition
>
> CMyControl  c_MyControl;
>
> In the OnInitialUpdate/OnInitDialog handler, I will do
>
> CRect r;
> c_MyControlPlace.GetWindowRect(&r);
> ScreenToClient(&r);
>
> c_MyControl.Create(r, c_MyControlPlace);
>
> c_MyControlPlace.DestroyWindow();
>
> where I have defined
>
> BOOL CMyControl::Create(const CRect & r, CWnd wnd)
>     {
>      if(!CWnd::Create(..., &r, ... wnd.GetDlgCtrlId()))
>         return FALSE;
>      SetWindowPos(&wnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
>      return TRUE;
>     }
>

I tried your approach to test its behavior and I compiled the DLL then
added the path + .lib in a test project's properties but when I call
Create(...) function, which is declared as public static, I get an
unresolved reference external error.... why?....
I say before this error I got same error also on constructor and
destructor which I had to declare

CMyControl c_MyControl;

as

CMyControl* c_MyControl;

any suggest?

Thanks
Ciao
Luigi
From: Luigino on
I tried this too:

CStatic* c_CStaticPlaced = (CStatic *)GetDlgItem(IDC_MYSTATIC);
CRect r;
c_CStaticPlaced->GetWindowRect(&r);
ScreenToClient(&r);
CCaChart* c_MyControl = new CCaChart();
CWnd* pWnd = c_CStaticPlaced->GetDlgItem(c_CStaticPlaced->GetDlgCtrlID
());
c_MyControl->Create(r, pWnd);
c_CStaticPlaced->DestroyWindow();

but I get this error:

1>test_chartDlg.obj : error LNK2019: unresolved external symbol
"public: __thiscall CCaChart::CCaChart(void)" (??0CCaChart@@QAE(a)XZ)
referenced in function "protected: virtual int __thiscall
Ctest_chartDlg::OnInitDialog(void)" (?
OnInitDialog(a)Ctest_chartDlg@@MAEHXZ)

but the constructor CaChart isn't declared as virtual in the header's
class, plus it's public...

Any suggest?...
Thanks
Ciao
Luigi
From: Ismo Salonen on
Luigino wrote:
> I tried this too:
>
> CStatic* c_CStaticPlaced = (CStatic *)GetDlgItem(IDC_MYSTATIC);
> CRect r;
> c_CStaticPlaced->GetWindowRect(&r);
> ScreenToClient(&r);
> CCaChart* c_MyControl = new CCaChart();
> CWnd* pWnd = c_CStaticPlaced->GetDlgItem(c_CStaticPlaced->GetDlgCtrlID
> ());
> c_MyControl->Create(r, pWnd);
> c_CStaticPlaced->DestroyWindow();
>
> but I get this error:
>
> 1>test_chartDlg.obj : error LNK2019: unresolved external symbol
> "public: __thiscall CCaChart::CCaChart(void)" (??0CCaChart@@QAE(a)XZ)
> referenced in function "protected: virtual int __thiscall
> Ctest_chartDlg::OnInitDialog(void)" (?
> OnInitDialog(a)Ctest_chartDlg@@MAEHXZ)
>
> but the constructor CaChart isn't declared as virtual in the header's
> class, plus it's public...
>
> Any suggest?...
> Thanks
> Ciao
> Luigi

This is linker error message. The real reason for the error message is
that the linker does not see the implementation. Either you do not have
the implementation for the CCaChart:.CCaChart(void) constructor of you
are not including relevant object file on command line.

ismo


ismo