Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Custom Draw in ListView.

Custom Draw in ListView.

Scheduled Pinned Locked Moved C#
tutorialcsharpcomgraphicshelp
9 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    David Fleming
    wrote on last edited by
    #1

    There's an excellent article here on CodeProject by Michael Dunn about intercepting NM_CUSTOMDRAW messages to modify the look of a ListView -- not as involved as OwnerDraw, though, which is nice. Here's the link: http://www.codeproject.com/listctrl/lvcustomdraw.asp[^] Unfortunately, the article is not written for C#. I was wondering how to hook into the message stream to intercept and act on NM_CUSTOMDRAW messages in C#. I asked at the article, but Michael does not know. I've seen the example code in the C# help on using OwnerDraw, but that of course requires handling almost all of the drawing chores. Can we, in C#, hook into the message stream and just do custom draw type stuff like described in Michael's excellent article? If so, how? What event handler do I need? Where do I declare it? Stuff like that. Or do I have to create a ListView derived class and override the WndProc? (which I'd rather not do if I can avoid it)

    J 1 Reply Last reply
    0
    • D David Fleming

      There's an excellent article here on CodeProject by Michael Dunn about intercepting NM_CUSTOMDRAW messages to modify the look of a ListView -- not as involved as OwnerDraw, though, which is nice. Here's the link: http://www.codeproject.com/listctrl/lvcustomdraw.asp[^] Unfortunately, the article is not written for C#. I was wondering how to hook into the message stream to intercept and act on NM_CUSTOMDRAW messages in C#. I asked at the article, but Michael does not know. I've seen the example code in the C# help on using OwnerDraw, but that of course requires handling almost all of the drawing chores. Can we, in C#, hook into the message stream and just do custom draw type stuff like described in Michael's excellent article? If so, how? What event handler do I need? Where do I declare it? Stuff like that. Or do I have to create a ListView derived class and override the WndProc? (which I'd rather not do if I can avoid it)

      J Offline
      J Offline
      J Dunlap
      wrote on last edited by
      #2

      Unfortunately, you do have to derive from the class and subclass the window. Subclass the ListView[^], and handle the WM_NOTIFY[^] message. When WM_NOTIFY is received, convert the memory that the lParam parameter points to to an NMHDR[^] instance, and check its code field. If it is set to NM_CUSTOMDRAW, convert the same memory to an NMLVCUSTOMDRAW[^] structure, and work with it as described in Mike's article.

      D 2 Replies Last reply
      0
      • J J Dunlap

        Unfortunately, you do have to derive from the class and subclass the window. Subclass the ListView[^], and handle the WM_NOTIFY[^] message. When WM_NOTIFY is received, convert the memory that the lParam parameter points to to an NMHDR[^] instance, and check its code field. If it is set to NM_CUSTOMDRAW, convert the same memory to an NMLVCUSTOMDRAW[^] structure, and work with it as described in Mike's article.

        D Offline
        D Offline
        David Fleming
        wrote on last edited by
        #3

        Wow! One of the best answers I've received here at CodeProject. Thanks a bunch for the links. I'll work through it and see what I can do. Thanks again.

        1 Reply Last reply
        0
        • J J Dunlap

          Unfortunately, you do have to derive from the class and subclass the window. Subclass the ListView[^], and handle the WM_NOTIFY[^] message. When WM_NOTIFY is received, convert the memory that the lParam parameter points to to an NMHDR[^] instance, and check its code field. If it is set to NM_CUSTOMDRAW, convert the same memory to an NMLVCUSTOMDRAW[^] structure, and work with it as described in Mike's article.

          D Offline
          D Offline
          David Fleming
          wrote on last edited by
          #4

          Hmm. I'm sure this is a totally rookie problem. I've done my fair share of casting in C++, but I'm pretty new to C#. How do I cast the lParam to my NMHDR struct in C#? In my class (which per the link you supplied is derived from NativeWindow), I have NMHDR defined:

              private struct NMHDR
              {
                  IntPtr hwndFrom;
                  IntPtr idFrom;
                  UInt32 code;
              }
          

          Then in the overridden WndProc, I have:

                  //Check to see if it is a WM\_NOTIFY msg
                  if (m.Msg == WM\_NOTIFY)
                  {
                      //Convert the lParam (within m) to NMHDR struct
                  }
          

          And since I am pretty new to C#, I don't know the syntax for this casting.

          D 1 Reply Last reply
          0
          • D David Fleming

            Hmm. I'm sure this is a totally rookie problem. I've done my fair share of casting in C++, but I'm pretty new to C#. How do I cast the lParam to my NMHDR struct in C#? In my class (which per the link you supplied is derived from NativeWindow), I have NMHDR defined:

                private struct NMHDR
                {
                    IntPtr hwndFrom;
                    IntPtr idFrom;
                    UInt32 code;
                }
            

            Then in the overridden WndProc, I have:

                    //Check to see if it is a WM\_NOTIFY msg
                    if (m.Msg == WM\_NOTIFY)
                    {
                        //Convert the lParam (within m) to NMHDR struct
                    }
            

            And since I am pretty new to C#, I don't know the syntax for this casting.

            D Offline
            D Offline
            David Fleming
            wrote on last edited by
            #5

            OK. I answered my own question. In case anyone else out there runs into this sort of problem, here's what I found (and it seems to be working):

                    //Check to see if it is a WM\_NOTIFY msg
                    if (m.Msg == WM\_NOTIFY)
                    {
                        NMHDR nmHdr = new NMHDR();
                        nmHdr = (NMHDR)m.GetLParam(typeof(NMHDR));
                        if (nmHdr.code == NM\_CUSTOMDRAW)
                        {
                            Debug.WriteLine("Got here.");
                        }
                    }
            

            But now I have a different question (there's always something): Where can I find all the info on the struct.s I need (including, but not necessarily limited to: NMHDR, NMCUSTOMDRAW and NMLVCUSTOMDRAW)? I can find them easily enough by searching MS KB, but is there a file somewhere that I can cut and paste from or just include in my code that already has all of this stuff defined (in C#)? Manually coding all of the structs is kind of a pain. Thanks.

            J 1 Reply Last reply
            0
            • D David Fleming

              OK. I answered my own question. In case anyone else out there runs into this sort of problem, here's what I found (and it seems to be working):

                      //Check to see if it is a WM\_NOTIFY msg
                      if (m.Msg == WM\_NOTIFY)
                      {
                          NMHDR nmHdr = new NMHDR();
                          nmHdr = (NMHDR)m.GetLParam(typeof(NMHDR));
                          if (nmHdr.code == NM\_CUSTOMDRAW)
                          {
                              Debug.WriteLine("Got here.");
                          }
                      }
              

              But now I have a different question (there's always something): Where can I find all the info on the struct.s I need (including, but not necessarily limited to: NMHDR, NMCUSTOMDRAW and NMLVCUSTOMDRAW)? I can find them easily enough by searching MS KB, but is there a file somewhere that I can cut and paste from or just include in my code that already has all of this stuff defined (in C#)? Manually coding all of the structs is kind of a pain. Thanks.

              J Offline
              J Offline
              J Dunlap
              wrote on last edited by
              #6

              You may find what you need at PInvoke.net[^], but they don't have all of the Win32 structures defined.

              D 1 Reply Last reply
              0
              • J J Dunlap

                You may find what you need at PInvoke.net[^], but they don't have all of the Win32 structures defined.

                D Offline
                D Offline
                David Fleming
                wrote on last edited by
                #7

                Boy, I hope you're not getting irritated with this thread yet because I like learning stuff. I'm just wondering if you've ever actually attempted what I'm talking about here. Your original response, and the associated links, made it seem as though this might be fairly easy. Unfortunately, the more I learn about it, the harder it seems to be getting. I was thinking about writing an article discussing OwnerDraw vs CustomDraw to customize the look of a ListView, but the CustomDraw thing seems to be monumental. Just from the standpoint of defining all the structs involved: NMHDR is easy, NMCUSTOMDRAW is fairly straightforward but even that also involves a RECT structure; don't even get me started on NMLVCUSTOMDRAW which involves RECT and COLORREF which itself is fairly complex. I am persistently running into unhandled exceptions involving read/write to protected memory which I am figuring must be due to not having completely figured all the structs out yet. I retrieve the LParam using:

                            nmlvCust = (NMLVCUSTOMDRAW)m.GetLParam(typeof(NMLVCUSTOMDRAW));
                

                and put it back into the LParam using:

                            Marshal.StructureToPtr(nmlvCust, m.LParam, true);
                

                But somewhere along the chain of messages, at the GetLParam point, I get the access violation. If you have any suggestions or ideas, please offer. If not, then I figure I'll abandon this for now and perhaps come back to it some other time. Regardless, thanks for your input -- it has certainly pointed me into areas I've not explored. It's been pretty cool thus far. Thanks.

                J 1 Reply Last reply
                0
                • D David Fleming

                  Boy, I hope you're not getting irritated with this thread yet because I like learning stuff. I'm just wondering if you've ever actually attempted what I'm talking about here. Your original response, and the associated links, made it seem as though this might be fairly easy. Unfortunately, the more I learn about it, the harder it seems to be getting. I was thinking about writing an article discussing OwnerDraw vs CustomDraw to customize the look of a ListView, but the CustomDraw thing seems to be monumental. Just from the standpoint of defining all the structs involved: NMHDR is easy, NMCUSTOMDRAW is fairly straightforward but even that also involves a RECT structure; don't even get me started on NMLVCUSTOMDRAW which involves RECT and COLORREF which itself is fairly complex. I am persistently running into unhandled exceptions involving read/write to protected memory which I am figuring must be due to not having completely figured all the structs out yet. I retrieve the LParam using:

                              nmlvCust = (NMLVCUSTOMDRAW)m.GetLParam(typeof(NMLVCUSTOMDRAW));
                  

                  and put it back into the LParam using:

                              Marshal.StructureToPtr(nmlvCust, m.LParam, true);
                  

                  But somewhere along the chain of messages, at the GetLParam point, I get the access violation. If you have any suggestions or ideas, please offer. If not, then I figure I'll abandon this for now and perhaps come back to it some other time. Regardless, thanks for your input -- it has certainly pointed me into areas I've not explored. It's been pretty cool thus far. Thanks.

                  J Offline
                  J Offline
                  J Dunlap
                  wrote on last edited by
                  #8

                  David Fleming wrote:

                  Boy, I hope you're not getting irritated with this thread yet because I like learning stuff.

                  Nope! :-)

                  David Fleming wrote:

                  I'm just wondering if you've ever actually attempted what I'm talking about here. Your original response, and the associated links, made it seem as though this might be fairly easy. Unfortunately, the more I learn about it, the harder it seems to be getting.

                  The devil is in the details! ;) No, I've never attempted to do a custom-draw listview, but I've worked with notification messages before, and this is a pretty typical case for them. I've got a lot of experience with interop, which is why I make it sound easy... but it's only easy if you've dealt with this stuff a lot. COLORREF==int. You can use ColorTranslator.FromWin32() to convert it to a Color. Your NMLVCUSTOMDRAW structure should look like this:

                  [StructLayout(LayoutKind.Sequential)]
                  struct NMLVCUSTOMDRAW
                  {
                  NMCUSTOMDRAW customDraw;
                  int textColor;
                  int textBackColor;
                  int subItemIndex;
                  //**From here on, the members are only declared for Common Controls 6
                  // - Windows XP and up! Therefore, if they are included, you can only
                  //use this struct on XP and above!**
                  uint itemType;
                  int faceColor;
                  //You may want to define the following 4 as enum values for ease of use
                  int iconEffect;
                  int iconPhase;
                  int partID;
                  int stateID;
                  RECT textRect;
                  uint groupAlignment;
                  }

                  NMCUSTOMDRAW, RECT, and NMHDR can be found on PInvoke.net[^]. Let me know if this is the same as or different than what you are doing. If it is the same, then the problem must lie elsewhere besides the struct definitions. If you need to support versions before Common Controls 6, but still want to support the XP features when they're available, you can use a BinaryReader instead of structures. I'll give you more on that if you want.

                  D 1 Reply Last reply
                  0
                  • J J Dunlap

                    David Fleming wrote:

                    Boy, I hope you're not getting irritated with this thread yet because I like learning stuff.

                    Nope! :-)

                    David Fleming wrote:

                    I'm just wondering if you've ever actually attempted what I'm talking about here. Your original response, and the associated links, made it seem as though this might be fairly easy. Unfortunately, the more I learn about it, the harder it seems to be getting.

                    The devil is in the details! ;) No, I've never attempted to do a custom-draw listview, but I've worked with notification messages before, and this is a pretty typical case for them. I've got a lot of experience with interop, which is why I make it sound easy... but it's only easy if you've dealt with this stuff a lot. COLORREF==int. You can use ColorTranslator.FromWin32() to convert it to a Color. Your NMLVCUSTOMDRAW structure should look like this:

                    [StructLayout(LayoutKind.Sequential)]
                    struct NMLVCUSTOMDRAW
                    {
                    NMCUSTOMDRAW customDraw;
                    int textColor;
                    int textBackColor;
                    int subItemIndex;
                    //**From here on, the members are only declared for Common Controls 6
                    // - Windows XP and up! Therefore, if they are included, you can only
                    //use this struct on XP and above!**
                    uint itemType;
                    int faceColor;
                    //You may want to define the following 4 as enum values for ease of use
                    int iconEffect;
                    int iconPhase;
                    int partID;
                    int stateID;
                    RECT textRect;
                    uint groupAlignment;
                    }

                    NMCUSTOMDRAW, RECT, and NMHDR can be found on PInvoke.net[^]. Let me know if this is the same as or different than what you are doing. If it is the same, then the problem must lie elsewhere besides the struct definitions. If you need to support versions before Common Controls 6, but still want to support the XP features when they're available, you can use a BinaryReader instead of structures. I'll give you more on that if you want.

                    D Offline
                    D Offline
                    David Fleming
                    wrote on last edited by
                    #9

                    The struct I had defined was pretty much the same except for the COLORREF parts. I had seen on PInvoke what you said about COLORREF being an int. The good news is that the memory exception went away when I fixed all the structs (using mine as well as your new one). The bad news is that it still isn't actually doing anything. I'll include my overridden WndProc for you to look at, and I'll make a couple of comments on it below:

                        protected override void WndProc(ref System.Windows.Forms.Message m)
                        {
                            //Always perform default functionality
                            base.WndProc(ref m);
                    
                            //Check to see if it is a WM\_NOTIFY msg
                            if (m.Msg == WM\_NOTIFY)
                            {
                                //NMHDR nmHdr = new NMHDR();
                                //nmHdr = (NMHDR)m.GetLParam(typeof(NMHDR));
                                nmlvCust = (NMLVCUSTOMDRAW)m.GetLParam(typeof(NMLVCUSTOMDRAW));
                                if (nmlvCust.nmcd.hdr.code == NM\_CUSTOMDRAW)
                                {
                                    //Have windows do default drawing in case we don't make any changes
                                    m.Result = (IntPtr)CDRF\_DODEFAULT;
                                    Debug.WriteLine("In CustomDraw. dwDrawStage = " + nmlvCust.nmcd.dwDrawStage  ". Result = " + m.Result);
                    
                    
                                    if (nmlvCust.nmcd.dwDrawStage == CDDS\_PREPAINT)
                                    {
                                        //We will notify windows that we want messages for each item
                                        m.Result = (IntPtr)CDRF\_NOTIFYITEMDRAW;
                                        Debug.WriteLine("In PrePaint. dwDrawStage = " + nmlvCust.nmcd.dwDrawStage + ". Result = " + m.Result);
                                    }
                                    else if (nmlvCust.nmcd.dwDrawStage == CDDS\_ITEMPREPAINT)
                                    {
                                        //We will notify windows that we want messages for each subitem
                                        m.Result = (IntPtr)CDRF\_NOTIFYSUBITEMDRAW;
                                        Debug.WriteLine("In ItemPrePaint. dwDrawStage = " + mlvCust.nmcd.dwDrawStage + ". Result = " + m.Result);
                                    }
                                    else if (nmlvCust.nmcd.dwDrawStage == (CDDS\_ITEMPREPAINT | CDDS\_SUBITEM))
                                    {
                                        //Here's where we do our stuff
                                        if (nmlvCust.iSubItem == 0)
                                        {
                                            nmlvCust.clrText = ColorTranslator.ToWin32(SystemColors.Control);
                                            nmlvCust.clrTextBk = ColorTranslator.ToWin32(SystemColors.Info);
                                        }
                    
                    1 Reply Last reply
                    0
                    Reply
                    • Reply as topic
                    Log in to reply
                    • Oldest to Newest
                    • Newest to Oldest
                    • Most Votes


                    • Login

                    • Don't have an account? Register

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • World
                    • Users
                    • Groups