Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Viewbox and ScrollViewer to work together

I’ve got n playing map where I use the ScrollViewer to move around the map, and I wish to use the ViewBox together with PinchManipulations to zoom in and out of the map. So far I’ve done this by setting the ScrollViewer’s Manipulation mode to control, however this gives me an lag when I zoom. Is there a way to get the ViewBox and ScrollViewer to work better together and thereby avoid the lag? The code I’ve got so far is:

ScrollViewer:

<ScrollViewer Grid.Column ="0"  Width="768" Height="380" HorizontalScrollBarVisibility="Hidden">
    <Viewbox Stretch="None">
        <View:Map/>
    </Viewbox>
</ScrollViewer>

PinchZoom:

<Grid x:Name="Map" Width="1271" Height="1381.5">

    <Grid.RenderTransform>
        <ScaleTransform ScaleX="{Binding Path=deltaZoom}" ScaleY="{Binding Path=deltaZoom}"/>
    </Grid.RenderTransform>

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="ManipulationStarted">
            <cmd:EventToCommand Command="{Binding Path=ZoomStartedCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>

        <i:EventTrigger EventName="ManipulationDelta">
            <cmd:EventToCommand Command="{Binding Path=ZoomDeltaCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>

        <i:EventTrigger EventName="ManipulationCompleted">
            <cmd:EventToCommand Command="{Binding Path=ZoomCompletedCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

</Grid>

The code where I use the pinch zoom:

public ICommand ZoomStartedCommand { get; set; }
public ICommand ZoomDeltaCommand { get; set; }
public ICommand ZoomCompletedCommand { get; set; }

private double _deltaZoom;
public double deltaZoom
{
    get
    {
        return _deltaZoom;
    }
    set
    {
        _deltaZoom = value;
        RaisePropertyChanged("deltaZoom");
    }
}

public double distance;
public MainViewModel()
{
    ZoomStartedCommand = new RelayCommand<ManipulationStartedEventArgs>(ZoomStart);
    ZoomDeltaCommand = new RelayCommand<ManipulationDeltaEventArgs>(ZoomDelta);
    ZoomCompletedCommand = new RelayCommand<ManipulationCompletedEventArgs>(ZoomCompleted);
}

public void ZoomStart(ManipulationStartedEventArgs e)
{
    FrameworkElement Element = (FrameworkElement)e.OriginalSource;

    var myScrollViewer = FindParentOfType<ScrollViewer>(Element) as ScrollViewer;
    myScrollViewer.SetValue(ScrollViewer.ManipulationModeProperty, ManipulationMode.Control);

}

public void ZoomDelta(ManipulationDeltaEventArgs e)
{
    if (e.PinchManipulation != null)
    { 
        deltaZoom = deltaZoom * e.PinchManipulation.DeltaScale;
    }
    else
    {
        FrameworkElement Element = (FrameworkElement)e.OriginalSource;

        var myScrollViewer = FindParentOfType<ScrollViewer>(Element) as ScrollViewer;
        myScrollViewer.SetValue(ScrollViewer.ManipulationModeProperty, ManipulationMode.System);
    }
}

public void ZoomCompleted(ManipulationCompletedEventArgs e)
{
    FrameworkElement Element = (FrameworkElement)e.OriginalSource;

    var myScrollViewer = FindParentOfType<ScrollViewer>(Element) as ScrollViewer;
    myScrollViewer.SetValue(ScrollViewer.ManipulationModeProperty, ManipulationMode.System);
}
like image 356
JonasN89 Avatar asked Nov 29 '13 14:11

JonasN89


2 Answers

I had this problem too when I tried to handle a lot of data in a single container. You can not simply load the entire map in a viewbox and use a scroll viewer and expect that everything would work fine.

The first solution to make an application to perform well on big data sets is to try UI Virtualization. A control that accepts virtualization creates only the elements needed to display the current view, or more exactly , just the elements which are visible on the display. In order to understand better this concept I will give you a real world example. Take a look at how Facebook renders its News feed. It loads X posts in the X html elements and then when the users scrolls down, it unloads non visible ones and loads the new ones.

1.UI Virtualization

It simply reuses elements. This is the concept of UI virtualization. I have to mention one important features: deferred scrolling (the user can scroll but the results are only displayed after the release of the click, this is a performance improvement if the user like to play up and down with the scrollbar)

Coming back to your problem. If the map is entirely loaded it is a performance issue to scroll or zoom into because the non visible parts would zoom and scroll also. Imagine what would had happened if Google Maps would not support UI Virtualization. When the user would try to zoom in , all Earth would zoom in(tons of GBs).

2.Data Virtualization

However your map should be fully loaded somewhere in the memory. If it is big, this is a problem and there it comes Data Virtualization. Instead of having all map fully loaded, it loads just the needed sections for display. If you want to have a better responsiveness you could use the concept that Google Maps uses. Load few more data into memory than it fits on the display when rendered, so the UI would not freeze(until the data is brought in memory) when you try to scroll.

You can read more :

  1. Implementing virtualized panel
  2. Item container generator
  3. Measure core
  4. Results

I solved my problem using Virtualized Canvas. Sample of application using virtualized canvas: http://pavzav.blogspot.ro/2010/11/virtualized-canvas.html

You might like to take a look at Wpf Bing Maps Control

like image 136
Emil Condrea Avatar answered Oct 01 '22 00:10

Emil Condrea


I think you should instead look at the viewportcontroller and look into the basic lens sample, where they have implemented this. The viewportcontroller, has a scrollviewer inside, and viewport.

The basics are this:

  1. While the image you have is "unzoomed", the scrollviewer has full control and the ViewportControl does nothing.
  2. When you start to pinch, lock the scrollviewer by disabling the verticalscrollbar AND setting the viewport.height = scrollviewer.height. This neutralizes the scollviewer.
  3. You can do a temporary zoom using the Image ScaleTransform.
  4. On pinch finished, resize your actual image so that it takes up real space inside the ViewportControl. Now your viewportControl will let you pan all over the zoomed image with nice bounce-back.
  5. When you zoom back out again, re-enable the scrollviewer. (Set the height to the screen height and turn on the scrollbar.)

FYI, I completely forget why there is a canvas in there, but I feel like it is important. See below:

While the sample below does not do what you want to do, I based my code on the MediaViewer inside this sample and modified it: Basic Lens Sample

However it should be noted that it is for picture zoom.

like image 24
JTIM Avatar answered Sep 30 '22 22:09

JTIM