Zoom and pan to specific object
#1
In the thread "How to reset without losing rotation?" (http://forum.wpf-graphics.com/showthread.php?tid=196) user asked a question on how to zoom and pan to a specific object.

Because this could be very interesting for others I have created a separate thread so the solution will be better visible.

The following code is used to create random objects inside ZoomPanel and then the user can select the objects from the ListBox and the ZoomPanel will zoom and pan to the object.


ZoomToObjectSample.xaml:
Code:
<Page x:Class="Ab2d.ZoomControlSample.ZoomPanel.ZoomToObjectSample"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:ab2d="clr-namespace:Ab2d.Controls;assembly=Ab2d.Controls.ZoomPanel"
      mc:Ignorable="d"
      d:DesignHeight="500" d:DesignWidth="800"
    Title="ZoomToControlSample">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="180"/>
        </Grid.ColumnDefinitions>

        <ab2d:ZoomPanel Name="ZoomPanel1" Grid.Column="0" Background="White"
                        IsAnimated="True" ZoomMode="None"
                        AnimationDuration="{Binding ElementName=DurationTextBox, Path=Text}"
                        ViewboxLimits="0 0 1 1" IsViewboxLimited="True">
            <Canvas Name="ContentCanvas"/>
        </ab2d:ZoomPanel>

        <Border Grid.Column="1" BorderBrush="Black" BorderThickness="1">
            <StackPanel Orientation="Vertical" Margin="10">
                <TextBlock FontSize="12" FontWeight="Bold" Text="Selected object:"/>
                
                <ListBox Name="ObjectsListBox" Width="150" Margin="0 5 0 0" HorizontalAlignment="Left" SelectionChanged="ObjectsListBox_SelectionChanged"/>

                <TextBlock Margin="0 15 0 0" Text="Zoom Margin:"/>
                <ComboBox Name="MarginComboBox" SelectedIndex="0" Width="80" HorizontalAlignment="Left" SelectionChanged="MarginComboBox_SelectionChanged">
                    <ComboBoxItem>1.0</ComboBoxItem>
                    <ComboBoxItem>1.2</ComboBoxItem>
                    <ComboBoxItem>1.5</ComboBoxItem>
                    <ComboBoxItem>2.0</ComboBoxItem>
                </ComboBox>

                <TextBlock Margin="0 2 0 0" TextWrapping="Wrap" Foreground="DimGray" Text="Margin defines how much space will visible around selected object."/>

                <TextBlock Margin="0 15 0 0" Text="Animation Duration (h:m:s):"/>
                <TextBox Name="DurationTextBox" Width="60" HorizontalAlignment="Left" Height="20">0:0:0.5</TextBox>
            </StackPanel>
        </Border>
    </Grid>
</Page>

ZoomToObjectSample.xaml.cs:
Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Ab2d.ZoomControlSample.ZoomPanel
{
    /// <summary>
    /// Interaction logic for ZoomToObjectSample.xaml
    /// </summary>
    public partial class ZoomToObjectSample : Page
    {
        private FrameworkElement _selectedObject;

        public ZoomToObjectSample()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(ZoomToObjectSample_Loaded);
        }

        void ZoomToObjectSample_Loaded(object sender, RoutedEventArgs e)
        {
            FillObjects();
        }

        private void ObjectsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!this.IsLoaded)
                return;

            if (ObjectsListBox.SelectedIndex == 0)
            {
                // Show all objects
                ZoomPanel1.Reset();
                _selectedObject = null;
            }
            else
            {
                FrameworkElement targetObject = ((ListBoxItem)ObjectsListBox.SelectedItem).Tag as FrameworkElement;

                ZoomToObject(targetObject);
            }
        }

        private void MarginComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_selectedObject == null)
                return;

            ZoomToObject(_selectedObject);
        }


        private void ZoomToObject(FrameworkElement targetObject)
        {
            // First get the position of the selected object
            // For our simple sample we can also use Canvas.GetLeft and Canvas.GetTop
            // But for more complex scenarios where the object is inside many parents with transformations we need to use TransformToAncestor
            GeneralTransform transform = targetObject.TransformToAncestor(ContentCanvas);
            Point objectsPosition = transform.Transform(new Point(0, 0));

            // Get the size of object
            Size objectsSize = new Size(targetObject.ActualWidth, targetObject.ActualHeight);

            // We will zoom to the center of the object
            Point centerPosition = new Point(objectsPosition.X + objectsSize.Width / 2, objectsPosition.Y + objectsSize.Height / 2);

            // Now get the required zoom level (1 = no zoom, 2 = 200% zoom, etc.)
            double zoomX, zoomY;
            double zoomFactor;

            // get zoom factor for width and height of the object
            zoomX = ContentCanvas.ActualWidth / objectsSize.Width;
            zoomY = ContentCanvas.ActualHeight / objectsSize.Height;

            // Get min value (so the whole object is shown) and adjust it for the selected margin
            zoomFactor = Math.Min(zoomX, zoomY);
            zoomFactor /= GetSelectedMargin();

            // Set the new zoom position and zoom factor
            ZoomPanel1.SetZoom(centerPosition, Controls.ZoomPanel.CenterPositionUnitsType.Absolute, zoomFactor);

            _selectedObject = targetObject;
        }

        private double GetSelectedMargin()
        {
            double margin;

            // NOTE: This is not very generic but works for this simple sample
            switch (MarginComboBox.SelectedIndex)
            {
                case 0:
                default:
                    margin = 1.0;
                    break;

                case 1:
                    margin = 1.2;
                    break;

                case 2:
                    margin = 1.5;
                    break;

                case 3:
                    margin = 2.0;
                    break;
            }

            return margin;
        }

        private void FillObjects()
        {
            Shape newShape;
            string shapeName;
            ListBoxItem item;
            Random rnd = new Random();

            ContentCanvas.Children.Clear();
            ObjectsListBox.Items.Clear();

            // Add special object to show all objects (zoom out)
            item = new ListBoxItem();
            item.Content = "Show all objects";
            item.IsSelected = true;
            ObjectsListBox.Items.Add(item);


            Color[] colors = new Color[] { Colors.Red, Colors.Blue, Colors.Green, Colors.Orange };
            string[] colorNames = new string[] { "Red", "Blue", "Green", "Orange" };

            for (int c = 0; c < colors.Length; c++ )
            {
                Color color = colors[c];

                for (int i = 0; i < 3; i++)
                {
                    double x, y;
                    double width, height;

                    // Get random position and size
                    width = 50 + rnd.NextDouble() * 100;
                    height = 20 + rnd.NextDouble() * 50;

                    // Use ZoomPanel1's actual size because Canvas size is set at the end of this method
                    x = 20 + rnd.NextDouble() * (ZoomPanel1.ActualWidth - 40 - width);
                    y = 20 + rnd.NextDouble() * (ZoomPanel1.ActualHeight - 40 - height);

                    switch (i)
                    {
                        case 0:
                        default:
                            newShape = new Rectangle();
                            shapeName = "Rectangle";
                            break;

                        case 1:
                            Rectangle newRectangle = new Rectangle();
                            shapeName = "Rounded Rectangle";
                            newRectangle.RadiusX = 5;
                            newRectangle.RadiusY = 5;
                            newShape = newRectangle;
                            break;

                        case 2:
                            newShape = new Ellipse();
                            shapeName = "Ellipse";
                            break;
                    }

                    newShape.Width = width;
                    newShape.Height = height;
                    newShape.Opacity = 0.8;
                    newShape.Fill = new SolidColorBrush(color);
                    newShape.Stroke = Brushes.Black;
                    newShape.StrokeThickness = 1;
                    Canvas.SetLeft(newShape, x);
                    Canvas.SetTop(newShape, y);

                    ContentCanvas.Children.Add(newShape);

                    item = new ListBoxItem();
                    item.Tag = newShape;
                    item.Content = string.Format("{0} {1}", colorNames[c], shapeName);

                    ObjectsListBox.Items.Add(item);
                }
            }

            // Without setting the size of the Canvas its content would not be visible
            // Use ZoomPanel1's actual size because it is already set in Loaded event
            ContentCanvas.Width = ZoomPanel1.ActualWidth;
            ContentCanvas.Height = ZoomPanel1.ActualHeight;

            ObjectsListBox.Focus();
        }
    }
}

The sample will be also part of the samples that will come with the next version of ZoomPanel.
Andrej Benedik
#2
I tried to run this example as using a window instead of a page and keep getting an error on load:

'NaN,NaN,1,1' is not a valid value for property 'Viewbox'.

If I put a breakpoint in the loading code, pause and then run it it works until I select an item from the listbox. I then get the same error.
#3
JRodman, please be correct.

You should admit that if the code above is copied as it is written here, it works without problems.

As it is seen from your newer post you had change the code quite a lot.

Also first you sad that the code does not work and than in the next post you sad it works but you have some other problems. Also adding a breakpoint to the code surely does not make it work - there are some other things that are wrong.

Therefore it is not correct from your side to say that the code does not work.

If you would like to get help from this forum, I would ask you to be more precise with your questions.

I do not say that ZoomPanel is 100% bug free, but before concluding that the problem is in ZoomPanel, please check your code first.
Andrej Benedik
#4
I would like to inform you that a greatly improved version of Zoom to object sample is available with full source code the new version of ZoomPanel (v3.3)

Check out the video of the sample - http://www.youtube.com/embed/sSVPQE3EY_o
Andrej Benedik
  


Forum Jump:


Users browsing this thread:
1 Guest(s)