Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Improve Zooming with Enhanced Mouse Wheels

4.65/5 (7 votes)
19 Oct 2011CPOL6 min read 44.4K   4K  
How to give your users a better zooming experience with High Resolution Mouse Wheels.

Introduction

This article looks at the code changes that need to be made to an application to make it zoom properly with Enhance High Resolution Mouse Wheels. It is the third Logitech CodeProject article written to help developers implement superior support for enhanced mouse wheels. In the first article co-authored with Tanvi Shah, Handling Enhanced Mouse Wheels in your Application, we looked at scrolling issues. The second article, Improving WPF Mouse Wheel Processing, written by Roger Vuistiner includes an excellent library that easily adds enhanced mouse wheel support to WPF applications.

Zoom problems with enhanced mouse wheels

Enhanced mouse wheel support was added with Microsoft’s introduction of Vista. For a fixed amount of rotation, an enhanced mouse wheel will generate more wheel events than a standard mouse wheel. With each event, a standard mouse wheel will have a delta value of +/- 120. Enhanced mouse wheels will have a smaller value – typically divisible into 120. Windows Vista and later automatically enables smooth scrolling for any device that supports it. Today, many applications, when used with an enhanced mouse wheel, don’t work properly or are not written to support the high resolution feature that takes advantage of the wheel movement’s granularity. The most common problem with zooming is this:

  • The application often ignores the magnitude of the absolute wheel delta value. This causes the application to zoom too quickly.

Other problems include:

  • Application does not zoom at all. This occurs if the delta value is not large enough for the application to visibly zoom. The application also fails to accumulate the wheel deltas until the minimum amount needed to zoom is reached.
  • If the user goes from zooming in to zooming out (or vice versa), the mouse wheel needs to turn more than one notch. This occurs if the application accumulates the delta (or remainder after zooming) but does not clear it out when zooming is reversed.
  • The zooming experience is no smoother than with a standard mouse. This can occur when the application only zooms when the accumulated delta reaches a magnitude of WHEEL_DELTA (defined in winuser.h as 120).

How to test enhanced mouse wheel zoom

You will need a mouse with Enhanced Scroll Wheel capabilities with the appropriate drivers. You may find it necessary to contact the manufacturer to obtain drivers or directions as to how to turn on this capability. To see if it is turned on, you’ll want to run Spy++ or similar message snooping software to capture the WM_MOUSEWHEEL messages. Use the scroll wheel to zoom your application. What you want to see are multiple messages for a small turn of the mouse wheel and each message with an absolute delta value of less than 120 (in the below example, we see a zDelta of -15).

spy.png

If you do not have an enhanced wheel mouse, use the Enhanced Scroll Wheel Emulator included. This application emulates an enhanced wheel by sending smaller wheel deltas to your application. So you can still test your application by running the emulator application (HiResScrollWheel.exe) and create your own Hi-Res WM_MOUSEWHEEL messages.

EMULATOR.png

Launching HiResScrollWheel.exe and setting the parameters as follows emulates a Hi-Res mouse with 8 events per click and a delta of 15.

If your application behaves well, the zooming behavior will be smoother with the enhanced mouse wheel. It should also zoom the same amount as with a regular mouse wheel.

Adding enhanced wheel zoom support to a Silverlight application

The first application I will add enhanced mouse wheel zoom support to is DeepZoomSample.

deepzoom.png

You can download, modify, and build the source from CodeProject.

The original code in Page.xaml.cs for processing mouse wheels was:

C#
void ZoomImage_MouseWheel(object obj,
     System.Windows.Input.MouseWheelEventArgs e)
{
    double newzoom = zoom;
    if (e.Delta > 0)
        newzoom /= 1.3;
    else
        newzoom *= 1.3;
    Point logicalPoint =
    this.ZoomImage.ElementToLogicalPoint(this.lastMousePos);
    logicalPoint.X, logicalPoint.Y);
    this.ZoomImage.ZoomAboutLogicalPoint(zoom/newzoom,
    zoom = newzoom;
    e.Handled = true;
}

The method was changed to:

