How to set a CMFCPropertyListCtrl's column width? - c++

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();
}
};

Related

gtkmm 4: How to get X window ID from inside widget?

In gtkmm 4, how can one get the X Window ID of type XID as defined in X11/X.h, from inside a class, that inherits from Gtk::Widget?
Not all of them have one.
Those widgets that do will implement the GtkNative interface, which provides the gtk_native_get_surface function, allowing you to obtain a GdkSurface. In gtkmm, this will correspond to casting to to Gtk::Native and calling get_surface.
To obtain a Window handle from that, you can use the GDK_SURFACE_XID macro. For that, I don’t think a C++ wrapper exists; you will have to call Gdk::Surface::gobj to obtain a GdkSurface * and use the C API.
I wanted to add two things to the accepted answer
It is of course important to check if get_surface() returned a valid nonzero object indeed. Otherwise get the ID after the Widget's signal_realize() is emitted, which is done after the widget is assigned to a surface. This can be achieved by overriding the default handler on_realize()
Instead of casting and calling ((Gtk::Native)this)->get_surface() it is also possible to call like get_native()->get_surface().
In conclusion do
void myWidget::on_realize() {
// Call default handler
Gtk::Widget::on_realize();
XID x_window = GDK_SURFACE_XID(get_native()->get_surface()->gobj());
}
to get the X window ID as early as possible.

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.

Changing default sort arrows of CMFCListCtrl

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);
}

How to implement delegate in QHeaderView

I have created one table by using QTableview and QAbstractTableModel . i have added some vertical header by using QHeaderView . In one of the header cell i want to use delegate ..
I am using the delegate but it does not have any impact ..
Is anywhere i am doing wrong ?
Had this issue myself. The answer from the Qt documentation is simple and annoying:
Note: Each header renders the data for each section itself, and does
not rely on a delegate. As a result, calling a header's
setItemDelegate() function will have no effect.
In other words you cannot use delegates with QHeaderView.
For the record, if you want to style a QHeaderView section, you'll have to do it either via the header data model (changing Qt::FontRole, etc.) or derive your own QHeaderView (don't forget to pass it to your table with "setVerticalHeader()") and overwrite the its paintSection()-function.
i.e.:
void YourCustomHeaderView::paintSection(QPainter* in_p_painter, const QRect& in_rect, int in_section) const
{
if (nullptr == in_p_painter)
return;
// Paint default sections
in_p_painter->save();
QHeaderView::paintSection(in_p_painter, in_rect, in_section);
in_p_painter->restore();
// Paint your custom section content OVER a specific, finished
// default section (identified by index in this case)
if (m_your_custom_section_index == in_section)
{
QPen pen = in_p_painter->pen();
pen.setWidthF(5.5);
pen.setColor(QColor(m_separator_color));
in_p_painter->setPen(pen);
in_p_painter->drawLine(in_rect.right(), in_rect.top(), in_rect.right(), in_rect.bottom());
}
}
This simplified example could of course easily be done via a stylesheet instead, but you could theoretically draw whatever you like using this method.

Binding a CPen to a list box

Does anyone know how to bind a CPen object to a listbox in VS2005 C++?
Can I do it as a ToString with some sort of conversion?
I am creating a custom list of different pens for the user to select.
Thanks.
COLORREF rgbRED = (255,0,0);
CPen penRed(PS_SOLID,3,rgbRED);
CDialog::OnInitDialog();
ShowWindow(SW_SHOW);
UpdateData();
lbLineWeight.InsertString(penRed);
2 options.
(simple) Use a normal CListBox with strings as the items, and keep the link between the string to the actual CPen as free functions (or member of some other classes) and you will have to do a one-to-one association between the current selected item (usually an index number) and the CPen information you have.
(a bit more complex) Derive your own class from CListBox and keep the CPen data internally, you will still have to keep a list of valid CPen in that new class, and do the one-to-one association between the selected item and the actual CPen; as a bonus you can make you derived CListBox owner-drawn and instead of using string, you could draw some sort of representation of each pen in the list items.
Another tought, you could add the CPen as a user data to each CListBox item (CListBox::SetItemData) to make the link between the item and the actual item a bit more easy.
Good luck.
Max.
Assuming I understand you correctly you want to have a CListBox which allows the user to select a CPen for use elsewhere.
I would probably make a little helper class:
struct PenHelper
{
CString m_displayName;
LOGPEN m_penProps;
bool CreatePen(CPen* pPen)
{
return pPen->CreatePenIndirect(&m_penProps) == 1;
}
};
The idea being you could have a container like std::map of multiple PenHelper each with a names like "Solid Red" and a corresponding LOGPEN struct with properties that match the name. In the CListBox you add the display name string. When they select one you can look it up by name and use the create function to actually make the corresponding CPen
Just one of a million ways to skin a cat.
Edit: Quick note. In order to handle ON_LBN_SELCHANGE in your message map for when they make a selection in your CListBox make sure you gave it the LBS_NOTIFY style in the Create call otherwise it won't fire.