WinAPI Object-Oriented Classes

triumphost 0 Tallied Votes 4K Views Share

Creating WinAPI windows and controls can be a pain and quite difficult/annoying at times. Most of the time, I find myself searching MSDN, DaniWeb, and StackOverflow. I hate having to use external libraries unless I absolutely have no other choice.

Below contains code for creating WinAPI windows, Controls, Sub-Classing, and EventHandling for each control. There is a sample working code below this article and an image of what the sample code produces.

There are only 4 files to include to your projects:

Form.hpp -- Form.cpp
Controls.hpp -- Controls.cpp

The code for these files are as follows:

Form.hpp:

#ifndef FORM_HPP_INCLUDED
#define FORM_HPP_INCLUDED

#include <windows.h>
#include <iostream>

class Form
{
    public:
        Form(std::string Title, std::string Class, WNDPROC WindowProcedure = nullptr, WNDCLASSEX WndClass = {0}, DWORD dwStyleEx = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW, POINT Location = {CW_USEDEFAULT, CW_USEDEFAULT}, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT, HWND Parent = HWND_DESKTOP, HMENU Menu = nullptr);
        Form(const Form &F) = delete;
        Form(Form && F) = delete;
        virtual ~Form();

    private:
        bool Message = false;
        HWND WindowHandle = nullptr;
        Form& operator = (const Form &F) = delete;
        Form& operator = (Form && F) = delete;
        static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);

    protected:
        MSG Messages = {nullptr};
        virtual int Show() = 0;
        virtual int MessageLoop();
        virtual HWND Handle() const {return WindowHandle;}
        virtual void SubClassWindow(HWND Window, WNDPROC SubClassProc);
        virtual void UnSubClassWindow(HWND Window);
};


#endif // FORM_HPP_INCLUDED

Form.cpp:

#include "Form.hpp"

void Form::SubClassWindow(HWND Window, WNDPROC SubClassProc)
{
    SetWindowLongPtr(Window, GWLP_USERDATA, static_cast<LONG_PTR>(SetWindowLongPtr(Window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SubClassProc))));
}

void Form::UnSubClassWindow(HWND Window)
{
    SetWindowLongPtr(Window, GWLP_WNDPROC, GetWindowLongPtr(Window, GWLP_USERDATA));
}

int Form::MessageLoop()
{
    if (!Message)
    {
        Message = true;
        while(GetMessage(&Messages, nullptr, 0, 0))
        {
            TranslateMessage(&Messages);
            DispatchMessage(&Messages);
        }
        return Messages.wParam;
    }
    return 0;
}

Form::~Form() {}

Form::Form(std::string Title, std::string Class, WNDPROC WindowProcedure, WNDCLASSEX WndClass, DWORD dwStyleEx, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU Menu)
{
    if (WindowProcedure == nullptr)
    {
        WindowProcedure = Form::WindowProcedure;
    }

    if (!WndClass.cbSize)
    {
        WndClass =
        {
            sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure,
            0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
            LoadCursor(nullptr, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_BACKGROUND),
            nullptr, Class.c_str(), LoadIcon(nullptr, IDI_APPLICATION)
        };
    }

    if(RegisterClassEx(&WndClass))
    {
        WindowHandle = CreateWindowEx(dwStyleEx, Class.c_str(), Title.c_str(), dwStyle, Location.x, Location.y, Width, Height, Parent, Menu, GetModuleHandle(nullptr), this);
    }
}

LRESULT __stdcall Form::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(Hwnd, Msg, wParam, lParam);
    }
    return 0;
}

Controls.hpp:

#ifndef CONTROLS_HPP_INCLUDED
#define CONTROLS_HPP_INCLUDED

#include <windows.h>
#include <Commctrl.h>
#include <string>
#include <functional>
#include <array>
#include <vector>

class Control
{
    private:
        HMENU ID;
        static SUBCLASSPROC GlobalSubClass;
        HWND Handle, Parent;
        std::string Class, Title;
        DWORD dwExStyle, dwStyle;
        POINT Location;
        int Width, Height;
        void Swap(Control &C);
        std::array<std::function<void(HWND, UINT, WPARAM, LPARAM)>, 20> Functions;

    public:
        Control(const Control &C) = delete;
        Control(Control && C);
        Control(DWORD dwExStyle, std::string Class, std::string Title, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HINSTANCE Instance, void* LParam);
        Control(DWORD dwExStyle, std::string Class, std::string Title, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU ID, HINSTANCE Instance, void* LParam);
        virtual ~Control();

        virtual HMENU GetID() const;
        virtual HWND GetHandle() const;
        virtual HWND GetParent() const;
        virtual std::string GetClass() const;
        virtual int GetWidth() const;
        virtual int GetHeight() const;
        virtual POINT GetLocation() const;
        virtual bool IsEnabled() const;
        virtual bool IsVisible() const;
        virtual bool GetTabStop() const;
        virtual std::string GetText() const;

        virtual void SetTabStop(bool Enabled);
        virtual void SetEnabled(bool Enabled);
        virtual void SetVisibility(bool Visible);
        virtual void SetBounds(RECT Bounds);
        virtual void SetText(const std::string &Text);
        virtual void SetParent(HWND Parent);
        virtual void SetLocation(POINT Location);
        virtual void SetSize(int Width, int Height);
        virtual void SetStyle(DWORD Style, bool RemoveStyle = false);

        virtual void Dispose();
        Control& operator = (const Control &C) = delete;
        Control& operator = (Control && C);

        static void SetGlobalSubClass(SUBCLASSPROC Procedure);
        void SetEvent(std::size_t Index, std::function<void(HWND, UINT, WPARAM, LPARAM)> Func);
        void ProcessEvent(std::size_t Index, HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);


    protected:
        Control();
        bool Initialized;
        void UnInitialized();
        static std::size_t IDs;
        void SetParentHandle(HWND Parent);
        static LRESULT __stdcall SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};

class Button : public Control
{
    public:
        Button();
        Button(const Button &B) = delete;
        Button(Button && B);
        Button(std::string Title, POINT Location, int Width, int Height, HWND Parent);
        virtual ~Button();

        Button& operator = (const Button &B) = delete;
        virtual Button& operator = (Button && B);
};

class Label : public Control
{
    public:
        Label();
        Label(const Label &L) = delete;
        Label(Label&& L);
        Label(std::string Text, POINT Location, int Width, int Height, HWND Parent);
        virtual ~Label();

        Label& operator = (const Label &L) = delete;
        virtual Label& operator = (Label&& L);
};

class CheckBox : public Control //BS_PUSHLIKE
{
    public:
        CheckBox();
        CheckBox(const CheckBox &C) = delete;
        CheckBox(CheckBox && C);
        CheckBox(std::string Title, POINT Location, int Width, int Height, HWND Parent, bool Checked = false);
        virtual ~CheckBox();

        virtual bool IsChecked();
        virtual void SetChecked(bool Checked);

        CheckBox& operator = (const CheckBox &C) = delete;
        virtual CheckBox& operator = (CheckBox && C);
};

class TextBox : public Control
{
    public:
        TextBox();
        TextBox(const TextBox &T) = delete;
        TextBox(TextBox && T);
        TextBox(std::string Text, POINT Location, int Width, int Height, HWND Parent, bool MultiLine = false);
        virtual ~TextBox();

        virtual void SetReadOnly(bool ReadOnly);
        virtual void SetPassword(bool Enabled, char PasswordChar = '*');
        virtual std::uint32_t GetTextLength() const;
        virtual void AppendText(const std::string &Text) const;
        virtual void ShowScrollBar(bool Show, int wBar = SB_VERT);

        TextBox& operator = (const TextBox &T) = delete;
        virtual TextBox& operator = (TextBox && T);
};

class ListBox : public Control
{
    public:
        ListBox();
        ListBox(const ListBox &L) = delete;
        ListBox(ListBox && L);
        ListBox(POINT Location, int Width, int Height, HWND Parent);
        virtual ~ListBox();