C#
void ZoomImage_MouseWheel(object obj, System.Windows.Input.MouseWheelEventArgs e)
{
    double newzoom = zoom;
    newzoom /= Math.Pow(1.3, (e.Delta / 120.0F));
    Point logicalPoint = 
    this.ZoomImage.ElementToLogicalPoint(this.lastMousePos);
    this.ZoomImage.ZoomAboutLogicalPoint(zoom / newzoom,
    logicalPoint.X, logicalPoint.Y);
    zoom = newzoom;
    e.Handled = true;
}

In ZoomImage_MouseWheel(), it was necessary to factor in the magnitude of the mouse wheel delta. Previously the code only considered the sign (+,-) of the wheel delta.

To factor in the delta’s absolute value instead of dividing or multiplying the old zoom value by 1.3, we always divide the old zoom value by 1.3 to the (e.Delta / 120) power. Note if e.Delta is negative, then we will divide the old zoom value by 1.3 to a negative power value (e.Delta/120) which is equivalent to multiplying the old zoom value by 1.3 to the abs(e.Delta/120) power.

There is an important difference between handling enhanced mouse wheel deltas for scrolling and zooming. For scrolling, the delta value affects the scroll position arithmetically.

The typical formula is:

C#
if (e.Delta > 0)
    newScrollPos = oldScrollPos + fixedScrollValue ;
else
    newScrollPos = oldScrollPos - fixedScrollValue ;

To factor in the magnitude of the delta, we can simply scale fixedScrollValue as follows:

C#
newScrollPos = oldScrollPos + (fixedScrollValue*delta/120) ;

For zooming however, the change is often geometric.

C#
if (e.Delta > 0)
    newZoomFactor = oldZoomFactor * fixedZoomValue ;
else
    newZoomFactor = oldZoomFactor / fixedZoomValue ;

To factor in the magnitude of the delta, we need to scale fixedZoomValue as follows:

C#
newZoomFactor = oldZoomFactor * Math.Pow(fixedZoomValue, delta / 120);

Adding enhanced mouse wheel support to the Ab2d.Controls.ZoomPanel library

In my second example, I added enhanced mouse wheel support to WPF Graphic’s ZoomPanel sample application. WPF Graphics Tools authors the Ab3d.PowerToys library that provides excellent 3D graphic, editing, and zooming support. The default behavior of this library however does not support enhanced mouse wheels out of the box. It appears the library uses the following algorithm to handle zooming with mouse wheels:

C#
if (e.Delta > 0)
   ZoomPanel1.ZoomForFactor(ZoomPanel1.MouseWheelZoomFactor);
else
   ZoomPanel1.ZoomForFactor(1 / ZoomPanel1.MouseWheelZoomFactor);

Image 4

Adding support to its sample program required routing the preview mouse wheel event to our handler.

This is done by adding a line to ZoomPanelSample.xaml:

XML
<Page x:Class="Ab2d.ZoomControlSample.ZoomPanel.ZoomPanelSample"
    PreviewMouseWheel="PreviewMouseWheelEventHandler" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ab2d="clr-namespace:Ab2d.Controls;assembly=Ab2d.Controls.ZoomPanel">

I handle zooming with the Preview Mouse Wheel Event because the Mouse Wheel Event was already routed to the Ab2d.Controls.ZoomPanel library. By setting e.Handled to true in PreviewMouseWheelEventHandler(), I stopped the Mouse Wheel Event from being generated and handled by the library. To zoom, I copied the code from PainterSample.xaml.cs and called ZoomForFactor(). It was also necessary to turn off animation when zooming with an enhanced mouse wheel for the graphic to zoom well.

PreviewMouseWheelEventHandler() was added to ZoomPanelSample.xaml.cs as follows:

C#
public void PreviewMouseWheelEventHandler(
      Object sender,
      MouseWheelEventArgs e )
{
      if (ZoomPanel1.IsMouseWheelZoomEnabled == false)
          return;
      double zoomFactor;
      bool bToggle = false;
      zoomFactor = Math.Pow(ZoomPanel1.MouseWheelZoomFactor,
                                                   ((double)e.Delta / 120.0));
      if ((ZoomPanel1.IsAnimated) && (e.Delta > -120) && (e.Delta < 120))
          bToggle = true;
      if (bToggle)
      {
          ZoomPanel1.IsAnimated = false;
          ZoomPanel1.ZoomForFactor(zoomFactor);
          ZoomPanel1.IsAnimated = true;
      }
      else
      {
          ZoomPanel1.ZoomForFactor(zoomFactor);
      }
      e.Handled = true;
}

