2009-07-24 4 views
10

Я программирую одно простое приложение C#, и мне нужно событие onScroll в Listview. Таким образом, я создал класс ListviewEx, который наследует оригинальный ListView. Я нашел, как обнаружить сообщение прокрутки от WinAPI и модифицированный метод WndProc. Теперь у меня есть этот WndProc:Событие ListView onScroll

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

    if (m.Msg == WM_VSCROLL) 
    { 
     onScroll(this, new EventArgs()); 
    } 
} 

Но проблема в том, что я не знаю, как определить информацию о прокрутке. Эти данные должны быть в WParam, но в C# нет макроса LOWORD, как в C++, и мне нужно переключиться на обнаружение таких параметров, как SB_ BOTTOM, SB_ ENDSCROLL, SB_PAGEUP и т. Д.

Есть ли способ заменить макрос LOWORD на C#?

Или другим способом определения необходимых параметров прокрутки?

ответ

9

Вы можете определить константы WPARAM, как следует:

private const int WM_HSCROLL = 0x114; 
private const int WM_VSCROLL = 0x115; 

private const int SB_HORZ = 0; 
private const int SB_VERT = 1; 

private const int SB_LINELEFT = 0; 
private const int SB_LINERIGHT = 1; 
private const int SB_PAGELEFT = 2; 
private const int SB_PAGERIGHT = 3; 
private const int SB_THUMBPOSITION = 4; 
private const int SB_THUMBTRACK = 5; 
private const int SB_LEFT = 6; 
private const int SB_RIGHT = 7; 
private const int SB_ENDSCROLL = 8; 

private const int SIF_TRACKPOS = 0x10; 
private const int SIF_RANGE = 0x1; 
private const int SIF_POS = 0x4; 
private const int SIF_PAGE = 0x2; 
private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS; 

Фактический код для проверки WPARAM бы что-то вроде этого:

if (m.Msg == WM_VSCROLL) 
{ 

     ScrollInfoStruct si = new ScrollInfoStruct(); 
     si.fMask = SIF_ALL; 
     si.cbSize = (uint)Marshal.SizeOf(si); 
     GetScrollInfo(msg.HWnd, SB_VERT, ref si); 
     if (msg.WParam.ToInt32() == SB_ENDSCROLL) 
     { 
      ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, si.nPos); 
      onScroll(this, sargs); 
     } 
} 

pinvoke.net большой сайт, чтобы получить постоянную значения, используемые в API Windows32, без необходимости самостоятельно проверять файлы заголовков.

See this example

+0

Спасибо! Это именно то, что мне нужно :) – Klinki

+0

Что такое msg variable в "if (msg.WParam.ToInt32() == SB_ENDSCROLL) "? Я не вижу определения msg – anhtv13

8

Мартейн ответ будет работать, но не будет ловить все прокруткой. Сообщение WM_VSCROLL отправляется только тогда, когда пользователь непосредственно манипулирует полосой прокрутки. Если пользователь прокручивается с помощью колеса мыши или использует клавиши UpArrow/DownArrow/PageUp/PageDown, то WM_VSCROLL не будет отправлен.

Вы можете прокручивать прокрутку, вызванную полосой прокрутки и колесиком мыши, прослушивая уведомление об уведомлении LVN_BEGINSCROLL.

Ловля прокрутки, которая возникает при использовании клавиш, сложнее. Нет сообщения, которое отправляется в элемент управления, когда он, например, прокручивается в ответ на ключ PageUp. Лучшее, что можно сделать в этом случае, - прослушивать события KeyPress, а затем проверять изменения в позициях полосы прокрутки до и после события.

Это может быть, конечно, полный перебор для ваших целей. Сообщение WM_VSCROLL может быть вполне достаточным для того, что вы хотите. Но если вы хотите поймать всю возможную прокрутку, посмотрите код в ObjectListView, у которого уже есть событие Scroll, которое ловит все эти возможности.

+0

Слишком правдиво, я пропустил это в своем ответе. –

13

Спасибо за ваши ответы. Это действительно помогло мне :) Теперь у меня есть то, что я хотел ...

Вот код:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 
using Microsoft.Win32; 
using System.Reflection; 

namespace ControlsEx 
{ 
    public class ListViewEx : ListView 
    { 
     // Windows messages 
     private const int WM_PAINT  = 0x000F; 
     private const int WM_HSCROLL = 0x0114; 
     private const int WM_VSCROLL = 0x0115; 
     private const int WM_MOUSEWHEEL = 0x020A; 
     private const int WM_KEYDOWN = 0x0100; 
     private const int WM_LBUTTONUP = 0x0202;     

     // ScrollBar types 
     private const int SB_HORZ = 0; 
     private const int SB_VERT = 1; 

     // ScrollBar interfaces 
     private const int SIF_TRACKPOS = 0x10; 
     private const int SIF_RANGE  = 0x01; 
     private const int SIF_POS  = 0x04; 
     private const int SIF_PAGE  = 0x02; 
     private const int SIF_ALL  = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS; 

     // ListView messages 
     private const uint LVM_SCROLL  = 0x1014; 
     private const int LVM_FIRST  = 0x1000;     
     private const int LVM_SETGROUPINFO = (LVM_FIRST + 147); 