        virtual std::string GetText() = delete;
        virtual int GetItemCount() const;
        virtual int GetSelectedIndex() const;
        virtual void SetSelectedIndex(int Index);
        virtual void AddItem(const std::string &Item, int Index = -1);
        virtual void RemoveItem(int Index);
        virtual void Clear();
        virtual int GetIndexOf(const std::string &Item);
        virtual int GetIndexPartial(const std::string &Item);
        virtual void SetColumnWidth(int Width);
        virtual std::string GetItem(int Index) const;

        ListBox& operator = (const ListBox &L) = delete;
        virtual ListBox& operator = (ListBox && L);
};

class ComboBox : public Control
{
    public:
        enum DropDownStyle {STYLE_SIMPLE, STYLE_DROPDOWN, STYLE_DROPDOWN_LIST};

        ComboBox();
        ComboBox(const ComboBox &C) = delete;
        ComboBox(ComboBox && C);
        ComboBox(DropDownStyle Style, POINT Location, int Width, int Height, HWND Parent);
        virtual ~ComboBox();

        virtual std::string GetText() = delete;
        virtual int GetItemCount() const;
        virtual int GetSelectedIndex() const;
        virtual void SetSelectedIndex(int Index);
        virtual void AddItem(const std::string &Item, int Index = -1);
        virtual void RemoveItem(int Index);
        virtual void Clear();
        virtual int GetIndexOf(const std::string &Item);
        virtual int GetIndexPartial(const std::string &Item);
        virtual void SetDropDownWidth(int Width);
        virtual void SetDropDownStyle(DropDownStyle Style);
        virtual std::string GetItem(int Index) const;

        ComboBox& operator = (const ComboBox &C) = delete;
        virtual ComboBox& operator = (ComboBox && C);
};

class PictureBox: public Control
{
    public:
        PictureBox();
        PictureBox(const PictureBox &P) = delete;
        PictureBox(PictureBox && P);
        PictureBox(POINT Location, int Width, int Height, HWND Parent);
        virtual ~PictureBox();

        virtual std::string GetText() = delete;
        virtual void SetImage(HBITMAP Img);

        PictureBox& operator = (const PictureBox &P) = delete;
        virtual PictureBox& operator = (PictureBox && P);
};

class MenuBar: public Control
{
    private:
        std::vector<HMENU> Menus;

    public:
        MenuBar();
        MenuBar(const MenuBar &M) = delete;
        MenuBar(MenuBar && M);
        MenuBar(HWND Parent);
        virtual ~MenuBar();

        std::string GetText(HMENU Menu, int Position);
        std::uint32_t GetMenuItemID(HMENU Menu, int Position);
        HMENU FindMenuItem(std::string MenuName);
        HMENU FindMenuItem(HMENU Parent, std::string MenuName);

        void CreateSubMenu(HMENU Parent, std::string Name, DWORD Style);
        void AddMenuItem(std::string Name, DWORD Style);
        void AppendMenuItem(HMENU Parent, std::string Name, DWORD Style);
        bool ToggleItemCheck(HMENU MenuItem, int Position);
        bool IsItemChecked(HMENU MenuItem, int Position);
        void Show();

        MenuBar& operator = (const MenuBar &M) = delete;
        virtual MenuBar& operator = (MenuBar && M);
};

class ToolBar: public Control
{
    public:
        ToolBar();
        ToolBar(const ToolBar &T) = delete;
        ToolBar(ToolBar && T);
        ToolBar(POINT Location, int Width, int Height, HWND Parent);
        virtual ~ToolBar();

        virtual std::string GetText() = delete;

        ToolBar& operator = (const ToolBar &T) = delete;
        virtual ToolBar& operator = (ToolBar && T);
};

#endif // CONTROLS_HPP_INCLUDED

Controls.cpp:

#include "Controls.hpp"

std::size_t Control::IDs = 0;
SUBCLASSPROC Control::GlobalSubClass = nullptr;

void Control::Swap(Control &C)
{
    using std::swap;
    swap(ID, C.ID);
    swap(Handle, C.Handle);
    swap(Parent, C.Parent);
    swap(Class, C.Class);
    swap(Title, C.Title);
    swap(dwExStyle, C.dwExStyle);
    swap(dwStyle, C.dwStyle);
    swap(Location, C.Location);
    swap(Width, C.Width);
    swap(Height, C.Height);
    swap(Functions, C.Functions);
    swap(Initialized, C.Initialized);
}

void Control::UnInitialized()
{
    if (!Initialized)
    {
        throw std::runtime_error("\nError! Control Not Constructed!");
        MessageBox(nullptr, "Control Not Constructed!", "Initialization Error!", MB_ICONERROR);
        ExitProcess(0);
    }
}

Control::~Control()
{
    RemoveWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID));
    SetWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID), 0);
}

Control::Control(Control && C) : ID(std::move(C.ID)), Handle(std::move(C.Handle)), Parent(std::move(C.Parent)), Class(std::move(C.Class)), Title(std::move(C.Title)), dwExStyle(std::move(C.dwExStyle)), dwStyle(std::move(C.dwStyle)), Location(std::move(C.Location)), Width(std::move(C.Width)), Height(std::move(C.Height)), Functions(std::move(C.Functions)), Initialized(std::move(C.Initialized))
{
    RemoveWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID));
    SetWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
}

Control::Control() : ID(nullptr), Handle(nullptr), Parent(nullptr), Class(std::string()), Title(std::string()), dwExStyle(0), dwStyle(0), Location({0, 0}), Width(0), Height(0), Functions(), Initialized(false) {}

Control::Control(DWORD dwExStyle, std::string Class, std::string Title, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HINSTANCE Instance, void* LParam) : ID(nullptr), Handle(nullptr), Parent(Parent), Class(Class), Title(Title), dwExStyle(dwExStyle), dwStyle(dwStyle), Location(Location), Width(Width), Height(Height), Functions(), Initialized(true)
{
    ID = reinterpret_cast<HMENU>(++IDs);
    Handle = CreateWindowEx(dwExStyle, Class.c_str(), Title.c_str(), dwStyle, Location.x, Location.y, Width, Height, Parent, ID, Instance, LParam);
    SetWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
}

Control::Control(DWORD dwExStyle, std::string Class, std::string Title, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU ID, HINSTANCE Instance, void* LParam) : ID(ID), Handle(nullptr), Parent(Parent), Class(Class), Title(Title), dwExStyle(dwExStyle), dwStyle(dwStyle), Location(Location), Width(Width), Height(Height), Functions(), Initialized(true)
{
    Handle = CreateWindowEx(dwExStyle, Class.c_str(), Title.c_str(), dwStyle, Location.x, Location.y, Width, Height, Parent, ID, Instance, LParam);
    SetWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
}

HMENU Control::GetID() const
{
    return ID;
}

HWND Control::GetHandle() const
{
    return Handle;
}

HWND Control::GetParent() const
{
    return Parent;
}

std::string Control::GetClass() const
{
    return Class;
}

int Control::GetWidth() const
{
    return Width;
}

int Control::GetHeight() const
{
    return Height;
}

POINT Control::GetLocation() const
{
    return Location;
}

bool Control::IsEnabled() const
{
    return IsWindowEnabled(Handle);
}

bool Control::IsVisible() const
{
    return IsWindowVisible(Handle);
}

bool Control::GetTabStop() const
{
    return ((GetWindowLongPtr(Handle, GWL_STYLE) & WS_TABSTOP) != 0);
}

void Control::SetTabStop(bool Enabled)
{
    SetWindowLongPtr(Handle, GWL_STYLE, GetWindowLongPtr(Handle, GWL_STYLE) | WS_TABSTOP);
}

void Control::SetEnabled(bool Enabled)
{
    EnableWindow(Handle, Enabled);
}

void Control::SetVisibility(bool Visible)
{
    ShowWindow(Handle, Visible ? SW_SHOW : SW_HIDE);
}

void Control::SetBounds(RECT Bounds)
{
    MoveWindow(Handle, Bounds.left, Bounds.top, Bounds.right - Bounds.left, Bounds.bottom - Bounds.top, false);
}

