Changing default sort arrows of CMFCListCtrl - c++

I found a problem. I can't change default sort arrows in header of CMFCListCtrl.
I found post on msdn about CMFCHeaderCtrl::OnDrawSortArrow but it didn't help because no examples there.
I tried simple method how I set arrows to CListCtrl header through the CimageList and HDITEM but those arrow sets only to left side because right side has already arrow default.
Google is empty on solutions how change default arrows on CMFCListCTrl.
Please help me)
Thanks!
PS. Please note that this is CMFCListCtrl not a CListCtrl where I can add arrows very easly.

Because CMFCHeaderCtrl is a member inside CMFCListCtrl you can not overwrite it.
Try to derive yor own CMFCListCtrl class, with your own CMFCHeaderCtrl class, that overwrites OnDrawSortHeader OnDrawSortArrow.
Overwrite CMFCListCtrl::InitHeader and subclass to your header control class.
If you start form scratch with a CListCtrl you can directly subclass the header control. The complete stuff inside CMFCListCtrl isn't so complicated and easy to reimplement. Depends what functionality you need.

The CMFCHeaderCtrl calls the currently active Visual Manager to do the actual drawing of sort arrows. It is easy to implement a customized Visual manager which overrides the arrow drawing method in the base class.
class CMyVisualManager:public CMFCVisualManagerOffice2007
{
virtual void OnDrawHeaderCtrlSortArrow(CMFCHeaderCtrl* pCtrl, CDC* pDC, CRect& rect, BOOL bIsUp);
};
void CMyVisualManager::OnDrawHeaderCtrlSortArrow(CMFCHeaderCtrl* pCtrl, CDC* pDC, CRect& rectArrow, BOOL bIsUp)
{
BOOL bDlgCtrl = pCtrl->IsDialogControl();
CPen penDark(PS_SOLID, 1, bDlgCtrl ? afxGlobalData.clrBtnDkShadow : afxGlobalData.clrBarDkShadow);
CPen* pPenOld = pDC->SelectObject(&penDark);;
ASSERT_VALID(pPenOld);
if (!bIsUp)
{
pDC->MoveTo(rectArrow.CenterPoint().x, rectArrow.bottom);
pDC->LineTo(rectArrow.CenterPoint().x, rectArrow.top);
pDC->MoveTo(rectArrow.CenterPoint().x-2, rectArrow.top+4);
pDC->LineTo(rectArrow.CenterPoint().x+3, rectArrow.top+4);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.top+3);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.top+3);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.top+2);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.top+2);
}
else
{
pDC->MoveTo(rectArrow.CenterPoint().x, rectArrow.top);
pDC->LineTo(rectArrow.CenterPoint().x, rectArrow.bottom);
pDC->MoveTo(rectArrow.CenterPoint().x-2, rectArrow.bottom-4);
pDC->LineTo(rectArrow.CenterPoint().x+3, rectArrow.bottom-4);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.bottom-3);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.bottom-3);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.bottom-2);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.bottom-2);
}
pDC->SelectObject(pPenOld);
}

Related

Changing text color of ribbon button

I'm having an issue in VS2013-15 where my buttons' font colour is a shade of grey rather than black.
Here is what the buttons look like currently:
Some of the text seems to change to the desired colour when hovered over. However, some don't changed even when hovered:
Any help would be greatly appreciated. Thanks.
I managed to fix it using Barmak's answer. The Windows 7 theme is the problem.
To fix, you have 2 options.
Option 1
Change the theme your program uses
This is a lazy work around. Just switch your visual manager from CMFCVisualManagerWindows7 to one of the other visual managers.
Option 2
Override the OnUpdateSystemColors() function
First off you'll need a new class that inherits CMFCVisualManagerWindows7:
class Win7VM : public CMFCVisualManagerWindows7
{
};
Then you'll need to call DECLARE_DYNCREATE as the object is created dynamically at runtime.
class Win7VM : public CMFCVisualManagerWindows7
{
DECLARE_DYNCREATE(Win7VM);
};
Next, you'll need to implement the class.
class Win7VM : public CMFCVisualManagerWindows7
{
DECLARE_DYNCREATE(Win7VM)
virtual void OnUpdateSystemColors()
{
CMFCVisualManagerWindows7::OnUpdateSystemColors();
m_clrRibbonPanelCaptionText = RGB(0, 0, 0);
}
};
After that, you need to call IMPLEMENT_DYNCREATE separate to your class implementation, in global scope.
IMPLEMENT_DYNCREATE(Win7VM, CMFCVisualManagerWindows7);
Finally, we need to change our usage of CMFCVisualManagerWindows7 to that of our new class:
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(Win7VM));

MFC CMFCVisualManager Override

I'm trying to override some functions on CMFCVisualManager to customize my ribbon. So I created a class and derived from it.
void CMyVisualManager::OnDrawRibbonCategory(CDC* pDC, CMFCRibbonCategory* pCategory, CRect rectCategory)
Now this works and can change colors etc, but there are some functions that I cant or override or not doing it right like
void CMyVisualManager::OnDrawRibbonLaunchButton(CDC* pDC, CMFCRibbonLaunchButton* pButton, CMFCRibbonPanel* pPanel)
My method doesnt override the original, and the original function gets called
But https://msdn.microsoft.com/en-us/subscriptions/downloads/65a24718-8128-43f9-973d-25262bdceae7(v=vs.90)
says it can be overridden.
If anyone can point me in the right direction, I've been looking but can't find an answer thanks
Yes you override a function in your class.
But you visual manager isn't the one that is created. So it is never used.
When your program starts an instance of the visual manager is created. And this instance is used.
Only if you also force the MFC to use your visual manager your overriden functions are used.
There for use SetDefaultManager with the runtime class of your class in InitInstance of your program.

