PanView is a custom control that allows you to pan a canvas or any other content from within your MainWindow or from within any other user interface element.
Posts in this series:
- Zeroing the Center of a CompositeTransform
- Flattening a TransformGroup
- Using Matrices to Flatten a TransformGroup
- PanView - A Metro Panning Custom Control
- PanView - The Design
A user interface element that supports panning should allow a user to swipe the content left/right with one finger, zoom in/out using two fingers, and rotate using two fingers.
Examples
Move left/right up/down.
Rotate clockwise/counterclockwise.
Zoom in/out.
See Touch interaction design (Metro style apps) and Guidelines for Panning (Metro style apps) for more information.
My goal is to make adding panning to a C# Metro application as easy as possible. To that end, I created a custom control called PanView.
Install PanView from NuGet.org and add to your existing Metro application:
- Install the NuGet Package Manager if you have not already.
- Open your Metro application in Visual Studio
- Select Manage NuGet Packages from the Project menu.
- Click Online. Search for PanView. Click Install.
Edit MainPage.xaml to:
- Add the using:PanViewLibrary.
- Add the PanView custom control.
- Add content to the PanView. In the example below, the content is a TextBlock.
x:Class="PanViewDemoApp.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PanViewDemoApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pvl="using:PanViewLibrary"
mc:Ignorable="d">
<Grid
Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<pvl:PanView>
<TextBlock
Text="This is the pannable content" />
</pvl:PanView>
</Grid>
</Page>
Limiting Manipulation Modes
You may choose to limit the types of manipulations the user may perform by setting the PanView dependency property “ManipulationMode”. The example below limits the panning to simple x/y translations with inertia.
- <pvl:PanView
- ManipulationMode="TranslateX TranslateY TranslateInertia"
Reset
You may programmatically reset the transformations by calling PanView.Reset(). In the XAML and C# code below, clicking the the reset button resets the panning.
Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<pvl:PanView
x:Name="MyPanView">
<Grid
x:Name="_grid"
Width="2000"
Height="2000" />
</pvl:PanView>
<Button
Content="Reset"
Click="OnResetClicked" />
</Grid>
{
MyPanView.Reset();
}
Constraining Translations
To limit the amount of translation distance, set the dependency properties MinTranslateX, MaxTranslateX, MinTranslateY, and MaxTranslateY as shown here:
MinTranslateX="-200"
MaxTranslateX="100"
MinTranslateY="-300"
MaxTranslateY="600"
Binaries and Source
PanView binaries reside here: http://nuget.org/packages/panview/
PanView source resides here: http://panview.codeplex.com/
Nice control, good job!
ReplyDeleteI'm looking into using a control like this but i would like to design some boundaries for the user so they can't move the complete working surface off the screen. Is it possible to add maximum boundaries for left/right and min/max zoom?
That certainly can be done. The source code is up at panview.codeplex.com. If you want to contribute to it, feel free. Either way, I will try to add that feature in the next week or so - depending on my schedule.
DeleteI also plan on enhancing panview to support the retrieval of data to render as the user pans. This would be similar to online maps that pull data as the user explores.
Maybe you can become non-anonymous or just leave your name so I know who you are.
Anonymous,
DeleteI added the ability to limit the amount of translations with MinTranslateX, MaxTranslateX, MinTranslateY, and MaxTranslateY. See the updated blogpost, source at codeplex, and binaries at nugget.
- John
Hi John:
ReplyDeleteI'm new in Visual Studio programming and I'm doing my first projects. I found the panView very cool. I managed to draw several polylines to create a picture and I was able to scale, move and rotate the image. I'd like to do a couple of things but couldn't figure out how.
First: I would like to reset the transformations to its original state after operating the panView but could not figure out how to do it.
Second: I don't want the user to be able to rotate the panView. How can I prevent it from happening?
Thank...
Pablo Santa Ana (Argentina)
Pablo Santa Ana,
DeleteI added the ability to reset the PanView. See the updates I made to the blogpost. I updated NuGet and CodePlex too.
I updated the blogpost to explain how to prevent rotations. There was no additional change needed in PanView to support this.
I also added the ability to limit the amount of translations with MinTranslateX, MaxTranslateX, MinTranslateY, and MaxTranslateY.
- John
Hi John:
ReplyDeleteThank you very much but... (there's always a "but"): in the blog, under "Limiting Manipulation Modes" says " The example below limits the panning to simple x/y translations with inertia." but the code is not shown. A bug, may be?
Regards...
Pablo.
Pablo,
DeleteThere is no C# code required. You just need to set the ManipulationMode in the XAML. Maybe you looked at the blog post while I was in the middle of editing it? The blog post should contain the XAML snippet:
<pvl:PanView
ManipulationMode="TranslateX TranslateY TranslateInertia"
Hello John,
ReplyDeleteNice work on the Panview. a really useful control i could use in my apps in the future.
Maybe it's a good idea to make the transform group also settable so you can set the transformation during startup? for example zoom to a specific level at the start?
regards,
Geert (twitter @geertvdc)
Geert,
DeleteI updated codeplex and nuget with a Reset(CompositeTransform). Does this work for you?
Perfect!
Deletethanks John, I'll be working on an app this weekend using this control.
If it's not to much work maybe it's smart to also implement mouse zoom on the panview so it's also usable on pc's :)
Geert
oh also.. i think most people use or should use MVVM so instead of a method to (re)set the zoom/pan level it might be better to have a property we can have two way binding on.
DeleteGeert
Geert, I moved CurrentTransformation and PreviousTransformations from private fields to public dependency properties for you. I also moved some code from ManipulationStarted to ManipulationCompleted to make accessing PreviousTransformations more logical to a ViewModel.
DeleteHello John!
ReplyDeleteI happily use this control in my Metro Style project.
However I run into a quiet simple problem. Because Win 8 can suspend my application anytime it wants, I would like to save the panView's state as recommended. I have tried to serialize currentTransformation and previousTransformation.Matrix and load it back when windows restart my app. But it doesn't give back the same view.
Could you make it possible to serialize(a method with string return value?) it's internal state and then load it back?
Thanks
Adam Gavronek
Adam,
DeleteAll you need to serialize is the contents of PreviousTransformations.Matrix.
I hope this helps.
Hi John
ReplyDeleteThank you for writing & sharing this control.
I'm trying to use this and I'm encountering some problems. What I want to do is be able to display a JPEG that could be larger than the size of the screen, hence the need to zoom in and then pan around.
The XAML I have at the moment is:
My first observation is that the Stretch parameter doesn't seem to be making any difference to the scaling of the image. Presumably that is a function of PanView as, by default, the image is being shown at full size.
Second, dragging the image around reveals that the image is getting cropped somewhere/somehow. In other words, if the image is larger than the screen, it starts out showing as much of the image as it can but if I then try to pan, I just end up showing the background.
Pinch/zoom doesn't seem to be doing anything - the image zoom factor isn't changing.
Finally, I'd like the initial starting zoom factor to be such that the image is scaled to fit the screen. Is that something your control can do or do I need to calculate it and provide that through the composite transform?
Many thanks for any insights you can provide.
Philip
Oh, the XAML has been "consumed". Here it is without the less than & great than symbols:
DeleteGrid Style="{StaticResource LayoutRootStyle}"
pvl:PanView x:Name="PanningControl"
Image x:Name="pannableImage" Stretch="UniformToFill" Source="{Binding FullSizedImage}" AutomationProperties.Name="{Binding Title}"
/pvl:PanView
/Grid
Philip, I was wondering if you might try placing the Image in a Canvas.
DeletePanView, Canvas, Image
Hi John
DeleteThat seems to have fixed the pinch/zoom and the image cropping problems.
With regard to the initial starting zoom factor, am I correct that I need to calculate this myself? My concern here is getting the calculations right based on whether the app is currently snapped or not.
Similarly, like others above, I want to constrain the panning so that the user cannot pan the image beyond the image's edges. I think I'm misunderstanding how you intend the Min & Max Translate properties to be used as I'm experimenting with setting the Min values to be 0 and all that means is that I cannot pan off to the left or the top, which isn't right if I've got bits of the image off to the right or bottom that I want to be able to see.
Thanks.
Philip
Hi John,
ReplyDeleteThanks for sharing this control its been really useful in a project i'm working on at the moment. I have this one issue maybe you can help me on.
This is my XAML
In C# I add a bunch of images to myCanvas which can be manipulated via gestureRecognizer to Scale, Rotate, and Translate.
All of this is working fine. Now the issue is that i have a set of functions that arranges my images in various formations on the screen prior to using your PanView control I was arranging these images on a point that the user sets.
However now that the parent is Transformable the point of reference at which these arrangements should form gets lost.
Here is a simple bit of code that shows how i form a stack arrangement of images
scale = 0.3 * System.Math.Min(
(this.parent.ActualHeight / element.ActualHeight),
(this.parent.ActualWidth / element.ActualWidth));
element.RenderTransform = new CompositeTransform
{
TranslateX = ((2 * this._targetPt.X) - element.ActualWidth) / 2,
TranslateY = ((2* this._targetPt.Y) - element.ActualHeight) / 2,
CenterX = element.ActualWidth / 2,
CenterY = element.ActualHeight / 2,
ScaleX = scale,
ScaleY = scale,
Rotation = 0
};
this._targetPt is calculated on the Grid event handler and looks something like this
Windows.UI.Input.PointerPoint ptrPt = e.GetCurrentPoint(null);
this._targetPt = ptrPt.RawPosition;
Now as you can see i get the RawPosition of the point of interaction and use it to be the anchor for which the center of my images translate to. How now what happens is that the panview transforms the scale which results in the image arrangement to be formed at the wrong location.
Is there any way to do retrieve the scale factor and rotation from PanView to fix this issue. Or is there a better way to do this.
Thanks
Taha
Looks like the XAML didnt come through
ReplyDeletepvl:PanView x:Name="myPanView" ManipulationMode="All"
Canvas x:Name="myCanvas"
pvl:PanView
Question, when I zoom in on a textblock, the textblock gets a bit shaky(i.e. the textblock bounces). Is there any way I can solve this?
ReplyDeleteGreetings. I would like to place use this control to create a drawing app where 1 finger draws while 2 fingers pans, zooms, and rotates the canvas. Is it possible to do this if I place the canvas inside this control?
ReplyDeleteHi John
ReplyDeletevery nice work on the PanView. Is there any way to prevent the content from scaling when I zoom? I want to achieve a behaviour similar to dropped pins on google maps which spread further apart when zooming in but at constant size. I know Bing Maps API has functionality like this but I don't really need all the map imagery plus there are license issues.
Thanks for your help! Regards, Blomquist