Freigeben über


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

Comments

  • Anonymous
    October 05, 2007
    PingBack from http://msdnrss.thecoderblogs.com/2007/10/05/ui-scaling-ui-zooming-with-wpf/

  • Anonymous
    October 13, 2007
    WPF enables the application developer to apply arbitrary transforms to every UI element. This can be

  • Anonymous
    October 21, 2007
    Eye, I agree it is easier but watch the cut-off of 100% CPU utilisation be so painfully slow.. WPF needs performance work people, I don't know how many times that needs to be said to realise it will fail because it is so pathetic in rendering. Go native if you have to achieve it but please do not advocate it is without a major, design fault so far: requiring a CrayT90000

  • Anonymous
    October 21, 2007
    The comment has been removed

  • Anonymous
    January 06, 2008
    Thanks. I'm just starting to use WPF for some real world apps and your notes helped me to get a zoomable UI up and running in seconds.

  • Anonymous
    December 03, 2008
    I'm having a canvas in a scrollviewer, like the code below. The scaling works brilliantly when zooming in, when zooming out my canvas is getting smaller. I would like my canvas to keep filling the scrollviewer but the elements on the canvas to become smaller. Do you have any hint on how to achieve this? Thanks!        <ScrollViewer x:Name="TheScrollViewer" Grid.Row="0" CanContentScroll="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">            <Canvas x:Name="ScheduleCanvas" Width="{Binding ElementName=TheScrollViewer, Path=ActualWidth}" Height="{Binding ElementName=TheScrollViewer, Path=ActualHeight}">                <Canvas.LayoutTransform>                    <ScaleTransform CenterX="0" CenterY="0">                        <ScaleTransform.ScaleX>                            <Binding Mode="Default" Path="Value" ElementName="ZoomSlider"/>                        </ScaleTransform.ScaleX>                        <ScaleTransform.ScaleY>                            <Binding Mode="Default" Path="Value" ElementName="ZoomSlider"/>                        </ScaleTransform.ScaleY>                    </ScaleTransform>                </Canvas.LayoutTransform>            </Canvas>        </ScrollViewer>

  • Anonymous
    April 21, 2009
    Great example, exactly what I was looking for! Thanks

  • Anonymous
    May 10, 2009
    That's a great article, many thanks. Lets say we have a large area for interface, is it possible to zoom out or zoom in to where mouse pointer is?

  • Anonymous
    May 10, 2009
    I've found this article which achieves a similar result. Because I don't have Visual Studio at work   I can't play with its source code: http://compilewith.net/2008/06/wpf-zoom-decorator-part1.html You can see a part of the code in the link above. One thing is interesting, which is related to the question I've asked previously: <ContentPresenter RenderTransformOrigin="0.5,0.5"> I guess by reading mouse position and giving it to RenderTransformOrigin in each render, zooming to/from mouse pointer position can be achieved. I'm a WPF newbie so I don't know if it is feasible for your code Ivo. Thanks

  • Anonymous
    June 13, 2009
    話題の小向美奈子ストリップを隠し撮り!入念なボディチェックをすり抜けて超小型カメラで撮影した神動画がアップ中!期間限定配信の衝撃的映像を見逃すな

  • Anonymous
    December 25, 2013
    This is great, thank you very much for sharing! I have just one question, is it possible to make zooming of the UI controles, like to deviating the buttons one from each other when zooming? That would be very useful to me, but I don't have idea how should I do that.. :/

  • Anonymous
    December 27, 2013
    @Daisy Yes, you can achieve that by adding an empty element (e.g. a grid row) between the buttons.

  • Anonymous
    April 01, 2014
    Is it possible not to resize the control while scaling?

  • Anonymous
    April 02, 2014
    @.Net Team Certainly! You just apply the scale transform to a UI element (or container) that does not contain the control.

  • Anonymous
    April 30, 2014
    It's amazing how far ahead of your time you are, you wrote this 7 years ago I'm just checking it out now! Thank you for your brilliance.

  • Anonymous
    May 24, 2014
    This article was very useful! Thanks man!

  • Anonymous
    October 02, 2014
    Thank you man. That's what I looked for

  • Anonymous
    January 15, 2015
    Thanks for the useful tip! I am having a problem trying to scale a control (ListView) within a ScrollViewer within a Grid (within my User Control). I just want the ListView to scale, not the scroll bars of the parent, etc. However, changing the LayoutTransform of the ListView affects ALL other controls in the UserControl.... Weird thing is when I embed the WPF control in a Windows Form app... the scaling works correctly, the scrollbars don't scale with the ListView! Any idea what I might be missing?