2016-04-15 3 views
1

WM_MOUSEWHEEL сообщения отправляются в элемент управления с фокусом. Мое приложение имеет сложную иерархию управления с элементами управления, содержащими другие элементы управления, некоторые из которых невидимы или перекрываются. Я хотел бы, чтобы колесо мыши прокручивало конкретный ScrollableControl.Как перенаправить сообщения колесика мыши из одного окна в другое?

This question имеет ответ с помощью IMessageFilter осуществления, который ловит WM_MOUSEWHEEL сообщений. Это хорошо работает, и я вижу, что сообщения пойманы. Я попробовал манипулировать свойством ScrollableControl VerticalScroll, чтобы прокрутить его содержимое, изменив значение VerticalScroll.Value. К сожалению, есть некоторые нежелательные побочные эффекты, такие как большой палец мыши в полосе прокрутки, который не синхронизируется с содержимым ScrollableControl. Возможно, это потому, что эта работа выполняется внутри насоса сообщений, а не в обработчике событий.

This post описывает технику, в которой сообщения WM_MOUSEWHEEL отправляются в другое окно. Я хотел бы реализовать IMessageFilter, который ловит сообщения WM_MOUSEWHEEL и пересылает их назначенному получателю.

Я создаю следующее IMessageFilter, которое пытается это сделать. Я вижу, что пересылаемое сообщение попадает в мой фильтр, и я возвращаю false из фильтра, чтобы передать управление для обработки сообщения. Целевой элемент управления не принимает событие OnMouseWheel.

Можно ли изменить этот фильтр, чтобы можно было прокручивать targetControl с помощью перенаправленных сообщений?

public static class MouseWheelMessageRedirector 
{ 
    public static void Add(Control rootControl, ScrollableControl targetControl) 
    { 
     var filter = new MouseWheelMessageFilter(rootControl, targetControl); 
     Application.AddMessageFilter(filter); 

     rootControl.Disposed += (sender, args) => Application.RemoveMessageFilter(filter); 

     targetControl.MouseWheel += (sender, args) => 
     { 
      // ... this code never executes 
      System.Diagnostics.Trace.WriteLine("WHEEL ON TARGET"); 
     }; 
    } 

    private class MouseWheelMessageFilter : IMessageFilter 
    { 
     const int WM_MOUSEWHEEL = 0x020A; 

     [DllImport("user32.dll", SetLastError = true)] 
     static extern bool PostMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); 

     public MouseWheelMessageFilter(Control rootControl, ScrollableControl targetControl) 
     { 
      _rootControl = rootControl; 
      _targetControl = targetControl; 
      _targetWindowHandle = _targetControl.Handle; 
     } 

     public bool PreFilterMessage(ref Message m) 
     { 
      if (m.Msg != WM_MOUSEWHEEL) 
       return false; 

      if (m.HWnd == _targetWindowHandle) 
       return false; 

      // ... get the control that the mouse is over 
      // ... determine if this is a control that we want to handle the message for 
      // ... (omitted) 

      PostMessage(_targetWindowHandle, m.Msg, m.WParam, m.LParam); 
      return true; 
     } 

     private Control _rootControl; 
     private ScrollableControl _targetControl; 
     private IntPtr _targetWindowHandle; 
    } 
} 

ответ

0

Я просто сделал это то же самое. Вот что я сделал:

public bool PreFilterMessage(ref Message m) 
    { 
     if ((WM)m.Msg == WM.MOUSEWHEEL) 
     { 
      // if mouse is over a certain component, prevent scrolling 
      if (comboBoxVendors.Bounds.Contains(PointToClient(Cursor.Position))) 
      { 
       // return true which says the message is already processed 
       return true; 
      } 


      // which direction did they scroll? 
      int delta = 0; 
      if ((long)m.WParam >= (long)Int32.MaxValue) 
      { 
       var wParam = new IntPtr((long)m.WParam <<32>> 32); 
       delta = wParam.ToInt32() >> 16; 
      } 
      else 
      { 
       delta = m.WParam.ToInt32() >> 16; 
      } 

      delta = delta*-1; 
      var direction = delta > 0 ? 1 : 0; 

      // post message to the control I want scrolled (I am converting the vertical scroll to a horizontal, bu you could just re-send the same message to the control you wanted 
      PostMessage(splitContainerWorkArea.Panel2.Handle, Convert.ToInt32(WM.HSCROLL), (IntPtr) direction, IntPtr.Zero); 

      // return true to say that I handled the message 
      return true; 
     }       

     // message was something other than scroll, so ignore it 
     return false; 
    } 

Кроме того, я использовал окно сообщений перечисления из Pinvoke.net: http://www.pinvoke.net/default.aspx/Enums.WindowsMessages

Наконец, не забудьте удалить фильтр сообщений, когда вы закрываете форму, или когда нет дольше необходимо обработать сообщения:

Application.RemoveMessageFilter(thisForm) 
Смежные вопросы