void Control::SetParentHandle(HWND Parent)
{
    this->Parent = Parent;
}

std::string Control::GetText() const
{
    std::vector<TCHAR> Buffer(GetWindowTextLength(Control::GetHandle()) + 1);
    GetWindowText(Control::GetHandle(), Buffer.data(), Buffer.size());
    return std::string(Buffer.begin(), Buffer.end());
}

void Control::SetText(const std::string &Text)
{
    UnInitialized();
    this->Title = Text;
    SetWindowText(Handle, Text.c_str());
}

void Control::SetParent(HWND Parent)
{
    UnInitialized();
    this->Parent = Parent;
    ::SetParent(Handle, Parent);
    ShowWindow(Handle, SW_SHOW);
}

void Control::SetLocation(POINT Location)
{
    UnInitialized();
    this->Location = Location;
    SetWindowPos(Handle, HWND_TOP, Location.x, Location.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}

void Control::SetSize(int Width, int Height)
{
    UnInitialized();
    this->Width = Width;
    this->Height = Height;
    SetWindowPos(Handle, HWND_TOP, 0, 0, Width, Height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}

void Control::SetStyle(DWORD Style, bool RemoveStyle)
{
    if (RemoveStyle)
    {
        SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE) & ~Style);
    }
    else
    {
        SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE) | Style);
    }
    SetWindowPos(Handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}

void Control::Dispose()
{
    ID = nullptr;
    Parent = nullptr;

    for (int I = 0; I < 6; ++I)
    {
        Functions[I] = nullptr;
    }

    if (Handle != nullptr)
    {
        RemoveWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID));
        DestroyWindow(Handle);
        Handle = nullptr;
    }
}

Control& Control::operator = (Control && C)
{
    C.Swap(*this);
    RemoveWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID));
    SetWindowSubclass(Handle, GlobalSubClass ? GlobalSubClass : SubClass, reinterpret_cast<UINT_PTR>(ID), reinterpret_cast<DWORD_PTR>(this));
    C.Dispose();
    return *this;
}

void Control::SetGlobalSubClass(SUBCLASSPROC Procedure)
{
    if (Control::GlobalSubClass == nullptr && Procedure != nullptr)
    {
        Control::GlobalSubClass = Procedure;
    }
}

void Control::SetEvent(std::size_t Index, std::function<void(HWND, UINT, WPARAM, LPARAM)> Func)
{
    if (Index < this->Functions.size() && Func != nullptr)
    {
        this->Functions[Index] = Func;
    }
}

void Control::ProcessEvent(std::size_t Index, HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (Index < this->Functions.size() && this->Functions[Index] != nullptr)
    {
        this->Functions[Index](Hwnd, Msg, wParam, lParam);
    }
}

LRESULT __stdcall Control::SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    //GET DYNAMIC ID.. GetWindowLong(HWnd, GWL_ID);
    //GET CLASS NAME.. GetClassName(HWnd, out LPSTR, MaxCount);
    //Control* Instance = reinterpret_cast<Control*>(dwRefData);

    switch(Msg)
    {
        case WM_NCDESTROY:
        {
            RemoveWindowSubclass(Window, GlobalSubClass ? GlobalSubClass : SubClass, uIdSubclass);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        default:
            return DefSubclassProc(Window, Msg, wParam, lParam);
    }
    return 0;
}

Button::~Button() {}

Button::Button() : Control() {};

Button::Button(Button&& B) : Control(std::forward<Button&&>(B)) {}

Button::Button(std::string Title, POINT Location, int Width, int Height, HWND Parent) : Control(0, "Button", Title, WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, Location, Width, Height, Parent, nullptr, nullptr) {}

Button& Button::operator = (Button && B)
{
    Control::operator = (std::forward<Button&&>(B));
    return *this;
}

Label::~Label() {}

Label::Label() : Control() {};

Label::Label(Label&& L) : Control(std::forward<Label&&>(L)) {}

Label::Label(std::string Title, POINT Location, int Width, int Height, HWND Parent) : Control(0, "Static", Title, WS_CHILD | WS_VISIBLE, Location, Width, Height, Parent, nullptr, nullptr) {}

Label& Label::operator = (Label&& L)
{
    Control::operator = (std::forward<Label&&>(L));
    return *this;
}

CheckBox::~CheckBox() {}

CheckBox::CheckBox() : Control() {};

CheckBox::CheckBox(CheckBox&& C) : Control(std::forward<CheckBox&&>(C)) {}

CheckBox::CheckBox(std::string Title, POINT Location, int Width, int Height, HWND Parent, bool Checked) : Control(0, "Button", Title, WS_CHILD | BS_AUTOCHECKBOX | WS_VISIBLE, Location, Width, Height, Parent, nullptr, nullptr)
{
    if (Checked)
    {
        CheckDlgButton(Control::GetHandle(), reinterpret_cast<std::size_t>(Control::GetID()), BST_CHECKED);
    }
}

bool CheckBox::IsChecked()
{
    return IsDlgButtonChecked(Control::GetHandle(), reinterpret_cast<std::size_t>(Control::GetID()));
}

void CheckBox::SetChecked(bool Checked)
{
    CheckDlgButton(Control::GetHandle(), reinterpret_cast<std::size_t>(Control::GetID()), Checked ? BST_CHECKED : BST_UNCHECKED);
}

CheckBox& CheckBox::operator = (CheckBox && C)
{
    Control::operator = (std::forward<CheckBox&&>(C));
    return *this;
}

TextBox::~TextBox() {}

TextBox::TextBox() : Control() {};

TextBox::TextBox(TextBox && T) : Control(std::forward<TextBox&&>(T)) {}

TextBox::TextBox(std::string Text, POINT Location, int Width, int Height, HWND Parent, bool MultiLine) : Control(WS_EX_STATICEDGE, "Edit", Text, WS_CHILD | WS_VISIBLE | (MultiLine ? ES_MULTILINE | ES_AUTOVSCROLL : 0), Location, Width, Height, Parent, nullptr, nullptr) {}

void TextBox::SetReadOnly(bool ReadOnly)
{
    SendMessage(Control::GetHandle(), EM_SETREADONLY, ReadOnly, 0);
}

void TextBox::SetPassword(bool Enabled, char PasswordChar)
{
    SendMessage(Control::GetHandle(), EM_SETPASSWORDCHAR, Enabled ? PasswordChar : 0, 0);
}

std::uint32_t TextBox::GetTextLength() const
{
    return GetWindowTextLength(Control::GetHandle());
}

void TextBox::ShowScrollBar(bool Show, int wBar)
{
    ::ShowScrollBar(Control::GetHandle(), wBar, true);
}

void TextBox::AppendText(const std::string &Text) const
{
    SendMessage(Control::GetHandle(), EM_SETSEL, -1, -1);
    SendMessage(Control::GetHandle(), EM_REPLACESEL, 0, reinterpret_cast<LPARAM>(Text.c_str()));
}

TextBox& TextBox::operator = (TextBox && T)
{
    Control::operator = (std::forward<TextBox&&>(T));
    return *this;
}

ListBox::~ListBox() {}

ListBox::ListBox() : Control() {};

ListBox::ListBox(ListBox && L) : Control(std::forward<ListBox&&>(L)) {}

ListBox::ListBox(POINT Location, int Width, int Height, HWND Parent) : Control(WS_EX_CLIENTEDGE, "Listbox", std::string(), WS_CHILD | WS_VISIBLE | LBS_NOTIFY, Location, Width, Height, Parent, nullptr, nullptr) {}

int ListBox::GetItemCount() const
{
    return SendMessage(Control::GetHandle(), LB_GETCOUNT, 0, 0);
}

int ListBox::GetSelectedIndex() const
{
    return SendMessage(Control::GetHandle(), LB_GETCURSEL, 0, 0);
}

void ListBox::SetSelectedIndex(int Index)
{
    SendMessage(Control::GetHandle(), LB_SETCURSEL, Index, 0);
}

