0

I have a lot of CDialogEx derived classes that do something like this in OnInitDialog:

CMeetingScheduleAssistantApp::InitialiseResizeIcon(m_bmpResize, m_lblResize, this);
CMeetingScheduleAssistantApp::RestoreWindowPosition(_T("PublisherDB"), this, true);

Then, I have the following added to each derived dialog class:

int CPublishersDatabaseDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDialogEx::OnCreate(lpCreateStruct) == -1)
        return -1;

    // Save Initial window size to m_rcInit
    GetWindowRect(&m_rcInit);

    return 0;
}

void CPublishersDatabaseDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
    // Set the minimum window size to initial size.
    lpMMI->ptMinTrackSize.x = m_rcInit.Width();
    lpMMI->ptMinTrackSize.y = m_rcInit.Height();

    CDialogEx::OnGetMinMaxInfo(lpMMI);
}

void CPublishersDatabaseDlg::OnClose()
{
    CMeetingScheduleAssistantApp::SaveWindowPosition(_T("PublisherDB"), this);
    CDialogEx::OnClose();
}

The only thing that is different for each dialog is the phrase that is used for saving the window position.

I want to have a based CDialogEx class that I can inherit from that will perform the above actions. I have looked on SO and seem some questions and creating a CDialog class and inheriting from another CDialog class. But this class I want to create is more generic. Effectively to be used as a base instead of CDialogEx.

Can this be done? Am I over-complicating this?

Problems

Why I try to create a new class, derived from CDialogEx:

Setup

Result

Error

I don't know if it is because it requires a dialog ID as stated here.

Classes such as CDialog, CFormView, or CPropertyPage, which require a dialog ID.

So I can't work out the correct way to create a base CDialogEx class for use in all my other dialog classes.

Update

I created this code and it tells me that CResizingDialog is not a class or a namespace:

#include "ResizingDialog.h"
#include "resource.h"
#include "stdafx.h"

IMPLEMENT_DYNAMIC(CResizingDialog, CDialogEx)

CResizingDialog::CResizingDialog(const CString& strWindowID, UINT nIDTemplate, CWnd* pParent = nullptr)
    : m_strWindowID(strWindowID), CDialogEx(nIDTemplate, pParent)
{

}

CResizingDialog::~CResizingDialog()
{
}

void CResizingDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CResizingDialog, CDialogEx)
    ON_WM_CREATE()
    ON_WM_GETMINMAXINFO()
    ON_WM_CLOSE()
END_MESSAGE_MAP()


int CResizingDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDialogEx::OnCreate(lpCreateStruct) == -1)
        return -1;

    // Save Initial window size to m_rcInit
    GetWindowRect(&m_rcInit);

    return 0;
}


void CResizingDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
    // Set the minimum window size to initial size.
    lpMMI->ptMinTrackSize.x = m_rcInit.Width();
    lpMMI->ptMinTrackSize.y = m_rcInit.Height();

    CDialogEx::OnGetMinMaxInfo(lpMMI);
}


