|
hi,
that code does not change the DrawItem-function. just added some code to the OnPaint-handler ;o)
first you have to remove the owner-draw style from the header-ctrl in the SetSortArrow-function:
<br />
<br />
(just comented it out)
than ad an OnPaint-Handler in your SortHeaderCtrl.x
SortHeaderCtrl.h:
<br />
afx_msg void OnPaint();<br />
<br />
DECLARE_MESSAGE_MAP()<br />
SortHeaderCtrl.cpp:
<br />
void CSortHeaderCtrl::OnPaint() {<br />
CHeaderCtrl::OnPaint();<br />
<br />
if(m_iSortColumn < 0)<br />
return;<br />
<br />
CRect pRect;<br />
<br />
TCHAR szText[ 256 ];<br />
HD_ITEM hditem;<br />
<br />
hditem.mask = HDI_TEXT | HDI_FORMAT;<br />
hditem.pszText = szText;<br />
hditem.cchTextMax = 255;<br />
<br />
GetItem(m_iSortColumn, &hditem);<br />
<br />
CString ItemText = hditem.pszText;<br />
<br />
GetItemRect(m_iSortColumn, &pRect);<br />
<br />
CDC *pDC = GetDC();<br />
<br />
CPoint pPoints[3];<br />
<br />
CSize sz = pDC->GetTextExtent(ItemText);<br />
int x = pRect.left + sz.cx + 5;<br />
int y = (pRect.bottom / 2) - 3;<br />
<br />
pRect.right -= 2;<br />
CRgn pRgn;<br />
pRgn.CreateRectRgnIndirect(pRect);<br />
pDC->SelectClipRgn(&pRgn);<br />
<br />
if( m_bSortAscending )<br />
{<br />
pPoints[0] = CPoint(x + 5, y);<br />
pPoints[1] = CPoint(x + 10, y + 5);<br />
pPoints[2] = CPoint(x, y + 5);<br />
} else {<br />
pPoints[0] = CPoint(x, y);<br />
pPoints[1] = CPoint(x + 10, y);<br />
pPoints[2] = CPoint(x + 5, y + 5);<br />
}<br />
pDC->Polygon(pPoints, 3);<br />
}<br />
the important thing is of course to call CHeaderCtrl::OnPaint(); bevore you do your own painting stuff.
play around wit the Pen and Brush-colors for the polygon (the arrow) - i left this out, so the arrow is white and the border of it is black. looks good i think.
have fun!
vertex
|
|
|
|
|
It seems that you've forgotten to call ReleaseDC(pDC) at the end of OnPaint.
Anyway, thanks for your great code!
|
|
|
|
|
To insert a variable icon before each row, i must use the function CListCtrl::InsertItem(),just like this:
<br />
<br />
m_images.Create(IDB_CHECK_BOX,13,1,RGB(255,255,255));<br />
m_listctrl.SetImageList(&m_images,LVSIL_STATE);<br />
<br />
LVITEMA lvi;<br />
lvi.iSubItem=0;<br />
lvi.mask=LVIF_TEXT|LVIF_STATE;<br />
lvi.stateMask=LVIS_STATEIMAGEMASK;<br />
lvi.iItem=i;<br />
lvi.pszText="some words";<br />
lvi.state=INDEXTOSTATEIMAGEMASK(UNCHECKED_INDEX);<br />
m_listctrl.InsertItem(&lvi); <br />
then i use your function SetItemText(),but it generates a bug,so i add a member funtion AddItem2(LPCTSTR pszText, ... ) to match the function CListCtrl::InsertItem(),as follows:
<br />
int CSortListCtrl::AddItem2( LPCTSTR pszText, ... )<br />
{<br />
const int iIndex = GetItemCount() - 1;<br />
<br />
LPTSTR* arrpsz = new LPTSTR[ m_iNumColumns ];<br />
arrpsz[ 0 ] = new TCHAR[ lstrlen( pszText ) + 1 ];<br />
(void)lstrcpy( arrpsz[ 0 ], pszText );<br />
<br />
va_list list;<br />
va_start( list, pszText );<br />
<br />
for( int iColumn = 1; iColumn < m_iNumColumns; iColumn++ )<br />
{<br />
pszText = va_arg( list, LPCTSTR );<br />
ASSERT_VALID_STRING( pszText );<br />
VERIFY( CListCtrl::SetItem( iIndex, iColumn, LVIF_TEXT, pszText, 0, 0, 0, 0 ) );<br />
<br />
arrpsz[ iColumn ] = new TCHAR[ lstrlen( pszText ) + 1 ];<br />
(void)lstrcpy( arrpsz[ iColumn ], pszText );<br />
}<br />
<br />
va_end( list );<br />
<br />
VERIFY( SetTextArray( iIndex, arrpsz ) );<br />
<br />
return iIndex;<br />
}<br />
In addition, your code can not sort float, so i add two member function , as follows:
<br />
bool IsFloat( LPCTSTR pszText )<br />
{<br />
ASSERT_VALID_STRING( pszText );<br />
<br />
for( int i = 0; i < lstrlen( pszText ); i++ )<br />
if( !_istdigit( pszText[ i ] ) && pszText[i] != '.')<br />
return false;<br />
<br />
return true;<br />
}<br />
<br />
<br />
int FloatCompare( LPCTSTR pszNumber1, LPCTSTR pszNumber2 )<br />
{<br />
ASSERT_VALID_STRING( pszNumber1 );<br />
ASSERT_VALID_STRING( pszNumber2 );<br />
<br />
const float iNumber1 = atof( pszNumber1 );<br />
const float iNumber2 = atof( pszNumber2 );<br />
<br />
if( iNumber1 < iNumber2 )<br />
return -1;<br />
<br />
if( iNumber1 > iNumber2 )<br />
return 1;<br />
<br />
return 0;<br />
}<br />
<br />
i just make a little modification based on your codes.
your codes is very good, thank you for sharing these codes.
sea_soul
|
|
|
|
|
while creating the control dynamically like this :
m_pList = new CSortListCtrl;
m_pList->Create(LVS_REPORT|WS_CHILD|WS_VISIBLE,Rect,this, 0x1000 );
it crashes while running the debug version. The Create() calls CSortListCtrl::PreSubclassWindow() and the problematic line is:
VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd() ) );
What can be done ? Why is that happenning?
|
|
|
|
|
I've got the same result in creating the control dynamically.
The GetHeaderCtrl() call in PreSubclassWindow fails because it returns NULL pointer.
So, I subclassed the CHeaderCtrl in the parent window.
I think that would work.
....
m_pListCtrl = new CSortListCtrl;
m_pListCtrl->Create(LVS_REPORT|WS_CHILD|WS_VISIBLE|LVS_SHOWSELALWAYS|LVS_EDITLABELS,
CRect(0, 0, 0, 0), this, 0);
VERIFY( m_pListCtrl->m_ctlHeader.SubclassWindow( m_pListCtrl->GetHeaderCtrl()->GetSafeHwnd() ) );
....
The code above part of the create function in the parent window.
|
|
|
|
|
Thanks a lot, works fine, also in views
MS
|
|
|
|
|
It cann't work in view of SDI, how did you implement?
Benny fun
|
|
|
|
|
In the original code the subclassing of the header control class is done in the function PreSubclassWindow of the list control class:
void CSortListCtrl::PreSubclassWindow()
{
// the list control must have the report style.
ASSERT( GetStyle() & LVS_REPORT );
CListCtrl::PreSubclassWindow();
//subclassing code
VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd()));
}
When working with a view instead of a dialog you have to do the subclassing of the header control in the OnInitialUpdate function of your view class. In other words: move the line
VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd()));
into the OnInitialUpdate handler of your view class after creating the CSortListCtrl:
//my view class
void CWiz_ESView::OnInitialUpdate()
{
CView::OnInitialUpdate();
RECT rect;
GetClientRect(&rect);
//m_listCtrl is the CSortListCtrl member of my view class
m_listCtrl.Create(LVS_REPORT|WS_CHILD|WS_VISIBLE|LVS_SHOWSELALWAYS|LVS_EDITLABELS, rect, this, 0);
VERIFY( m_listCtrl.m_ctlHeader.SubclassWindow(m_listCtrl.GetHeaderCtrl()->GetSafeHwnd() ) );
//adding the columns to the list
m_listCtrl.SetHeadings("#,50;Hierarchy,100;Plan,100;Baustein,100;Bausteinkommentar,120;Anschluss,100;\
Anschlusskommentar,120;Messtellenanschluss,5;Wert,100;Signal,100;Einheit,50;\
Datentyp,100;I/O,50;Bausteintyp,100;Messtellentyp,5;Instanz DB,50");
m_listCtrl.LoadColumnInfo();
...
...
}
Hope this helps
MS
|
|
|
|
|
It cann't work completely.
Benny fun
|
|
|
|
|
|
It works fine now,
thanks you all.
|
|
|
|
|
Hi...
The control is nice, but there are a few bugs when using it in a view:
1) The control does not show scrollbars (vertical and horizontal)
2) The header control is not correctly updated when the user resizes the view window (ie, in a SDI view).
Thanks.
MaxPayne.
|
|
|
|
|
Hi,
In View, I am unable to successfully create an instance of CSortListCtrl in View.
The statement CSortListCtrl m_ListCtrl; returns a NULL for m_ListCtrl.
-Pete
Pete
|
|
|
|
|
Although this does work fine, I didn't want the container to know about the HeaderCtrl inside the ListCtrl, so what I did was:
a) safegaurd the PreSubclassWindow call with a check to make sure that the GetHeaderCtrl() returned a valid pointer (althought this may not be needed at all any more).
b) override both the create and createex in the listctrl class and add the subclassing there.
So the three functions should look like:
void FCListCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
CListCtrl::PreSubclassWindow();
if(GetHeaderCtrl()) // Works for placed controls (non-dynamic creation)
VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd() ) );
}
BOOL FCListCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
// TODO: Add your specialized code here and/or call the base class
BOOL bReturn = CListCtrl::Create(dwStyle, rect, pParentWnd, nID);
VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd() ) );
return bReturn;
}
BOOL FCListCtrl::CreateEx(DWORD dwExStyle, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
// TODO: Add your specialized code here and/or call the base class
BOOL bReturn = CListCtrl::CreateEx(dwExStyle, dwStyle, rect, pParentWnd, nID);
VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd() ) );
return bReturn;
}
Now anyone else can use the list control in any manner they like, and all they have to do is include the 4 files (two headerctrl and two listctrl files).
Works for me anyhow, hopefuly you'll find it usefull.
Thanks very much for the submission, I was just looking for the header control, this saved me time! The header ctrl is used as is but I noticed the web site listed in the header no longer exists, let me know if you want this updated.
Regards,
Adam
Adam Bialowas
Systems Engineer
Fugro Chance Inc.
|
|
|
|
|
If you create listctrl within WS_VISIBLE, the headerctrl haven't create.
so, you must notice it.
// the safe create
BOOL CListCtrlForFlatTab::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
bool show = dwStyle&WS_VISIBLE;
BOOL ret = __super::Create(dwStyle|WS_VISIBLE, rect, pParentWnd, nID);
subclass_header();
if (!show)
ShowWindow(SW_HIDE);
return ret;
}
|
|
|
|
|
I find it work well,thanks.
When I click on the header, the sort button will display, but the text on the header will move.
How can I solve the problem.
|
|
|
|
|
inline bool IsNumber( LPCTSTR pszText )
{
ASSERT_VALID_STRING( pszText );
register int iBegin, length = lstrlen( pszText );
if ( length && pszText[0] == '-' ) iBegin = 1; else iBegin = 0;
for( register i = iBegin; i < length; i++ )
if( !_istdigit( pszText[ i ] ) && (pszText[ i ] != _T( '.' )) )
return false;
return true;
}
|
|
|
|
|
Hi,
The code has sort routines for sorting numbers, text, and date, but when I use it with IP addresses, it doesn't seem to work. For example, if I have the following IP addresses in the list:
10.10.10.1
10.10.10.2
10.10.10.3
and I enter a new address value, for example: 10.10.10.10, I get the following result:
10.10.10.1
10.10.10.10 <---new value
10.10.10.2
10.10.10.3
The current sorting routine for sorting the date is close to what I would like to do, but the difference is that, the number of digits in the 4 octets of the IP address is not of a fixed length like the date (99/99/9999), because the user can enter different values like: 1.1.1.1 or 192.254.100.244, which would change the position of the indices if we are to implement a similar routine.
How can I implement this? Any help is much appreciated.
CS
|
|
|
|
|
Ok here's code to make it sort IP Addresses. Note: this will make the code depend on the winsock libraries for inet_addr, you could re-implement inet_addr yourself if you wanted to avoid this dependencies. To the code...
You need to add these 2 functions to SortListCtrl.cpp:
bool IsIPAddr(LPCSTR pszText)
{
ASSERT_VALID_STRING( pszText );
if (inet_addr(pszText) != INADDR_NONE)
return true;
else
return false;
}
int IPAddrCompare(LPCTSTR pszIP1, LPCTSTR pszIP2)
{
ASSERT_VALID_STRING( pszIP1 );
ASSERT_VALID_STRING( pszIP2 );
long lIP1 = inet_addr(pszIP1);
long lIP2 = inet_addr(pszIP2);
if( lIP1 < lIP2 )
return -1;
if( lIP1 > lIP2 )
return 1;
return 0;
}
then you need to change the CSortListCtrl::CompareFunction function comparison part to:
if( IsNumber( pszText1 ) )
return pListCtrl->m_bSortAscending ? NumberCompare( pszText1, pszText2 ) : NumberCompare( pszText2, pszText1 );
else if (IsIPAddr(pszText1))
return pListCtrl->m_bSortAscending ? IPAddrCompare(pszText1, pszText2) : IPAddrCompare(pszText2, pszText1);
else if( IsDate( pszText1 ) )
return pListCtrl->m_bSortAscending ? DateCompare( pszText1, pszText2 ) : DateCompare( pszText2, pszText1 );
else
// text.
return pListCtrl->m_bSortAscending ? lstrcmp( pszText1, pszText2 ) : lstrcmp( pszText2, pszText1 );
|
|
|
|
|
Hi VS.NOT,
Here's a small modification to help if you have a UNICODE project.
Jeff
INT SortIPAddress( LPCTSTR pszOne, LPCTSTR pszTwo, INT nOrder )
{
INT nReturn = 0;
ULONG lIP1, lIP2;
#ifdef UNICODE
{
USES_CONVERSION;
lIP1 = inet_addr( T2A( pszOne ) );
lIP2 = inet_addr( T2A( pszTwo ) );
}
#else
{
lIP1 = inet_addr( pszOne );
lIP2 = inet_addr( pszTwo );
}
#endif
if( lIP1 < lIP2 ) {
nReturn = -1;
} else if ( lIP1 > lIP2 ) {
nReturn = 1;
} else {
nReturn = 0;
}
return nOrder == SORT_ASCENDING ? nReturn : nReturn * -1;
}
|
|
|
|
|
I found that the above sort does not work as expected! It sorts on the 4th octet, then the 1st and 2nd - disregarding the 3rd octet.
Here's something I used to work around (I did not feel like Googling for a solution). It assumes a well formed IP address of the form a.b.c.d:
INT SortIPAddress( LPCTSTR pszOne, LPCTSTR pszTwo, INT nOrder )
{
const INT NOT_FOUND = -1;
INT nReturn = 0, nPos, nShift;
CString szIP1 = pszOne, szIP2 = pszTwo;
ULONG lIP1 = 0, lIP2 = 0;
nShift = 24;
while( NOT_FOUND != ( nPos = szIP1.Find( _T(".") ) ) ) {
lIP1 += _ttoi( szIP1.Left( nPos ) ) << nShift;
szIP1 = szIP1.Right( szIP1.GetLength() - nPos - 1 );
nShift -= 8;
}
lIP1 += _ttoi( szIP1 );
nShift = 24;
while( NOT_FOUND != ( nPos = szIP2.Find( _T(".") ) ) ) {
lIP2 += _ttoi( szIP2.Left( nPos ) ) << nShift;
szIP2 = szIP2.Right( szIP2.GetLength() - nPos - 1 );
nShift -= 8;
}
lIP2 += _ttoi( szIP2 );
nReturn = lIP1 - lIP2;
return nOrder == SORT_ASCENDING ? nReturn : nReturn * -1;
}
|
|
|
|
|
if pszIP1 is host byte order, should to change to:
long lIP1 = htonl(inet_addr(pszIP1));
long lIP2 = htonl(inet_addr(pszIP2));
|
|
|
|
|
beautiful..
|
|
|
|
|
Hello,
How about sorting, when style set to Icon. Is it supported by standart CListCtrl? Or there will be no other way, but to implement it's handling myself?
Any ideas?
Thank you.
Rimantas
|
|
|
|
|
I use the sort method of CListCtrl,but the following code
is not work,Why?
static int CALLBACK MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
CString strItem1 = pListCtrl->GetItemText(lParam1, 0);
CString strItem2 = pListCtrl->GetItemText(lParam2, 0);
return strcmp(strItem2, strItem1);
}
void SortItems()
{
extern CListCtrl* pmyListCtrl;
pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
}
Thanks in advance!
Best Regards!
|
|
|
|
|