void ListBox::AddItem(const std::string &Item, int Index)
{
    SendMessage(Control::GetHandle(), Index == 0 ? LB_ADDSTRING : LB_INSERTSTRING, Index, reinterpret_cast<LPARAM>(Item.c_str()));
}

void ListBox::RemoveItem(int Index)
{
    SendMessage(Control::GetHandle(), LB_DELETESTRING, Index, 0);
}

void ListBox::Clear()
{
    SendMessage(Control::GetHandle(), LB_RESETCONTENT, 0, 0);
}

int ListBox::GetIndexOf(const std::string &Item)
{
    return SendMessage(Control::GetHandle(), LB_FINDSTRINGEXACT, -1, reinterpret_cast<LPARAM>(Item.c_str()));
}

int ListBox::GetIndexPartial(const std::string &Item)
{
    return SendMessage(Control::GetHandle(), LB_FINDSTRING, -1, reinterpret_cast<LPARAM>(Item.c_str()));
}

void ListBox::SetColumnWidth(int Width)
{
    SendMessage(Control::GetHandle(), LB_SETCOLUMNWIDTH, Width, 0);
}

std::string ListBox::GetItem(int Index) const
{
    std::vector<char> Buffer(SendMessage(Control::GetHandle(), LB_GETTEXTLEN, Index, 0) + 1);
    SendMessage(Control::GetHandle(), LB_GETTEXT, Index, reinterpret_cast<LPARAM>(Buffer.data()));
    return std::string(Buffer.begin(), Buffer.end());
}

ListBox& ListBox::operator = (ListBox && L)
{
    Control::operator = (std::forward<ListBox&&>(L));
    return *this;
}

ComboBox::~ComboBox() {}

ComboBox::ComboBox() : Control() {};

ComboBox::ComboBox(ComboBox && L) : Control(std::forward<ComboBox&&>(L)) {}

ComboBox::ComboBox(DropDownStyle Style, POINT Location, int Width, int Height, HWND Parent) : Control(WS_EX_CLIENTEDGE, "ComboBox", std::string(), (Style == DropDownStyle::STYLE_SIMPLE ? CBS_SIMPLE : Style == DropDownStyle::STYLE_DROPDOWN ? CBS_DROPDOWN : CBS_DROPDOWNLIST) | WS_CHILD | WS_VISIBLE, Location, Width, Height, Parent, nullptr, nullptr) {}

int ComboBox::GetItemCount() const
{
    return SendMessage(Control::GetHandle(), CB_GETCOUNT, 0, 0);
}

int ComboBox::GetSelectedIndex() const
{
    return SendMessage(Control::GetHandle(), CB_GETCURSEL, 0, 0);
}

void ComboBox::SetSelectedIndex(int Index)
{
    SendMessage(Control::GetHandle(), CB_SETCURSEL, Index, 0);
}

void ComboBox::AddItem(const std::string &Item, int Index)
{
    SendMessage(Control::GetHandle(), Index == 0 ? CB_ADDSTRING : CB_INSERTSTRING, Index, reinterpret_cast<LPARAM>(Item.c_str()));
}

void ComboBox::RemoveItem(int Index)
{
    SendMessage(Control::GetHandle(), CB_DELETESTRING, Index, 0);
}

void ComboBox::Clear()
{
    SendMessage(Control::GetHandle(), CB_RESETCONTENT, 0, 0);
}

int ComboBox::GetIndexOf(const std::string &Item)
{
    return SendMessage(Control::GetHandle(), CB_FINDSTRINGEXACT, -1, reinterpret_cast<LPARAM>(Item.c_str()));
}

int ComboBox::GetIndexPartial(const std::string &Item)
{
    return SendMessage(Control::GetHandle(), CB_FINDSTRING, -1, reinterpret_cast<LPARAM>(Item.c_str()));
}

void ComboBox::SetDropDownWidth(int Width)
{
    SendMessage(Control::GetHandle(), CB_SETDROPPEDWIDTH, Width, 0);
}

void ComboBox::SetDropDownStyle(DropDownStyle Style)
{
    Control::SetStyle(CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST, true);
    Control::SetStyle(Style == DropDownStyle::STYLE_SIMPLE ? CBS_SIMPLE : Style == DropDownStyle::STYLE_DROPDOWN ? CBS_DROPDOWN : CBS_DROPDOWNLIST);
}

std::string ComboBox::GetItem(int Index) const
{
    std::vector<char> Buffer(SendMessage(Control::GetHandle(), CB_GETLBTEXTLEN, Index, 0) + 1);
    SendMessage(Control::GetHandle(), CB_GETLBTEXT, Index, reinterpret_cast<LPARAM>(Buffer.data()));
    return std::string(Buffer.begin(), Buffer.end());
}

ComboBox& ComboBox::operator = (ComboBox && C)
{
    Control::operator = (std::forward<ComboBox&&>(C));
    return *this;
}

PictureBox::~PictureBox() {}

PictureBox::PictureBox() : Control() {};

PictureBox::PictureBox(PictureBox && P) : Control(std::forward<PictureBox&&>(P)) {}

PictureBox::PictureBox(POINT Location, int Width, int Height, HWND Parent) : Control(WS_EX_CLIENTEDGE, "Static", std::string(), WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_BITMAP, Location, Width, Height, Parent, nullptr, nullptr) {}

void PictureBox::SetImage(HBITMAP Img)
{
    SendMessage(Control::GetHandle(), STM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast<LPARAM>(Img));
}

PictureBox& PictureBox::operator = (PictureBox && P)
{
    Control::operator = (std::forward<PictureBox&&>(P));
    return *this;
}


MenuBar::~MenuBar() {}

MenuBar::MenuBar() : Control(), Menus() {};

MenuBar::MenuBar(MenuBar&& M) : Control(std::forward<MenuBar&&>(M)), Menus(std::move(M.Menus)) {}

MenuBar::MenuBar(HWND Parent) : Control(), Menus()
{
    Control::Initialized = false;
    this->SetParentHandle(Parent);
    Menus.push_back(CreateMenu());
}

std::string MenuBar::GetText(HMENU Menu, int Position)
{
    TCHAR Buffer[1024] = {0};
    MENUITEMINFO MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIIM_TYPE;
    MenuInfo.fType = MFT_STRING;
    MenuInfo.cch = sizeof(Buffer);
    MenuInfo.dwTypeData = Buffer;
    if (GetMenuItemInfo(Menu, Position, true, &MenuInfo))
    {
        return Buffer;
    }
    return std::string();
}

std::uint32_t MenuBar::GetMenuItemID(HMENU Menu, int Position)
{
    return ::GetMenuItemID(Menu, Position);
}

HMENU MenuBar::FindMenuItem(std::string MenuName)
{
    for (std::size_t I = 0; I < Menus.size(); ++I)
    {
        if (MenuName == GetText(Menus.front(), I))
        {
            return GetSubMenu(Menus.front(), I);
        }
    }
    return nullptr;
}

HMENU MenuBar::FindMenuItem(HMENU Parent, std::string MenuName)
{
    std::size_t Count = GetMenuItemCount(Parent);
    for (std::size_t I = 0; I < Count; ++I)
    {
        if (MenuName == GetText(Parent, I))
        {
            return GetSubMenu(Parent, I);
        }
    }
    return nullptr;
}

void MenuBar::CreateSubMenu(HMENU Parent, std::string Name, DWORD Style)
{
    Menus.push_back(CreatePopupMenu());
    AppendMenu(Parent, Style, reinterpret_cast<UINT_PTR>(Menus.back()), Name.c_str());
}

void MenuBar::AddMenuItem(std::string Name, DWORD Style)
{
    Menus.push_back(CreateMenu());
    MENUINFO MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIM_STYLE;
    GetMenuInfo(Menus.back(), &MenuInfo);
    MenuInfo.dwStyle |= MNS_NOTIFYBYPOS;
    SetMenuInfo(Menus.back(), &MenuInfo);
    AppendMenu(Menus.front(), Style, reinterpret_cast<UINT_PTR>(Menus.back()), Name.c_str());
}

