11-08-2011, 01:57 PM
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:
ZoomToObjectSample.xaml.cs:
The sample will be also part of the samples that will come with the next version of ZoomPanel.
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