Adding enhanced wheel zoom support to a WPF custom control

SimpleZoomAndPanSample is one of the programs included in the CodeProject article: A WPF custom control for zooming and panning. To add enhanced wheel zoom support, I modified the method for handling mouse wheels to factor in the magnitude of the mouse wheel delta. The change in zoom was arithmetic and therefore did not require taking the nth root of the zoom factor 1.1.

wpf.png

The original code in MainWindow.xaml.cs is as follows:

C#
private void zoomAndPanControl_MouseWheel(object sender, MouseWheelEventArgs e)
{
   e.Handled = true;
   if (e.Delta > 0)
   {
      ZoomIn();
   }
   else if (e.Delta < 0)
   {
      ZoomOut();
   }
}
private void ZoomOut()
{
    zoomAndPanControl.ContentScale -= 0.1; //change is arithmetic, not geometric
}
private void ZoomIn()
{
    zoomAndPanControl.ContentScale += 0.1; //change is arithmetic, not geometric
}

The method in MainWindow.xaml.cs was modified as follows:

C#
private void zoomAndPanControl_MouseWheel(object sender, MouseWheelEventArgs e)
{
   e.Handled = true;
   Zoom(e.Delta);
}
private void Zoom(int Delta)
{
    zoomAndPanControl.ContentScale += 0.1*Delta/120;
}

Adding enhanced wheel zoom support to a Visual Basic application

The CodeProject article, Pan and Zoom Very Large Images, includes a Visual Basic program that can easily be modified to support zoom for enhanced mouse wheels. To add enhanced wheel zoom support, I modified the method for handling mouse wheels to factor in the magnitude of the mouse wheel delta.

imagecontrol.png

The original code follows:

VB
Private Sub ZoomImage(ByVal ZoomIn As Boolean)
   ' Get center point
   m_centerpoint.X = m_Origin.X + SrcRect.Width / 2
   m_centerpoint.Y = m_Origin.Y + SrcRect.Height / 2

   'set new zoomfactor
   If ZoomIn Then
        ZoomFactor = Math.Round(ZoomFactor * 1.1, 2) 
   Else
        ZoomFactor = Math.Round(ZoomFactor * 0.9, 2)
   End If

   'Reset the origin to maintain center point
   m_Origin.X = m_centerpoint.X - ClientSize.Width / m_ZoomFactor / 2
   m_Origin.Y = m_centerpoint.Y - ClientSize.Height / m_ZoomFactor / 2

   CheckBounds()
End Sub

Changes made:

VB
Private Sub ZoomImage(ByVal Delta As Integer)
   ' Get center point
   m_centerpoint.X = m_Origin.X + SrcRect.Width / 2
   m_centerpoint.Y = m_Origin.Y + SrcRect.Height / 2

   'set new zoomfactor
   If (ZoomIn > 0) Then
       ZoomFactor = Math.Round(ZoomFactor * Math.Pow(1.1, ZoomIn / 120.0), 6)
   ElseIf (ZoomIn < 0) Then
       ZoomFactor = Math.Round(ZoomFactor * Math.Pow(0.9, -ZoomIn / 120.0), 6)
   End If 

   'Reset the origin to maintain center point
   m_Origin.X = m_centerpoint.X - ClientSize.Width / m_ZoomFactor / 2
   m_Origin.Y = m_centerpoint.Y - ClientSize.Height / m_ZoomFactor / 2

   CheckBounds()
End Sub

Adding enhanced wheel zoom support to a C# application

ImageZoom.exe is a C# application included in the CodeProject article: Zooming and panning in Windows Forms with fixed focus.

windowForm.png

OnMouseWheel() in MainForms.cs was modified to factor in the magnitude of the mouse wheel delta.

Original code:

C#
if (e.Delta > 0)
{
    zoom += 0.1F; //change is arithmetic, not geometric
}
else if (e.Delta < 0) //change is arithmetic, not geometric
{
    zoom = Math.Max(zoom - 0.1F, 0.01F);
}

Changed to:

C#
zoom = Math.Max(zoom + (0.1F*e.Delta/120.0F), 0.01F);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)