void MenuBar::AppendMenuItem(HMENU Parent, std::string Name, DWORD Style)
{
    static int ID = 0;
    AppendMenu(Parent, Style, ++ID, Name.c_str());
}

bool MenuBar::ToggleItemCheck(HMENU MenuItem, int Position)
{
    MENUITEMINFO MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIIM_STATE;
    GetMenuItemInfo(MenuItem, Position, true, &MenuInfo);
    MenuInfo.fState = (MenuInfo.fState & MF_CHECKED ? MenuInfo.fState & ~MF_CHECKED : MenuInfo.fState | MF_CHECKED);
    SetMenuItemInfo(MenuItem, Position, true, &MenuInfo);
    return MenuInfo.fState & MF_CHECKED;
}

bool MenuBar::IsItemChecked(HMENU MenuItem, int Position)
{
    MENUITEMINFO MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIIM_STATE;
    GetMenuItemInfo(MenuItem, Position, true, &MenuInfo);
    return MenuInfo.fState & MF_CHECKED;
}

void MenuBar::Show()
{
    SetMenu(Control::GetParent(), Menus.front());
}

MenuBar& MenuBar::operator = (MenuBar && M)
{
    Control::operator = (std::forward<MenuBar&&>(M));
    return *this;
}


ToolBar::~ToolBar() {}

ToolBar::ToolBar() : Control() {};

ToolBar::ToolBar(ToolBar && T) : Control(std::forward<ToolBar&&>(T)) {}

ToolBar::ToolBar(POINT Location, int Width, int Height, HWND Parent) : Control(0, TOOLBARCLASSNAME, std::string(), WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | CCS_ADJUSTABLE | CCS_NODIVIDER, Location, Width, Height, Parent, nullptr, nullptr) {}

ToolBar& ToolBar::operator = (ToolBar && T)
{
    Control::operator = (std::forward<ToolBar&&>(T));
    return *this;
}
/**
	This is the working Sample.
**/

#include <windows.h>
#include "Form.hpp"
#include "Controls.hpp"


/*
    Class for easily creating a Window from a Form.
*/
class Window : public Form
{
    public:
        using Form::Form;
        Window(std::string Title, std::string Class, WNDPROC WindowProcedure, int Width, int Height)
            : Form(Title, Class, WindowProcedure, {0}, 0, WS_OVERLAPPEDWINDOW, {CW_USEDEFAULT, CW_USEDEFAULT}, Width, Height)
        {}

        int Show() override {return ShowWindow(Form::Handle(), SW_SHOW);}
        int MessageLoop() {return Form::MessageLoop();}
        HWND Handle() {return Form::Handle();}
};

/*
	Enumeration of a small list of events to handle.
	Feel free to add more.
*/
enum EVENT
{
    MOUSE_MOVE, MOUSE_WHEEL, MOUSE_LBUTTONDOWN, MOUSE_LBUTTONUP, MOUSE_LBUTTONDBLCLK, MOUSE_ACTIVATE,
    MOUSE_LEAVE, MOUSE_RBUTTONDOWN, MOUSE_RBUTTONUP, MOUSE_RBUTTONDBLCLK,
    KEY_DOWN, KEY_UP
};

