I have some paths drawn on screen in wpf. The coordinates used are pretty small, so they were made to fill the screen with a viewing field. Now I am trying to implement the pan and zoom functions. I would like to be able to scale wherever the mouse is connected to ui (the enlarged screen center is equal to the coordinates of the mouse). The current result is that the center of the screen when zoomed in does not reflect the exact position of the mouse on ui.
If you want to see what happens ... Here is my current solution file .
or
Here is the code:
View Xaml
<Grid Name="MasterGrid" DataContext="{StaticResource mainWindowViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" Name="VisualisationScroller">
<Viewbox Name="VisualisationBox" Stretch="Fill" Loaded="VisualisationBox_Loaded">
<ItemsControl Name="CustomDrawingElement" ItemsSource="{Binding Trajectories}" Width="{Binding VisualisationWidth}" Height="{Binding VisualisationHeight}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="data:VisualisedTrajectory">
<Path Data = "{Binding PathData}" Stroke="Red" StrokeThickness="0.001" Fill="Transparent" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
Background="DarkGray"
IsItemsHost="True">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
<TranslateTransform />
</TransformGroup>
</ItemsControl.RenderTransform>
</ItemsControl>
</Viewbox>
</ScrollViewer>
</Grid>
Show model
public class MainWindowViewModel : BaseViewModel
{
public MainWindowViewModel()
{
VisualiseRawTrajectories();
}
private ObservableCollection<VisualisedTrajectory> _trajectories = new ObservableCollection<VisualisedTrajectory>();
public ObservableCollection<VisualisedTrajectory> Trajectories
{
get { return _trajectories; }
}
#region VisualisationDimensions
private double _visualisationWidth = 100;
public double VisualisationWidth
{
get { return _visualisationWidth; }
private set { _visualisationWidth = value; }
}
private double _visualisationHeight = 100;
public double VisualisationHeight
{
get { return _visualisationHeight; }
private set { _visualisationHeight = value; }
}
#endregion
public void VisualiseRawTrajectories()
{
var rand = new Random();
for (int i = 0; i < 5; i++)
{
var currentTrajectorySet = new List<Point>();
for (int j = 0; j < 5; j++)
{
currentTrajectorySet.Add(new Point(rand.NextDouble() * 0.5, rand.NextDouble() * 0.5));
if(j == 4)
{
currentTrajectorySet.Add(new Point(0.5, 0.5));
_trajectories.Add(new VisualisedTrajectory(CreatePathData(currentTrajectorySet)));
}
}
}
VisualisationHeight = 0.5;
VisualisationWidth = 0.5;
OnPropertyChanged("VisualisationHeight");
OnPropertyChanged("VisualisationWidth");
}
private Geometry CreatePathData(IList<Point> points)
{
var geometry = new StreamGeometry {FillRule = FillRule.EvenOdd};
using (StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(points[0], false, false);
ctx.PolyLineTo(points, true, true);
}
return (Geometry)geometry.GetAsFrozen();
}
}
View code behind
public MainWindow()
{
InitializeComponent();
VisualisationScroller.PreviewMouseWheel += OnPreviewMouseWheel;
}
private Point originalDimensions;
private void VisualisationBox_Loaded(object sender, RoutedEventArgs e)
{
Viewbox viewBox = sender as Viewbox;
viewBox.Width = viewBox.ActualWidth;
viewBox.Height = viewBox.ActualHeight;
originalDimensions = new Point(viewBox.ActualWidth, viewBox.ActualHeight);
}
#region Zoom
private int _numberDrawnItems = 0;
private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var zoomScale = new Point(CustomDrawingElement.RenderTransform.Value.M11,
CustomDrawingElement.RenderTransform.Value.M22);
if (CustomDrawingElement != null && _numberDrawnItems != CustomDrawingElement.Items.Count)
{
_numberDrawnItems = CustomDrawingElement.Items.Count;
CustomDrawingElement.RenderTransformOrigin = new Point(0.5, 0.5);
}
if (e.Delta > 0)
{
if (CustomDrawingElement != null)
{
VisualisationBox.Width = originalDimensions.X * (zoomScale.X + 1);
VisualisationBox.Height = originalDimensions.Y * (zoomScale.Y + 1);
var mousePosition = e.GetPosition(MasterGrid);
CustomDrawingElement.RenderTransformOrigin = new Point(mousePosition.X / MasterGrid.ActualWidth, mousePosition.Y / MasterGrid.ActualHeight);
CustomDrawingElement.RenderTransform = new MatrixTransform(zoomScale.X + 1, 0, 0, zoomScale.Y + 1, 0, 0);
}
}
if (e.Delta < 0)
{
if (zoomScale.X > 1 && zoomScale.Y > 1)
{
if (CustomDrawingElement != null)
{
VisualisationBox.Width = VisualisationBox.Width - originalDimensions.X;
VisualisationBox.Height = VisualisationBox.Height - originalDimensions.Y;
CustomDrawingElement.RenderTransform = new MatrixTransform(zoomScale.X - 1, 0, 0, zoomScale.Y - 1, 0, 0);
}
}
}
e.Handled = true;
}
#endregion //Zooming code
}