MFC: How to custom draw a dynamically created CListCtrl

I need to customize the head/row height of a CListCtrl. After some googling, I know that I need to subclass CListCtrl, so I wrote my own list class, with the outline as follows:
class CListCtrlCl : public CListCtrl
{
DECLARE_DYNAMIC(CListCtrlCl)
public:
...
BEGIN_MESSAGE_MAP(CListCtrlCl, CListCtrl)
ON_WM_MEASUREITEM()
ON_WM_MEASUREITEM_REFLECT()
END_MESSAGE_MAP()
void CListCtrlCl::PreSubclassWindow()
{
ModifyStyle(0,LVS_OWNERDRAWFIXED);
CListCtrl::PreSubclassWindow();
CHeaderCtrl *pHeader = GetHeaderCtrl();
m_Header.SubclassWindow(pHeader->GetSafeHwnd());
}
void CListCtrlCl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if (m_nRowHeight>0)
{
lpMeasureItemStruct->itemHeight = 100;
}
}
The problem is that this method worked if I drag a CListCtrl control in dialog template, but if I create this listctrl dynamically, like:
BOOL CListCtrlTestDlg::OnInitDialog()
{
CRect rect(7,7,300,300);
this->m_ListCtrl.Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT|LVS_ALIGNLEFT|WS_BORDER | WS_TABSTOP, rect, this,IDC_LIST1 + 1);
SetWindowLong(m_ListCtrl.m_hWnd ,GWL_EXSTYLE,WS_EX_CLIENTEDGE);
m_ListCtrl.SetExtendedStyle(LVS_EX_GRIDLINES);
::SendMessage(m_ListCtrl.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
...
Then the customization code just doesn't take effect. Whatever I did, the result listctrl is the standard one without any customization. But I need to create this listctrl dynamically, so could anyone tell me what I need to do to make dynamically created clistctrl customizable?
Thanks.
You've left off the necessary style for custom drawing when you create the control. Add LVS_OWNERDRAWFIXED. That should fix your problem.
The reason is that, PreSubclassWindow is only called when you subclass a control. When you createthe control you have also control over the style.
Simply overwrite the virtual Create function and just add the style like you are doing it in the PreSubclassWindow function. Than call the base class. You can also overwrite PreCreateWindow.
But much simpler than using the ownerdraw feature is cusum draw.

How to set a CMFCPropertyListCtrl's column width?

I'm adding properties to an object of type CMFCPropertyGridCtrl like this:
myPropertyListCtrl.AddProperty(
new CMFCPropertyGridProperty(
_T("Name"),
foo.GetName())
);
The result is that only the second column is visible but not the first that should contain "Name".
I found CMFCPropertyGridCtrl::GetPropertyColumnWidth() but there appears to be no corresponding Set... method...
I looked at the NewControls sample, in which the column sizing appears to be fully automatic. However, I couldn't find the relevant difference to my code.
What am I missing?
m_nLeftColumnWidth responsible for holding the "Name" column's width is a protected member of the CMFCPropertyGridCtrl class. Create your own class, that derives from CMFCPropertyGridCtrl and you will be able to manipulate m_nLeftColumnWidth.
Note that m_nLeftColumnWidth is initially set to 0 in CMFCPropertyGridCtrl's ctor. One of the few other places that it is set, is in the OnSize() method (ref. AfxPropertyGridCtrl.cpp, line 2783 in VS2010), where it is set to half the width of the grid. This may not be ideal, nor the customized value described by overriding the class to explicitly set it, but may be good enough.
If so, then it is merely to trigger having the CMFCPropertyGridCtrl::OnSize() protected method. When used in a resizable window such as a CDockablePane, OnSize() will be called automatically. But in a CDialog, it needs to be trigger explicitly such as to send a WM_SIZE message:
CRect rect;
myPropGrid.GetWindowRect(&rect);
myPropGrid.PostMessage(WM_SIZE, 0, MAKELONG(rect.Width(),rect.Height()));
The reason the "set" isn't there is because it's left to the header control. The below is the method of handling through MFC versus posting window messages:
HDITEM hdItem;
hdItem.mask = HDI_WIDTH; // indicating cxy is width
hdItem.cxy = 300; // whatever you want the property name column width to be
PropListCtrl.GetHeaderCtrl().SetItem(0, &hdItem);
class CServerPropertyGridCtrl : public CMFCPropertyGridCtrl
{
public:
void SetLeftColumnWidth(int cx)
{
m_nLeftColumnWidth = cx;
AdjustLayout();
}
};

How do I drag and drop something into a Static control?

How would I drag and drop something into a static control? It looks like I need to create a sub class of COleDropTarget and include that as a member variable in a custom CStatic. That doesn't appear to be working though. When I try and drag something onto the Static control I get the drop denied cursor.
The static control's m_hWnd must be valid when you call COleDropTarget::Register, which is why it doesn't work from within your CMyStatic constructor. What you can do is override CWnd::PreSubclassWindow within your CMyStatic class:
class CMyStatic : public CStatic {
...
virtual void PreSubclassWindow();
};
void CMyStatic::PreSubclassWindow()
{
CStatic::PreSubclassWindow();
m_MyDropTarget.Register(this);
}
There's a really good article here on CodeProject that you may find helpful.
In addition to the PreSubClassWindow() addition, you also have to set your CStatic control to have the Notify flag set in its resource parameters. Otherwise the control won't let the app know about mouse movements and hence not trigger the OnDragEnter() method.