0
votes

So I've spent a whole load of time trying to render some text. I finally managed to get somewhere by subclassing the ListView and adding a WndProc override as follows:

protected override void WndProc(ref Message m) { base.WndProc( ref m );

            //NM_CUSTOMDRAW =-12
            switch( m.Msg )
            {
                case 0x004e://WM_NOTIFY:
                case 0x204e://WM_REFLECT_NOTIFY
                    {
                        NMHDR nmhdr     = (NMHDR)System.Runtime.InteropServices.Marshal.PtrToStructure( m.LParam, typeof( NMHDR ) );
                        if ( nmhdr.code == -12 )   //NM_CUSTOMDRAW
                        {
                            NMLVCUSTOMDRAW nmlvcd     = (NMLVCUSTOMDRAW)System.Runtime.InteropServices.Marshal.PtrToStructure( m.LParam, typeof( NMLVCUSTOMDRAW ) );
                            System.Diagnostics.Trace.WriteLine( nmlvcd.nmcd.dwDrawStage.ToString() );
                            if      ( nmlvcd.nmcd.dwDrawStage == 1 ) //CDDS_PREPAINT
                            {
                                int result  = (int)m.Result;
                                result      |= 0x10;//CDRF_NOTIFYPOSTPAINT;
                                m.Result    = (IntPtr)result;
                            }
                            else if ( nmlvcd.nmcd.dwDrawStage == 2 ) //CDDS_POSTPAINT
                            {
                                Graphics g  = Graphics.FromHdc( nmlvcd.nmcd.hdc );
                                if ( DrawFloatingItem != null )
                                { 
                                    PaintEventArgs pe   = new PaintEventArgs( g, nmlvcd.nmcd.rc );
                                    DrawFloatingItem( this, pe );
                                }
                            } 
                            else if ( nmlvcd.nmcd.dwDrawStage == 65537 )    //CDDS_ITEMPREPAINT 
                            {
                                int result  = (int)m.Result;
                                result      |= 0x10;//CDRF_NOTIFYPOSTPAINT;
                                m.Result    = (IntPtr)result;
                            }
                            else if ( nmlvcd.nmcd.dwDrawStage == 65538 )    //CDDS_ITEMPOSTPAINT 
                            {

                            }

                        }
                    }
                    break;
            }
        }

With this I've successfully managed to render over the list view. However the moment I do render (From the DrawFloatingItem event) then all the items shift up by the header's height (ie the first item is rendererd under the column header).

This is the list before:

enter image description here

And this is the list afterwards:

enter image description here

Has anyone got any idea what I am doing wrong here? If I comment out my draw commands (inside the "DrawFloatingItem" function) then everything works as expected. However the moment I do any drawing then the rendering goes wrong as above.

Any help would be massively appreciated!

1
In case your unaware you can often accomplish the same end without directly dipping into the underlying API: msdn.microsoft.com/en-us/library/… - Alex K.
@AlexK: OwnerDraw does not allow you to handle PostPaint. And yes .. I have already set OwnerDraw to true ... because if I didn't the NM_CUSTOMDRAW wouldn't be called in the first place. - Goz

1 Answers

0
votes

Its typical I always figure it out shortly after posting up a question. My mistake was that I wasn't disposing of the Graphics object I was creating before caling my DrawFloatingItem event.

Changing to the following fixed the problem:

 using( Graphics g  = Graphics.FromHdc( nmlvcd.nmcd.hdc ) )
 { 
     if ( DrawFloatingItem != null )
     { 
         PaintEventArgs pe   = new PaintEventArgs( g, nmlvcd.nmcd.rc );
         DrawFloatingItem( this, pe );
     }
 }