void CResizingDialog::OnClose()
{
    SaveWindowPosition(m_strWindowID, this);

    CDialogEx::OnClose();
}
Community
  • 1
  • 1
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 2
    You want to specialize your dialog base class. C++ inheritance is the standard solution to that problem. Just create your dialog base class, derived from `CDialogEx`, provide a c'tor with the same arguments as `CDialogEx`' plus your string identifier. Add a `const CString&` member to that base, and initialize it from your c'tor's initializer list, just like the base. Essentially something like `CDialogBase::CDialogBase(const CString& phrase, UINT nIDTemplate, CWnd* pParent=NULL) : m_phrase(phrase), CDialogEx(nIDTemplate, pParent) {}`. – IInspectable Jun 19 '18 at 10:24
  • @IInspectable Please see updated question. – Andrew Truckle Jun 19 '18 at 10:46
  • @IInspectable Added some more information. – Andrew Truckle Jun 19 '18 at 11:02
  • 1
    Just create your `CDialogEx` specialization from scratch, without using the (often broken) Wizard. It's better for the learning experience anyway. – zett42 Jun 19 '18 at 11:06
  • @zett42 I prefer to learn from a good tutorial if I am doing something by scratch, as opposed to blindly trying. I often find if they are not created correctly with teh classwizard that all the messages don't show in the properties as it doesn't realise what type of object it is. – Andrew Truckle Jun 19 '18 at 11:10
  • @zett42 Working on it – Andrew Truckle Jun 19 '18 at 11:20
  • @zett42 Hit a snag, please see updated question. – Andrew Truckle Jun 19 '18 at 11:47
  • `#include ` on stdafx.h – sergiol Jun 19 '18 at 11:55
  • @sergiol That is already included in the `stdafx.h` file. – Andrew Truckle Jun 19 '18 at 11:55
  • I had to `#include` the `stdafx.h` file first. – Andrew Truckle Jun 19 '18 at 11:57
  • 1
    You need to `#include "ResizingDialog.h"` in every other file using it! – sergiol Jun 19 '18 at 12:00
  • 1
    That's just how precompiled headers work. Everything ahead of the `#include ` directive is ignored, that's why you get the error, that the symbol is not a class. If you want to prevent such errors, you can use the [/FI (Name Forced Include File)](https://learn.microsoft.com/en-us/cpp/build/reference/fi-name-forced-include-file) compiler option, and not ever think about your precompiled header anymore. Well, until things do go wrong again. – IInspectable Jun 19 '18 at 12:00
  • @IInspectable OK, new switch to learn about. But in my project not all source files use precompiled headers. Each file is stated as such. Will using this switch affect them? And if I use it, is it `/FI[stdafx.h]`? – Andrew Truckle Jun 19 '18 at 12:04
  • 1
    I don't know the exact format of the compiler option, but you could use Visual Studio's project settings dialog (C/C++ -> Advanced -> Forced Include File) to enable it (and inspect the command line in case you are interested). I believe you can enable the option per project as well as on a per compilation unit basis. Anyway, I hardly ever use it anyway. Only exception is when I have to compile 3rd party code I cannot modify, but still want to continue using precompiled headers. – IInspectable Jun 19 '18 at 12:11

1 Answers1

1

Based on the comments encouraging me to try to create the class manually, I have it working:

#include "stdafx.h"
#include "resource.h"
#include "ResizingDialog.h"

IMPLEMENT_DYNAMIC(CResizingDialog, CDialogEx)

CResizingDialog::CResizingDialog(const CString& strWindowID, UINT nIDTemplate, CWnd* pParent /* nullptr */, bool bOnlyStorePosition /* false */)
    : m_strWindowID(strWindowID),
      m_bOnlyStorePosition(bOnlyStorePosition), CDialogEx(nIDTemplate, pParent)
{

}

CResizingDialog::~CResizingDialog()
{
}

void CResizingDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CResizingDialog, CDialogEx)
    ON_WM_CREATE()
    ON_WM_GETMINMAXINFO()
    ON_WM_CLOSE()
END_MESSAGE_MAP()

int CResizingDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDialogEx::OnCreate(lpCreateStruct) == -1)
        return -1;

    // Save Initial window size to m_rcInit
    GetWindowRect(&m_rcInit);

    return 0;
}

void CResizingDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
    // Set the minimum window size to initial size.
    lpMMI->ptMinTrackSize.x = m_rcInit.Width();
    lpMMI->ptMinTrackSize.y = m_rcInit.Height();

    CDialogEx::OnGetMinMaxInfo(lpMMI);
}

void CResizingDialog::OnClose()
{
    SaveWindowPosition(m_strWindowID, this);

    CDialogEx::OnClose();
}

void CResizingDialog::OnOK()
{
    SaveWindowPosition();
    CDialogEx::OnOK();
}

BOOL CResizingDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    if(!m_bOnlyStorePosition)
        InitialiseResizeIcon(m_bmpResize, m_lblResize, this);

    RestoreWindowPosition(m_strWindowID, this, true);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}

I decided to duplicate the methods that were in the app class into this new dialog class instead. Eventually they can be removed from the app class. The only thing I also had to do was #include my resource file because the image needs to know the value of the resource ID.

This is the ResizingDialog.h header:

#pragma once
#include <afxwin.h>