/*
	Subclass/Event Handler for ALL controls.
	Feel free to handle more events.
*/
LRESULT __stdcall SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    Control* ctrl = reinterpret_cast<Control*>(dwRefData);

    switch(Msg)
    {
        case WM_LBUTTONDOWN:
        {
            ctrl->ProcessEvent(MOUSE_LBUTTONDOWN, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_LBUTTONUP:
        {
            ctrl->ProcessEvent(MOUSE_LBUTTONUP, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_LBUTTONDBLCLK:
        {
            ctrl->ProcessEvent(MOUSE_LBUTTONDBLCLK, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_RBUTTONDOWN:
        {
            ctrl->ProcessEvent(MOUSE_RBUTTONDOWN, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_RBUTTONUP:
        {
            ctrl->ProcessEvent(MOUSE_RBUTTONUP, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_RBUTTONDBLCLK:
        {
            ctrl->ProcessEvent(MOUSE_RBUTTONDBLCLK, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_MOUSEMOVE:
        {
            ctrl->ProcessEvent(MOUSE_MOVE, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_MOUSEACTIVATE:
        {
            ctrl->ProcessEvent(MOUSE_ACTIVATE, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_MOUSELEAVE:
        {
            ctrl->ProcessEvent(MOUSE_LEAVE, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_KEYDOWN:
        {
            ctrl->ProcessEvent(KEY_DOWN, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_KEYUP:
        {
            ctrl->ProcessEvent(KEY_UP, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        case WM_NCDESTROY:
        {
            RemoveWindowSubclass(Window, SubClass, uIdSubclass);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        default:
            return DefSubclassProc(Window, Msg, wParam, lParam);
    }
    return 0;
}


/*
	Some controls our Window will have.
	Add more if you like. This sample code will show the usage of these controls.
*/
MenuBar* Mnb;
TextBox* ReceiveBox;
TextBox* SendBox;
Button* SendButton;
ListBox* ContactsBox;
PictureBox* FriendPicture;
PictureBox* ClientPicture;


LRESULT WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
        case WM_CREATE:  //Creating our controls:
        {
            FriendPicture = new PictureBox({15, 15}, 125, 125, Hwnd);
            ClientPicture = new PictureBox({15, 235}, 125, 125, Hwnd);
            ReceiveBox = new TextBox("", {160, 15}, 500, 250, Hwnd, true);
            SendBox = new TextBox("", {160, 275}, 500, 100, Hwnd, true);
            SendButton = new Button("Send", {565, 382}, 95, 22, Hwnd);
            ContactsBox = new ListBox({675, 15}, 150, 395, Hwnd);

            //Changing behaviour of a control:
            ReceiveBox->ShowScrollBar(true);
            ReceiveBox->SetReadOnly(true);
            HideCaret(ReceiveBox->GetHandle());

            //Handling the event of a control through an std::function/lamda/regular function.
            SendButton->SetEvent(MOUSE_LBUTTONDOWN, [](HWND, UINT, WPARAM, LPARAM){std::cout<<"Send Button Clicked\n";});

            //Creating a menu-bar:
            Mnb = new MenuBar(Hwnd);
            Mnb->AddMenuItem("File", MF_POPUP);
            Mnb->AddMenuItem("Edit", MF_POPUP);
            Mnb->AddMenuItem("View", MF_POPUP);
            Mnb->AddMenuItem("Settings", MF_POPUP);
            Mnb->AddMenuItem("Help", MF_POPUP);

            HMENU FileMenu = Mnb->FindMenuItem("File");
            Mnb->CreateSubMenu(FileMenu, "Status", MF_POPUP);
            Mnb->AppendMenuItem(FileMenu, "Separator", MF_SEPARATOR);
            Mnb->AppendMenuItem(FileMenu, "Invite Contact", MF_STRING);
            Mnb->AppendMenuItem(FileMenu, "Close", MF_STRING);

            HMENU StatusMenu = Mnb->FindMenuItem(FileMenu, "Status");
            Mnb->AppendMenuItem(StatusMenu, "Online", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Away", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Busy", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Invisible", MF_STRING);

            HMENU EditMenu = Mnb->FindMenuItem("Edit");
            Mnb->AppendMenuItem(EditMenu, "Undo", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Redo", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Separator", MF_SEPARATOR);
            Mnb->AppendMenuItem(EditMenu, "Cut", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Copy", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Paste", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Paste Special", MF_STRING);

            HMENU ViewMenu = Mnb->FindMenuItem("View");
            Mnb->CreateSubMenu(ViewMenu, "Display", MF_POPUP);
            Mnb->AppendMenuItem(ViewMenu, "Show Contacts", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(ViewMenu, "Status Bar", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(Mnb->FindMenuItem(ViewMenu, "Display"), "Display Picture", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(Mnb->FindMenuItem(ViewMenu, "Display"), "Contact Picture", MF_STRING | MF_CHECKED);

            HMENU SettingsMenu = Mnb->FindMenuItem("Settings");
            Mnb->AppendMenuItem(SettingsMenu, "Change Picture", MF_STRING);
            Mnb->Show();
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(Hwnd, Msg, wParam, lParam);
    }
    return true;
}


int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    Control::SetGlobalSubClass(SubClass); //Make all controls use "SubClass" Event Handler.
    Window Win("Chat Client", "ChatClient", WindowProcedure, 855, 470);
    Win.Show();
    return Win.MessageLoop();
}
triumphost 120 Posting Whiz

The sample working code above will create a GUI that looks like:

d031fa04e76273e62b89e2173fd3ccb6

triumphost 120 Posting Whiz

If someone can edit my post for me, that would be great. If so, then please delete this comment.

Not sure why it won't let me edit the article but I was going to change:

Control.hpp:

std::map<std::int32_t, std::function<void(HWND, UINT, WPARAM, LPARAM)>> Functions;

Control.cpp:

void Control::SetEvent(std::size_t Index, std::function<void(HWND, UINT, WPARAM, LPARAM)> Func)
{
    if (Func != nullptr)
    {
        this->Functions.insert(std::make_pair(Index, Func));
    }
}

void Control::ProcessEvent(std::size_t Index, HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (this->Functions[Index] != nullptr)
    {
        this->Functions[Index](Hwnd, Msg, wParam, lParam);
    }
}

LRESULT __stdcall Control::SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    //GET DYNAMIC ID.. GetWindowLong(HWnd, GWL_ID);
    //GET CLASS NAME.. GetClassName(HWnd, out LPSTR, MaxCount);
    Control* Instance = reinterpret_cast<Control*>(dwRefData);

    switch(Msg)
    {
        case WM_NCDESTROY:
        {
            RemoveWindowSubclass(Window, GlobalSubClass ? GlobalSubClass : SubClass, uIdSubclass);
            return DefSubclassProc(Window, Msg, wParam, lParam);
        }

        default:
            Instance->ProcessEvent(Msg, Window, Msg, wParam, lParam);
            return DefSubclassProc(Window, Msg, wParam, lParam);
    }
    return 0;
}

Thus the sample example is MUCH shorter and becomes:

#include <windows.h>
#include "BaseForm.hpp"
#include "Form.hpp"
#include "Controls.hpp"

class Window : public Form
{
    public:
        using Form::Form;
        Window(std::string Title, std::string Class, WNDPROC WindowProcedure, int Width, int Height)
            : Form(Title, Class, WindowProcedure, {0}, 0, WS_OVERLAPPEDWINDOW, {CW_USEDEFAULT, CW_USEDEFAULT}, Width, Height)
        {}

        int Show() override {return ShowWindow(Form::Handle(), SW_SHOW);}
        int MessageLoop() {return Form::MessageLoop();}
        HWND Handle() {return Form::Handle();}
};

enum EVENT
{
    MOUSE_MOVE, MOUSE_WHEEL, MOUSE_LBUTTONDOWN, MOUSE_LBUTTONUP, MOUSE_LBUTTONDBLCLK, MOUSE_ACTIVATE,
    MOUSE_LEAVE, MOUSE_RBUTTONDOWN, MOUSE_RBUTTONUP, MOUSE_RBUTTONDBLCLK,
    KEY_DOWN, KEY_UP
};

MenuBar* Mnb;
TextBox* ReceiveBox;
TextBox* SendBox;
Button* SendButton;
ListBox* ContactsBox;
PictureBox* FriendPicture;
PictureBox* ClientPicture;


LRESULT WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
        case WM_CREATE:
        {
            FriendPicture = new PictureBox({15, 15}, 125, 125, Hwnd);
            ClientPicture = new PictureBox({15, 235}, 125, 125, Hwnd);
            ReceiveBox = new TextBox("", {160, 15}, 500, 250, Hwnd, true);
            SendBox = new TextBox("", {160, 275}, 500, 100, Hwnd, true);
            SendButton = new Button("Send", {565, 382}, 95, 22, Hwnd);
            ContactsBox = new ListBox({675, 15}, 150, 395, Hwnd);

            ReceiveBox->ShowScrollBar(true);
            ReceiveBox->SetReadOnly(true);
            HideCaret(ReceiveBox->GetHandle());

            SendButton->SetEvent(WM_LBUTTONDOWN, [](HWND, UINT, WPARAM, LPARAM){std::cout<<"Send Button Clicked\n";});

            Mnb = new MenuBar(Hwnd);
            Mnb->AddMenuItem("File", MF_POPUP);
            Mnb->AddMenuItem("Edit", MF_POPUP);
            Mnb->AddMenuItem("View", MF_POPUP);
            Mnb->AddMenuItem("Settings", MF_POPUP);
            Mnb->AddMenuItem("Help", MF_POPUP);

            HMENU FileMenu = Mnb->FindMenuItem("File");
            Mnb->CreateSubMenu(FileMenu, "Status", MF_POPUP);
            Mnb->AppendMenuItem(FileMenu, "Separator", MF_SEPARATOR);
            Mnb->AppendMenuItem(FileMenu, "Invite Contact", MF_STRING);
            Mnb->AppendMenuItem(FileMenu, "Close", MF_STRING);

            HMENU StatusMenu = Mnb->FindMenuItem(FileMenu, "Status");
            Mnb->AppendMenuItem(StatusMenu, "Online", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Away", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Busy", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Invisible", MF_STRING);

            HMENU EditMenu = Mnb->FindMenuItem("Edit");
            Mnb->AppendMenuItem(EditMenu, "Undo", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Redo", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Separator", MF_SEPARATOR);
            Mnb->AppendMenuItem(EditMenu, "Cut", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Copy", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Paste", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Paste Special", MF_STRING);

            HMENU ViewMenu = Mnb->FindMenuItem("View");
            Mnb->CreateSubMenu(ViewMenu, "Display", MF_POPUP);
            Mnb->AppendMenuItem(ViewMenu, "Show Contacts", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(ViewMenu, "Status Bar", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(Mnb->FindMenuItem(ViewMenu, "Display"), "Display Picture", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(Mnb->FindMenuItem(ViewMenu, "Display"), "Contact Picture", MF_STRING | MF_CHECKED);

            HMENU SettingsMenu = Mnb->FindMenuItem("Settings");
            Mnb->AppendMenuItem(SettingsMenu, "Change Picture", MF_STRING);
            Mnb->Show();
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(Hwnd, Msg, wParam, lParam);
    }
    return true;
}


int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    Window Win("Chat Client", "ChatClient", WindowProcedure, 855, 470);
    Win.Show();
    return Win.MessageLoop();
}
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I don't understand the point of this. Are you publishing code on Daniweb? Usually, code snippets are for demonstration purposes only. Daniweb is not exactly a platform for code publication in general, better try platforms like github or sourceforge.

If you want to publish code (in the sense of "hey, here is some code, use it"), then that requires a licensing notice (e.g., BSD, (L)GPLv(2-3), Boost, etc..). And on Daniweb it is a bit fuzzy when it comes to that because technically-speaking, Daniweb gets the copyright to the code.

That said, if what you want is constructive criticism about your code, then this is a lot to chew on. I'll have to make a few broad observations:

(1) Use namespaces. If you want to write "library code", you really, absolutely, need to put your library code into a namespace, with a name unique to your library (more or less).

(2) PImpl the windows header. One of the main reasons I can think of for wrapping the Win32 API code into classes (or other code) is to insulate the windows.h header. This header is one of the dirtiest piece of bad-C / barely-C++ code ever put in an "official" header file. The number one step would be to put it away, deep. The compilation firewall, or PImpl, is the idiom that you should be using for this purpose. You might also be able to get away with forward declarations or by only including the windef.h header, since Win32 API already uses a lot of opaque pointers.

(3) It would appear to me that the WindowsProcedure is leaking a lot of memory. Using global variables as a way to create the components seems like a very unreliable method. I would suggest to incorporate the WindowsProcedure function as a private member function of the Window class, along with a (static) function that dispatches the calls in a way similar to the way you do it with the ProcessEvent function for your controls. In other words, look up the instances of the Window class in a map indexed according to the window-handle. In both cases, you should use a std::unordered_map instead of a std::map when you have an integer (or handle, or opaque pointer) as the key type, and when you don't want to have the elements sorted by key-values. Once you can do that, you can start to attach components to a Window and encapsulate their initialization and their lifetime management into it. This would solve your leaking issues, and would remove the non-sense in the WindowsProcedure function of your example.

(4) There seems to somewhat of an abuse of virtual functions, or very monolithic classes. The hierarchy rooted with the Control base class seems to have either way too many virtual functions or be way too much of a "catch-all" base-class. I'm not overly familiar with the Win32 API (as there are many higher-level GUI toolkits or libraries that allows people to avoid having to deal with it), but it seems that this might be more of a problem with the API being too monolithic, and not so much a problem coming from your design choices. And then again, GUI class hierarchies are often monstreous beasts whichever way you split them, so, there might not be much you can do about this problem.

(5) Out with the old, in with the new. You should consider finding a way to get rid of any relics of a C API. The presence of function pointers like that WinProc thing, and that arcane message-passing interface with the wParam / lParam stuff. Wouldn't it be nice if the user could use your library in a uniform way and not have to be reminded of the archaic and arcane montrosity that is hidden below.

That's about all I can see from a quick look at it.

triumphost 120 Posting Whiz

Oh ok, I was not aware that I had to publish it with a license. There's no way for me to delete posts though.

As for the deleting of the memory in WindowProcedure. I was aware that it is leaking but I wasn't sure whether I should do something about it. The reason is because I saw the wxWidgets hello world example below:

bool MyApp::OnInit()
{
    MyFrame *frame = new MyFrame( _("Hello World"), wxPoint(50, 50), wxSize(450, 340) );
    frame->Show(true);
    SetTopWindow(frame);
    return true;
}

Notice that myframe is never deleted! None of the controls they create are ever deleted! Every example I ever see uses new and somehow never deletes a single thing.

I asked on StackOverflow and someone said: "The memory is deleted when the application is closed so just leave it".

I know it sounds extremely stupid and makes my eye twitch but I thought to myself, "they have 98k+ rep and are far better than I am, wxWidgets is a public and popular api, so it must be doing something right..".

That's why I didn't delete it. Any idea if what they said is true? Why does wxWidgets not delete? Is there a way to override new so that it when someone does:

new Something() it automatically deletes?

Yeah it has a lot of virtual functions but that is the only way I know how to override a base class function.

You should consider finding a way to get rid of any relics of a C API. The presence of function pointers like that WinProc thing

Not sure what you mean there?

Again, there's no way for me to edit or delete articles so I'm not sure how to do that.

EDIT: I asked again on SO, they told me the set functions delete the pointers in the parent's destructor. Can you delete my posts for me?

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Notice that myframe is never deleted! None of the controls they create are ever deleted! Every example I ever see uses new and somehow never deletes a single thing.

In that wxWidget example, the frame is created (with new) and then it is registered as the "top-window" using the SetTopWindow function (which is a member of the base-class wxApp of the MyApp class). I would assume that once this top-window is set, it will be deleted in the destructor or finalizer of the wxApp class.

This is how a lot of "old-school" C++ libraries work, and I would assume wxWidgets works that way. Certainly, Qt does. This is not great, because "modern" C++ has a number of established practices that are much better than that, such as using smart-pointers, but once you are committed to a particular system, you have to keep using it and live with its flaws or ambiguities. And GUI libraries like wxWidgets and Qt carry around quite a bit of that "old-school" baggage.

I asked on StackOverflow and someone said: "The memory is deleted when the application is closed so just leave it".

Not exactly true. The memory is reclaimed by the operating system when the application is closed, meaning that you won't get a system-wide problem as a result of this (i.e., it won't consume RAM after it is closed). However, objects are not deleted when the application is closed. In other words, any object that was dynamically allocated and constructed with the new operator is not going to be automatically deleted (in the sense of calling delete) when the application is closed. So, the memory is reclaimed (deallocated), but the destructors are not called. It is very possible that the destructors are not really necessary (or don't do anything), in which case, it doesn't matter if those objects are left to leak, but, in general, it is bad practice to do so. Just get used to not leaking any memory.

I know it sounds extremely stupid and makes my eye twitch but I thought to myself, "they have 98k+ rep and are far better than I am, wxWidgets is a public and popular api, so it must be doing something right..".

Stackoverflow is dangerous sometimes. Reputation points, especially in the very higher end, is not so much correlated with competence, but rather with ability to copy-paste and to post a lot of short and easy answers quickly. You don't get a 98k+ reputation by occasionally imparting unparalleled wisdom, but mainly by posting a massive quantity of quick and easy answers all over the place. Let people stand on the merits of their arguments and the clarity of their explanations.

As for wxWidgets, it is true that it is a public and popular API, and I would also assume that it has been doing a lot of things right. However, APIs are also not necessarily always as popular as they deserve (in terms of "quality"), and vice versa. Also, there is a lot of inertia in this field. Large and popular libraries tend to stick around for a long time because people are not prepared to migrate their entire code-base (millions of LOCs) to another "better" library, nor will they accept any significant changes to the existing library. This means that an old and large library like wxWidgets has to carry its own baggage (like old style of coding), and cannot renew itself very much. So, be careful with drawing lessons from looking at these old frameworks and libraries. As I say, they are a mix of good and old.

Is there a way to override new so that it when someone does: new Something() it automatically deletes?

No... well, I'm not sure. What you are describing is a garbage collector (it's a thing that watches the execution and monitors all object allocations and uses, and whenever objects are no longer in use, it deletes them). There are garbage collectors available for C++, and the standard allows it too. However, I am not familiar with any of this because I have never myself (or in any code that I have seen) needed a garbage collector for anything. As the saying goes, you only need a garbage collector if you produce garbage.

What does exist, and can help you significantly in this context, are smart-pointers. This is as close as you can (easily) get to the "allocate and automatically delete" behavior. In my experience, this will be sufficient, I have yet to see an example where it is not.

Yeah it has a lot of virtual functions but that is the only way I know how to override a base class function.

Virtual functions are the only (native) way to override base class functions (note that you could also use protected virtuals). The problem is that function overriding is something that should always serve a very specific purpose. And usually, with carefully designed interfaces, there is rarely a need for a single base-class having dozens and dozens of virtual functions, which means that it's either too big (does too much stuff in one class), or it has redundant or superfluous virtual functions. This is just from general experience that I say that, it doesn't mean that there aren't exceptions.

You should consider finding a way to get rid of any relics of a C API. The presence of function pointers like that WinProc thing

Not sure what you mean there?

What I mean is that you are writing this set of OOP wrappers for this C API (the Win32 API). But you are not making a complete job of hiding away all those elements of the underlying C API that you are wrapping. Imagine this, you have a person who comes up to you and says "I hate Win32 API, I hate C APIs, I don't ever want to see anything like it polluting my nice OOP-style C++ code, do you have an OOP C++ library that I could use?". Could you really present the code that you have as such a library? No. Because your users will have to define a WinProc function with handling of Windows Messages and these wParam / lParam parameters. Any user that is allergic to the Win32 API will have serious heart-attack if you expose him to that. That's what encapsulation means, that even if you tried to guess how things are implemented "under the hood", you couldn't figure it out, because everything about it is hidden from the user. The other side of that is also that you, as a library implementer, have all the liberty you want when it comes to changing those implementation details (such as porting to a different platform or version of the OS), which is a huge advantage.

So, just imagine that your users are fatally allergic to the Win32 API. You cannot leave any recognizable piece of it in your interface (the "visible" part of your code). That is the guideline in general, and that is also the hallmark of any good cross-platform or robust library.

The WindowsProcedure function is particularly aggregious. You cannot have the user create a separate WinAPI message-handling function, and have him pass that as a function pointer to the creation of the "Window" object. The Window class should encapsulate that part of the WinAPI inner-workings. What I mean is that currently the user has to do:

MenuBar* Mnb;
TextBox* ReceiveBox;
TextBox* SendBox;
Button* SendButton;
ListBox* ContactsBox;
PictureBox* FriendPicture;
PictureBox* ClientPicture;

LRESULT WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
        case WM_CREATE:
        {
            FriendPicture = new PictureBox({15, 15}, 125, 125, Hwnd);
            ClientPicture = new PictureBox({15, 235}, 125, 125, Hwnd);
            ReceiveBox = new TextBox("", {160, 15}, 500, 250, Hwnd, true);
            SendBox = new TextBox("", {160, 275}, 500, 100, Hwnd, true);
            SendButton = new Button("Send", {565, 382}, 95, 22, Hwnd);
            ContactsBox = new ListBox({675, 15}, 150, 395, Hwnd);

            ReceiveBox->ShowScrollBar(true);
            ReceiveBox->SetReadOnly(true);
            HideCaret(ReceiveBox->GetHandle());
            SendButton->SetEvent(WM_LBUTTONDOWN, [](HWND, UINT, WPARAM, LPARAM){std::cout<<"Send Button Clicked\n";});

            Mnb = new MenuBar(Hwnd);
            Mnb->AddMenuItem("File", MF_POPUP);
            Mnb->AddMenuItem("Edit", MF_POPUP);
            Mnb->AddMenuItem("View", MF_POPUP);
            Mnb->AddMenuItem("Settings", MF_POPUP);
            Mnb->AddMenuItem("Help", MF_POPUP);

            HMENU FileMenu = Mnb->FindMenuItem("File");
            Mnb->CreateSubMenu(FileMenu, "Status", MF_POPUP);
            Mnb->AppendMenuItem(FileMenu, "Separator", MF_SEPARATOR);
            Mnb->AppendMenuItem(FileMenu, "Invite Contact", MF_STRING);
            Mnb->AppendMenuItem(FileMenu, "Close", MF_STRING);

            HMENU StatusMenu = Mnb->FindMenuItem(FileMenu, "Status");
            Mnb->AppendMenuItem(StatusMenu, "Online", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Away", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Busy", MF_STRING);
            Mnb->AppendMenuItem(StatusMenu, "Invisible", MF_STRING);

            HMENU EditMenu = Mnb->FindMenuItem("Edit");
            Mnb->AppendMenuItem(EditMenu, "Undo", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Redo", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Separator", MF_SEPARATOR);
            Mnb->AppendMenuItem(EditMenu, "Cut", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Copy", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Paste", MF_STRING);
            Mnb->AppendMenuItem(EditMenu, "Paste Special", MF_STRING);

            HMENU ViewMenu = Mnb->FindMenuItem("View");
            Mnb->CreateSubMenu(ViewMenu, "Display", MF_POPUP);
            Mnb->AppendMenuItem(ViewMenu, "Show Contacts", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(ViewMenu, "Status Bar", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(Mnb->FindMenuItem(ViewMenu, "Display"), "Display Picture", MF_STRING | MF_CHECKED);
            Mnb->AppendMenuItem(Mnb->FindMenuItem(ViewMenu, "Display"), "Contact Picture", MF_STRING | MF_CHECKED);
            HMENU SettingsMenu = Mnb->FindMenuItem("Settings");
            Mnb->AppendMenuItem(SettingsMenu, "Change Picture", MF_STRING);
            Mnb->Show();
        }
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        default:
            return DefWindowProc(Hwnd, Msg, wParam, lParam);
    }
    return true;
}

int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    Window Win("Chat Client", "ChatClient", WindowProcedure, 855, 470);
    Win.Show();
    return Win.MessageLoop();
}

When the user should only have to do something like this:

int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpszArgument, int nCmdShow)
{
    Window Win("Chat Client", "ChatClient", 855, 470);

    PictureBox FriendPicture({15, 15}, 125, 125, Win);
    PictureBox ClientPicture({15, 235}, 125, 125, Win);
    TextBox ReceiveBox("", {160, 15}, 500, 250, Win, true);
    TextBox SendBox("", {160, 275}, 500, 100, Win, true);
    Button SendButton("Send", {565, 382}, 95, 22, Win);
    ListBox ContactsBox({675, 15}, 150, 395, Win);

    ReceiveBox.ShowScrollBar(true);
    ReceiveBox.SetReadOnly(true);

    HideCaret(ReceiveBox.GetHandle());
    SendButton.SetEvent(WM_LBUTTONDOWN, [](UINT, WPARAM, LPARAM) { 
      std::cout << "Send Button Clicked\n";
    });

    MenuBar Mnb(Win);
    Mnb.AddMenuItem("File", MF_POPUP);
    Mnb.AddMenuItem("Edit", MF_POPUP);
    Mnb.AddMenuItem("View", MF_POPUP);
    Mnb.AddMenuItem("Settings", MF_POPUP);
    Mnb.AddMenuItem("Help", MF_POPUP);

    HMENU FileMenu = Mnb.FindMenuItem("File");
    Mnb.CreateSubMenu(FileMenu, "Status", MF_POPUP);
    Mnb.AppendMenuItem(FileMenu, "Separator", MF_SEPARATOR);
    Mnb.AppendMenuItem(FileMenu, "Invite Contact", MF_STRING);
    Mnb.AppendMenuItem(FileMenu, "Close", MF_STRING);

    HMENU StatusMenu = Mnb.FindMenuItem(FileMenu, "Status");
    Mnb.AppendMenuItem(StatusMenu, "Online", MF_STRING);
    Mnb.AppendMenuItem(StatusMenu, "Away", MF_STRING);
    Mnb.AppendMenuItem(StatusMenu, "Busy", MF_STRING);
    Mnb.AppendMenuItem(StatusMenu, "Invisible", MF_STRING);

    HMENU EditMenu = Mnb.FindMenuItem("Edit");
    Mnb.AppendMenuItem(EditMenu, "Undo", MF_STRING);
    Mnb.AppendMenuItem(EditMenu, "Redo", MF_STRING);
    Mnb.AppendMenuItem(EditMenu, "Separator", MF_SEPARATOR);
    Mnb.AppendMenuItem(EditMenu, "Cut", MF_STRING);
    Mnb.AppendMenuItem(EditMenu, "Copy", MF_STRING);
    Mnb.AppendMenuItem(EditMenu, "Paste", MF_STRING);
    Mnb.AppendMenuItem(EditMenu, "Paste Special", MF_STRING);

    HMENU ViewMenu = Mnb.FindMenuItem("View");
    Mnb.CreateSubMenu(ViewMenu, "Display", MF_POPUP);
    Mnb.AppendMenuItem(ViewMenu, "Show Contacts", MF_STRING | MF_CHECKED);
    Mnb.AppendMenuItem(ViewMenu, "Status Bar", MF_STRING | MF_CHECKED);
    Mnb.AppendMenuItem(Mnb.FindMenuItem(ViewMenu, "Display"), "Display Picture", MF_STRING | MF_CHECKED);
    Mnb.AppendMenuItem(Mnb.FindMenuItem(ViewMenu, "Display"), "Contact Picture", MF_STRING | MF_CHECKED);

    HMENU SettingsMenu = Mnb.FindMenuItem("Settings");
    Mnb.AppendMenuItem(SettingsMenu, "Change Picture", MF_STRING);

    Win.Show();

    return Win.MessageLoop();
}

Where the point is that you create a Window, and then create a bunch of components that you put in it, and then "run" it. The user should not have to be concerned about how the messages are handled and how any of that is happening. This is much more "user-friendly" is it not?

Again, there's no way for me to edit or delete articles so I'm not sure how to do that.
Can you delete my posts for me?

No, we do not delete posts per request, unless they are in clear violation of the rules, which it is not in this case. There is a minimum age of 13 years old, which is when people are legally responsible for what they say on the internet or what "trail" they leave. It's not our responsibility if people have "regrets".

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.