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).
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.
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.
You can download, modify, and build the source from CodeProject.
The original code in Page.xaml.cs for processing mouse wheels was:
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:
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:
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:
newScrollPos = oldScrollPos + (fixedScrollValue*delta/120) ;
For zooming however, the change is often geometric.
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:
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:
if (e.Delta > 0)
ZoomPanel1.ZoomForFactor(ZoomPanel1.MouseWheelZoomFactor);
else
ZoomPanel1.ZoomForFactor(1 / ZoomPanel1.MouseWheelZoomFactor);
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:
<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:
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.
The original code in MainWindow.xaml.cs is as follows:
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;
}
private void ZoomIn()
{
zoomAndPanControl.ContentScale += 0.1;
}
The method in MainWindow.xaml.cs was modified as follows:
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.
The original code follows:
Private Sub ZoomImage(ByVal ZoomIn As Boolean)
m_centerpoint.X = m_Origin.X + SrcRect.Width / 2
m_centerpoint.Y = m_Origin.Y + SrcRect.Height / 2
If ZoomIn Then
ZoomFactor = Math.Round(ZoomFactor * 1.1, 2)
Else
ZoomFactor = Math.Round(ZoomFactor * 0.9, 2)
End If
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:
Private Sub ZoomImage(ByVal Delta As Integer)
m_centerpoint.X = m_Origin.X + SrcRect.Width / 2
m_centerpoint.Y = m_Origin.Y + SrcRect.Height / 2
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
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.
OnMouseWheel()
in MainForms.cs was modified to factor in the magnitude of the mouse wheel delta.
Original code:
if (e.Delta > 0)
{
zoom += 0.1F;
}
else if (e.Delta < 0)
{
zoom = Math.Max(zoom - 0.1F, 0.01F);
}
Changed to:
zoom = Math.Max(zoom + (0.1F*e.Delta/120.0F), 0.01F);