Prior to version 19.2 it was impossible to apply control's theme to scroll bars in ToolkitPro controls such as CXTPEdit, CXTPTree, CXTPListBox, CXTPListCtrl and CXTPPropertyGrid due to the way the underlying common Windows controls were showing and handling their own scroll bars. Overriding that behavior for the Windows common controls and maintaining full backward compatibility was quite challenging technically and led to solution that requires certain consideration for being able to use. This article describes the key points and tries to answer most common questions that may arise.
In fact the following classes remained unchanged and still use standard Windows scroll bars without theme applied:
CXTPEdit
CXTPTreeCtrl
CXTPListBox
CXTPListCtrl
In order to be able to apply scrollbar theme or customize scroll bar class one has to use or derive new controls classes derived from a new special adaptor template class called CXTPScrollable<Base>
.
For the most common use cases it will be sufficient to replace controls class names used in your application with their corresponding class names that are derived from CXTPScrollable
:
CXTPScrollableEdit
CXTPScrollableTreeCtrl
CXTPScrollableListBox
CXTPScrollableListCtrl
CXTPPropertyGrid
is a special case, it does not have another CXTPScrollable
derived version, it simply uses new approach internally, remains fully backward compatible and does not require any special considerations unless it is confirmed that new additions cause a blocking issue. If this is the case starting from version 19.3 it is possible to disable PropertyGrid scroll bar theme support making it work
If this is the case starting from version 19.3 there is an option to fallback to pre-19.2 behavior at the price of disabling scroll bar themes. For this either uncomment the XTP_PROPERTY_GRID_DISABLE_SCROLLBAR_THEMES
macro at the beginning of XTPPropertyGrid.h file (C:\Program Files (x86)\Codejock Software\MFC\Xtreme ToolkitPro v19.3.0\Source\PropertyGrid\XTPPropertyGrid.h) or specify that macro as defined in the C++ compiler properties of the ToolkitPro or the PropertyGrid library project for selected configurations then re-build the ToolkitPro or the PropertyGrid projects in order the change to take an effect.
Using those classes will ensure that controls will have proper scroll bar theme set automatically and make it possible to set a custom scroll bar theme using new method:
void SetScrollBarTheme(XTPScrollBarTheme nTheme);
Using classes derived from CXTPScrollable also impose certain restrictions:
Create
or CreateEx
method exposed by their MFC base class, one should call only CWnd::Create
or CWnd::CreateEx
overrides as due to lack of C++ support in old versions of Microsoft C++ compiler calling overloaded Create
or CreateEx
method from MFC CTreeCtrl
, CEdit
, CListBox
and CListCtrl
will lead to compilation errors when using those compilers.
CXTPTreeCtrl m_tree;
// As CXTPTreeCtrl is derived from CTreeCtrl it uses CTreeCtrl::Create overloaded
// method which has signature different from CWnd::Create and thus should not be used for CXTPScrollableTreeCtrl
m_tree.Create(WS_CHILD | TVS_LINESATROOT, rc, this, IDC_TREE);
Example of the fixed code:
CXTPScrollableTreeCtrl m_tree;
// Call CWnd::Create overridden method to ensure it can be compiled using all Microsoft C++ compilers
m_tree.Create(_T("SysTreeView32"), NULL, WS_CHILD | TVS_LINESATROOT, rc, this, IDC_TREE);
CXTPScrollable
as they most likely will be invalidated.CXTPScrollable
will be destroyed and re-created upon initial creation. This process is done absolutely transparent for regular use cases, but if the handle is cached by the client code early it may be soon invalidated, such places include but not limited to:
CDialog::OnInitDialog
callCXTPScrollable
try to make control's initialization logic as simple and lightweight as possible as subsequent control re-creation may result in slowing down application startup if CPU and memory strain during control re-creation doubles.CXTPScrollable
must be default constructible, i.e. expose a public constructor with no arguments or all arguments with their default values provided.CXTPScrollable
it is necessary to call RedrawWindow
with RDW_ALLCHILDREN
flag set as calling UpdateWindow
will not have any effect.CXTPScrollable
needs to access its parent window after creation it has to call GetParent
twice as it will be placed on an intermediate host control.CXTPScrollable
with Resizer control requires using CXTPResize::SetResize
method that takes control's window handle in addition to control's ID value, for example:
CXTPEdit m_edtSingleLine;
CXTPScrollableEdit m_edtMultiline;
// IDC_EDIT_SINGLELINE is NOT derived from CXTPScrollable and thus can be referenced by Resize control by ID only.
SetResize(IDC_EDIT_SINGLELINE, XTP_ANCHOR_TOPLEFT, CXTPResizePoint(1.f / 3.f, 0));
// IDC_EDIT_MULTILINE is derived from CXTPScrollable and thus must be referenced by Resize control by both ID and handle value.
SetResize(IDC_EDIT_MULTILINE, m_edtMultiline, XTP_ANCHOR_TOPLEFT, CXTPResizePoint(1.f / 3.f, 0));
It is possible to use a custom control with CXTPScrollable
in order to be able to apply to its scroll bars provided that the control is derived from supported classes and uses standard Windows scroll bars.
There are two possible use cases:
CEdit
, CListBox
, CTreeCtrl
or CListCtrl
CWnd
For the first case ToolkitPro provides adapter templates for the corresponding base classes:
CXTPScrollableEditT<Base>
CXTPScrollableListBoxT<Base>
CXTPScrollableTreeCtrlT<Base>
CXTPScrollableListCtrlT<Base>
Example:
// Your existing classes
class CCustomEdit : public CEdit { /*...*/ };
class CCustomTreeCtrl : public CXTPTreeCtrl { /*...*/ };
// Your new derived classes
class CScrollableCustomEdit : public CXTPScrollableEditT<CCustomEdit> { /*...*/ };
class CScrollableTreeCtrl : public CXTPScrollableEditT<CCustomTreeCtrl> { /*...*/ };
In other cases when you need to derive another kind of custom control you'll need to implement IXTPScrollable
interface:
// Your existing class
class CCustomControl : public CWnd
{
public:
void InitializeCustomState();
// ...
};
// Your new derived class.
// Some or all method may have default implementation, the example demonstrates overloading of all methods.
class CScrollableCustomControl : public CXTPScrollable
{
public:
// IXTPScrollable overrides
virtual BOOL HasVScroll(DWORD dwStyle, DWORD dwExStyle) const
{
// Determine if control has vertical scroll.
return 0 != (GetStyle() & WS_VSCROLL);
}
virtual BOOL HasHScroll(DWORD dwStyle, DWORD dwExStyle) const
{
// Determine if control has horizontal scroll.
return 0 != (GetStyle() & WS_HSCROLL);
}
virtual BOOL HasLeftScrollbar(DWORD dwStyle, DWORD dwExStyle) const
{
// Determine if control has scroll bar on the lets.
return 0 != (GetExStyle() & WS_EX_LEFTSCROLLBAR);
}
virtual void DisableScrollbars()
{
// Force default scroll bars to hide.
DisableScrollbars(*this);
}
virtual void DisableScrollbars(CWnd& wnd)
{
// Force default scroll bars to hide for a specific window.
wnd.ModifyStyle(WS_VSCROLL | WS_HSCROLL, 0);
}
virtual CScrollBar* CreateBar() const
{
// Create scroll bar instance.
return new CXTPScrollBarCtrl();
}
virtual CWnd* CreateControl() const
{
// Re-create custom control instance and perform default initialization if necessary.
CCustomControl* pNewCtrl = new CCustomControl();
VERIFY(NULL != pNewCtrl);
pNewCtrl->InitializeCustomState();
return pNewCtrl;
}
virtual DWORD FilterStyle(DWORD dwStyle) const
{
return dwStyle;
}
virtual DWORD FilterExStyle(DWORD dwExStyle) const
{
return dwExStyle;
}
virtual BOOL RequiresMouseWheelOverriding() const
{
return true;
}
// ...
};
User Comments
No comments yet, sign in to comment.