UI Scaling (UI Zooming) with WPF

WPF enables the application developer to apply arbitrary transforms to every UI element. This can be used to create dynamically scalable/zoomable UIs, enabling the end-user to scale the user interface of the application.

Pulling off a dynamically scalable/zoomable UI is prohibitively difficult (and thus practically impossible) to do in classic Win32 (and by extension - WinForms) and DHTML as well as other popular GUI toolkits such as AWT, Swing, QT, etc. It is, however, remarkably easy to do in WPF. Here’s how:

1. First, you need to give to the user a way to control the scale factor. A Slider control is the natural choice for that

<Slider

    x:Name="uiScaleSlider"

    ToolTip="Determines the UI scale factor."

    Value="1" Minimum="0.1" Maximum="4"

    ...

/>

 

2. Next, define a LayoutTransform for the FrameworkElement you intend to scale (this is typically the top-level panel that contains all of your UI). The LayoutTransform needs to be a ScaleTransform.

 

3. Bind the scale factor of the ScaleTransform to the value of the slider control.

<DockPanel Grid.Column="0" Grid.ColumnSpan="2" LastChildFill="True">

    <DockPanel.LayoutTransform>

        <ScaleTransform 

            CenterX="0" CenterY="0"

            ScaleX="{Binding ElementName=uiScaleSlider,Path=Value}"

            ScaleY="{Binding ElementName=uiScaleSlider,Path=Value}"

        />

    </DockPanel.LayoutTransform>

    ...

</DockPanel>

 

4. Finally, provide a way for the user to reset the scaling factor back to 1 or 100%. One way to do that is to do that is to define a double-click handler for your Scale control, which resets the scale factor:

public partial class Window1 : System.Windows.Window

{

    public Window1()

    {

        InitializeComponent();

        uiScaleSlider.MouseDoubleClick +=

            new MouseButtonEventHandler(RestoreScalingFactor);  

    }

 

    void RestoreScalingFactor(object sender, MouseButtonEventArgs args)

    {

       ((Slider)sender).Value = 1.0;  

    }

}  

 

Here are the results:

image

Note that because of the use of LayoutTransform, the original layout of the window is preserved. All of elements of the UI are within reach and completely usable regardless of the current scale factor.

Adding mouse wheel support

Several existing WPF applications (e.g. Microsoft Expression Blend) already expose such UI scaling capability. Some use the mouse wheel to control the scale factor.

Here’s one possible strategy of using the mouse-wheel. Let’s assume the following UI contract:

Gesture

UI meaning

CTRL + Wheel up

Scale up (i.e. zoom in)

CTRL + Wheel down

Scale down (i.e. zoom out)

CTRL + Wheel click

Restore scaling factor to 100%

 

We just intercept the appropriate events in the OnPreviewMouseWheel and OnPreviewMouseDown methods of the main window as follows:

public partial class Window1 : System.Windows.Window

{

    public Window1()

    {

        ...

    }

 

    ...

 

    protected override void OnPreviewMouseWheel(MouseWheelEventArgs args)

    {

        base.OnPreviewMouseWheel(args);

        if (Keyboard.IsKeyDown(Key.LeftCtrl) ||

  Keyboard.IsKeyDown(Key.RightCtrl))

        {

            uiScaleSlider.Value += (args.Delta > 0) ? 0.1 : -0.1;

        }

    }

 

    protected override void OnPreviewMouseDown(MouseButtonEventArgs args)

    {

        base.OnPreviewMouseDown(args);

        if (Keyboard.IsKeyDown(Key.LeftCtrl) ||

           Keyboard.IsKeyDown(Key.RightCtrl))

        {

            if (args.MiddleButton == MouseButtonState.Pressed)

            {

                RestoreScalingFactor(uiScaleSlider, args);

            }

        }

    }

}

ScalableUI.zip