     public enum ScrollBarCommands : int 
     { 
      SB_LINEUP = 0, 
      SB_LINELEFT = 0, 
      SB_LINEDOWN = 1, 
      SB_LINERIGHT = 1, 
      SB_PAGEUP = 2, 
      SB_PAGELEFT = 2, 
      SB_PAGEDOWN = 3, 
      SB_PAGERIGHT = 3, 
      SB_THUMBPOSITION = 4, 
      SB_THUMBTRACK = 5, 
      SB_TOP = 6, 
      SB_LEFT = 6, 
      SB_BOTTOM = 7, 
      SB_RIGHT = 7, 
      SB_ENDSCROLL = 8 
     } 

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

      switch(m.Msg) 
      { 
       case WM_VSCROLL: 
        ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT)); 
        onScroll(this, sargs); 
        break; 

       case WM_MOUSEWHEEL: 
        ScrollEventArgs sarg = new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT)); 
        onScroll(this, sarg); 
        break; 

       case WM_KEYDOWN: 
        switch (m.WParam.ToInt32()) 
        { 
         case (int)Keys.Down: 
          onScroll(this, new ScrollEventArgs(ScrollEventType.SmallDecrement, GetScrollPos(this.Handle, SB_VERT))); 
          break; 
         case (int)Keys.Up: 
          onScroll(this, new ScrollEventArgs(ScrollEventType.SmallIncrement, GetScrollPos(this.Handle, SB_VERT))); 
          break; 
         case (int)Keys.PageDown: 
          onScroll(this, new ScrollEventArgs(ScrollEventType.LargeDecrement, GetScrollPos(this.Handle, SB_VERT))); 
          break; 
         case (int)Keys.PageUp: 
          onScroll(this, new ScrollEventArgs(ScrollEventType.LargeIncrement, GetScrollPos(this.Handle, SB_VERT))); 
          break; 
         case (int)Keys.Home: 
          onScroll(this, new ScrollEventArgs(ScrollEventType.First, GetScrollPos(this.Handle, SB_VERT))); 
          break; 
         case (int)Keys.End: 
          onScroll(this, new ScrollEventArgs(ScrollEventType.Last, GetScrollPos(this.Handle, SB_VERT))); 
          break; 
        } 
        break; 
      } 

     } 

     public int ScrollPosition 
     { 
      get 
      { 
       return GetScrollPos(this.Handle, SB_VERT); 
      } 
      set 
      { 
       int prevPos; 
       int scrollVal; 

       if (ShowGroups == true) 
       { 
        prevPos = GetScrollPos(this.Handle, SB_VERT); 
        scrollVal = -(prevPos - value); 
       } 
       else 
       { 
        // TODO: Add setScrollPosition if ShowGroups == false 
       } 

       SendMessage(this.Handle, LVM_SCROLL, (IntPtr)0, (IntPtr)scrollVal); 
      } 
     } 

     public event ScrollEventHandler onScroll; 

     [DllImport("user32.dll")] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi); 

     [DllImport("user32.dll")] 
     public static extern int SendMessage(
       int hWnd,  // handle to destination window 
       uint Msg,  // message 
       long wParam, // first message parameter 
       long lParam // second message parameter 
      ); 

     [DllImport("user32.dll")] 
     static extern int SendMessage(IntPtr hWnd, int wMsg, 
             int wParam, int lParam); 

     [DllImport("user32.dll")] 
     static extern int SendMessage(IntPtr hWnd, uint wMsg, 
             IntPtr wParam, IntPtr lParam); 

     [DllImport("user32.dll")] 
     static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw); 

     [DllImport("user32.dll", CharSet = CharSet.Auto)] 
     static extern int GetScrollPos(IntPtr hWnd, int nBar); 


     [StructLayout(LayoutKind.Sequential)] 
     struct SCROLLINFO 
     { 
      public uint cbSize; 
      public uint fMask; 
      public int nMin; 
      public int nMax; 
      public uint nPage; 
      public int nPos; 
      public int nTrackPos; 
     } 
    } 
} 
+0

Приятно, мне, вероятно, понадобится заимствовать этот код. :) Я заметил, что событие Key down перевод в 'ScrollEventArgs' отменены, хотя и не используются' LargeIncrement' и 'LargeDecrement'. Поэтому' Keys.Up' должен соответствовать 'SmallDecrement',' PageUp' => 'LargeDecrement',' Down' => 'SmallIncrement',' PageDown' => 'LargeIncrement'. – Jonas

+0

Я тоже заимствую этот код :) Спасибо за вклад. Однако он дает ошибку« NullReferenceException »в случаях WM_MOUSEWHEEL и WM_KEYDOWN (где методы onScroll вызывается) по той причине, что я не знаю почему. –

0

@Klinki

Спасибо вам прохладный вклад Я думаю, что вы забыли обработать горизонтальными прокрутка

Case WM_HSCROLL 
    Dim sargs As New ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(Me.Handle, SB_HORZ)) 
      RaiseEvent OnScroll(Me, sargs) 
      Exit Select 
Смежные вопросы