class CResizingDialog : public CDialogEx
{
    DECLARE_DYNAMIC(CResizingDialog)

public:
    CResizingDialog(const CString& phrase, UINT nIDTemplate, CWnd* pParent = nullptr, bool bOnlyStorePosition = false); // Constructor
    virtual ~CResizingDialog(); // Destructor

protected:
    void OnOK() override;
    virtual void DoDataExchange(CDataExchange* pDX) override;    // DDX/DDV support
    void SaveWindowPosition(void) { SaveWindowPosition(m_strWindowID, this); }

public:
    BOOL OnInitDialog() override;
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
    afx_msg void OnClose();
    DECLARE_MESSAGE_MAP()

private:
    CBitmap m_bmpResize;
    CStatic m_lblResize;
    CRect m_rcInit;
    CString m_strWindowID;
    bool m_bOnlyStorePosition;

    void RestoreWindowPosition(CString strWindow, CWnd* pWindow, bool bOverrideState = false);
    void SaveWindowPosition(CString strWindow, CWnd* pWindow);
    void InitialiseResizeIcon(CBitmap& rBmpResize, CStatic& rLblResize, CWnd* pDialog);
};

The actual functions SaveWindowPosition, RestoreWindowPosition and InitialiseResizeIcon are not shown here as they don't directly relate to the issue.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • You should also include the header file. – zett42 Jun 19 '18 at 12:53
  • @zett42 What header? – Andrew Truckle Jun 19 '18 at 13:06
  • 1
    "ResizingDialog.h". Someone who reads this answer to learn how to inherit from `CDialogEx` would surely need this information. – zett42 Jun 19 '18 at 14:35
  • @zett42 That is line three in my code. Surely it is common sense if they use this class to derive from in another class that they include the header? #confused :) – Andrew Truckle Jun 19 '18 at 14:47
  • 1
    I meant to include the content of "ResizingDialog.h" in the answer, not the `#include` line. – zett42 Jun 19 '18 at 15:27
  • @zett42 Ah, right. I have added it. Note that I do not understand why I have to specify my fourth paramater `bOnlyStorePosition` since it defaults to `false`. But if I do not specify it the compiler complains. That is another issue. – Andrew Truckle Jun 19 '18 at 16:13
  • In your header the 4th c'tor parameter doesn't have a default defined. – zett42 Jun 19 '18 at 16:44
  • @zett42 OK, I revised my answer to have the updated code. I do specify a default. – Andrew Truckle Jun 19 '18 at 16:50
  • Oops, you have updated the code of the .cpp, not the .h file. – zett42 Jun 19 '18 at 16:56
  • 1
    I just refreshed the page, it's not. The declarations in .h and .cpp file must be swapped. – zett42 Jun 19 '18 at 17:17
  • @zett42 What a muppet. Thanks. Fixed. – Andrew Truckle Jun 19 '18 at 17:24
  • You don't need `OnCreate`. You can put the window initialization in `OnInitDialog` - The constructor and destructor have to be `public`. Members like `OnInitDialog` should be `protected` because you don't need to call them from outside, it doesn't really matter though... – Barmak Shemirani Jun 20 '18 at 14:45
  • @BarmakShemirani Where does `OnInitDialog` fit in relation to `OnGetMinMaxInfo`? – Andrew Truckle Jun 20 '18 at 15:45
  • `OnGetMinMaxInfo` is called before `OnInitDialog` and `OnCreate`. So `m_rcInit` is not properly initialized, it initially mis-informs the dialog that the minimum size is zero, but it's not a problem. You can initialize `m_rcInit` in constructor to set it to zero, to make it clear. – Barmak Shemirani Jun 20 '18 at 15:50
  • @BarmakShemirani Then I don't get it. `OnGetMinMaxInfo` is supposed to state the minimum size of the dialog. This always works for me the way I have it. – Andrew Truckle Jun 20 '18 at 15:57
  • 1
    You asked about the relation `OnGetMinMaxInfo` to `OnInitDialog`. It's called once before `OnInitDialog`. It's called again whenever you resize, by that time `m_rcInit` is set and it works as expected. – Barmak Shemirani Jun 20 '18 at 16:15