This is an interesting request.
To solve the problem I used the ZoomPanelDump to check what are the edge values where ZoomPanel's Viewbox value need to be preserved.
Below you can find the source for the solution. To check the solution, copy the source code over the existing code in the ZoomPanelDumpSample.xaml.cs file in the ZoomPanel Samples under ZoomPanel folder.
I will add the GetActualViewbox method to the next version of ZoomPanel so such calculations will be easier.
Note that when using ZoomPanelNavigator you need to set the IsZoomPanelLimitedToMinMaxZoomFactors property to false to prevent limiting Viewbox by the ZoomPanelNavigator.
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.Shapes;
using Ab2d.Controls;
namespace Ab2d.ZoomControlSample.ZoomPanel
{
/// <summary>
/// Interaction logic for ZoomPanelDumpSample.xaml
/// </summary>
public partial class ZoomPanelDumpSample : Page
{
public ZoomPanelDumpSample()
{
InitializeComponent();
ZoomPanel1.PreviewViewboxChanged += delegate(object sender, ViewboxChangedRoutedEventArgs e)
{
// First we prevent zoom out (viewbox.Width or Height bigger then 1)
if (e.NewViewboxValue.Width > 1)
e.NewViewboxValue = new Rect(e.OldViewboxValue.X, e.OldViewboxValue.Y, 1, e.NewViewboxValue.Height);
if (e.NewViewboxValue.Height > 1)
e.NewViewboxValue = new Rect(e.OldViewboxValue.X, e.OldViewboxValue.Y, e.NewViewboxValue.Width, 1);
var newViewbox = e.NewViewboxValue;
var actualNewViewbox = GetActualViewbox(newViewbox);
//System.Diagnostics.Debug.WriteLine("actualViewbox: " + actualViewbox.ToString());
if (actualNewViewbox.X < -(actualNewViewbox.Width / 2) ||
actualNewViewbox.X > (1 - actualNewViewbox.Width / 2))
{
// Update the viewbox to use the old X value
e.NewViewboxValue = new Rect(e.OldViewboxValue.X, newViewbox.Y, newViewbox.Width, newViewbox.Height);
//e.Handled = true; // Prevent any change
}
if (actualNewViewbox.Y < -(actualNewViewbox.Height / 2) ||
actualNewViewbox.Y > (1 - actualNewViewbox.Height / 2))
{
// Update the viewbox to use the old Y value
e.NewViewboxValue = new Rect(e.NewViewboxValue.X, e.OldViewboxValue.Y, newViewbox.Width, newViewbox.Height);
//e.Handled = true; // Prevent any change
}
};
}
private Rect GetActualViewbox(Rect viewbox)
{
var actualContentSize = ZoomPanel1.UsedViewboxEx.ActualContentSize;
double zoomPanelAspectRatio = ZoomPanel1.ActualWidth / ZoomPanel1.ActualHeight;
double contentAspectRatio = actualContentSize.Width / actualContentSize.Height;
Rect actualViewbox;
double aspectRatioFactor = zoomPanelAspectRatio / contentAspectRatio;
if (aspectRatioFactor > 1) // zoomPanelAspectRatio < contentAspectRatio
{
// ZoomPanel is wider then content - this means that on the left and right we show empty space - so the actualViewbox.Width is bigger then viewbox.Width
double actualWidth = viewbox.Width * aspectRatioFactor;
double actualX = -0.5 * (actualWidth - viewbox.Width) + viewbox.X;
actualViewbox = new Rect(actualX, viewbox.Y, actualWidth, viewbox.Height);
}
else
{
// ZoomPanel content is wider then ZoomPanel - this means that on the top and bottom show empty space - so the actualViewbox.Height is bigger then viewbox.Height
double actualHeight = viewbox.Height / aspectRatioFactor;
double actualY = -0.5 * (actualHeight - viewbox.Height) + viewbox.Y;
actualViewbox = new Rect(viewbox.X, actualY, viewbox.Width, actualHeight);
}
return actualViewbox;
}
private void ShowAdditionalInfoCheckBox_Checked(object sender, RoutedEventArgs e)
{
if (!this.IsLoaded)
return;
ZoomPanelDump1.ShowAdditionalInfo = true;
}
private void ShowAdditionalInfoCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
if (!this.IsLoaded)
return;
ZoomPanelDump1.ShowAdditionalInfo = false;
}
}
}