WPF-Acercar una imagen dentro de un visor de desplazamiento y hacer que las barras de desplazamiento se ajusten en consecuencia
image-processing (3)
He reunido una aplicación WPF simple para demostrar el problema que tengo. Mi XAML está abajo:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="427" Width="467" Loaded="MainWindow_OnLoaded">
<Grid>
<ScrollViewer Name="MyScrollViewer" CanContentScroll="True">
<Image Name="MyImage" HorizontalAlignment="Left" VerticalAlignment="Top" MouseWheel="UIElement_OnMouseWheel" MouseDown="MyImage_OnMouseDown" MouseUp="MyImage_OnMouseUp"/>
</ScrollViewer>
</Grid>
</Window>
El código subyacente está abajo:
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void UIElement_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var matrix = MyImage.RenderTransform.Value;
if (e.Delta > 0)
{
matrix.ScaleAt(1.5, 1.5, e.GetPosition(this).X, e.GetPosition(this).Y);
}
else
{
matrix.ScaleAt(1.0 / 1.5, 1.0 / 1.5, e.GetPosition(this).X, e.GetPosition(this).Y);
}
MyImage.RenderTransform = new MatrixTransform(matrix);
}
private WriteableBitmap writeableBitmap;
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var image = new WriteableBitmap(new BitmapImage(new Uri(@"C:/myImage.png", UriKind.Absolute)));
MyImage.Width = image.Width;
MyImage.Height = image.Height;
image = BitmapFactory.ConvertToPbgra32Format(image);
writeableBitmap = image;
MyImage.Source = image;
}
private Point downPoint;
private Point upPoint;
private void MyImage_OnMouseDown(object sender, MouseButtonEventArgs e)
{
downPoint = e.GetPosition(MyImage);
}
private void MyImage_OnMouseUp(object sender, MouseButtonEventArgs e)
{
upPoint = e.GetPosition(MyImage);
writeableBitmap.DrawRectangle(Convert.ToInt32(downPoint.X), Convert.ToInt32(downPoint.Y), Convert.ToInt32(upPoint.X), Convert.ToInt32(upPoint.Y), Colors.Red);
MyImage.Source = writeableBitmap;
}
}
}
He añadido WriteableBitmapEx utilizando Nuget. Si ejecuta esto y reemplaza myImage.png con la ubicación de una imagen real en su computadora, encontrará una aplicación que se parece a esto:
Puede dibujar un cuadro en la imagen haciendo clic en la esquina superior izquierda del lugar donde desea que vaya el cuadro, y arrastrando hacia la parte inferior derecha del lugar donde desea que vaya el cuadro, aparece un rectángulo rojo. Incluso puede hacer zoom con el botón central y dibujar un rectángulo de cerca para obtener más precisión, esto funciona como se espera.
El problema es que, cuando se desplaza con el mouse central, las barras de desplazamiento no se reajustan, lo cual es un requisito del programa que estoy creando. Mi pregunta es ¿cómo puedo forzar la reajuste de las barras de desplazamiento en el visor de desplazamiento cuando se acerca la imagen?
Estoy convencido de que tiene algo que ver con la propiedad RenderTransform del ScrollViewer, y que necesito actualizarla al mismo tiempo que actualizo la propiedad RenderTransform de la imagen (en UIElement_OnMouseWheel) pero no estoy seguro de cómo hacerlo. .
Debería usar LayoutTransform
lugar de RenderTransform
en su imagen.
RenderTransform
ocurre después de que se completa el diseño y es solo visual. LayoutTransform
se realiza antes de la aprobación del diseño y, por lo tanto, puede notificar al ScrollViewer
del nuevo tamaño.
Consulte aquí para obtener más información: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.layouttransform.aspx
Para el desplazamiento puro, prefiero usar un ScaleTransform, debería ajustar las barras de desplazamiento en consecuencia. Puedes probar el siguiente código si soluciona tu problema.
private double _zoomValue = 1.0;
private void UIElement_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
{
_zoomValue += 0.1;
}
else
{
_zoomValue -= 0.1;
}
ScaleTransform scale = new ScaleTransform(_zoomValue, _zoomValue);
MyImage.LayoutTransform = scale;
e.Handled = true;
}
Supongamos que tiene un Canvas_Main dentro de un ViewBox_CanvasMain, que a su vez dentro de un ScrollViewer_CanvasMain. Desea acercar girando la rueda del mouse y ScrollViewer ajustará el desplazamiento automáticamente para que la función (señalada con el mouse en el Canvas_Main) permanezca durante el acercamiento / alejamiento. Es complejo, pero aquí está el código llamado por el controlador de eventos de la rueda del mouse:
private void MouseWheelZoom(MouseWheelEventArgs e)
{
if(Canvas_Main.IsMouseOver)
{
Point mouseAtImage = e.GetPosition(Canvas_Main); // ScrollViewer_CanvasMain.TranslatePoint(middleOfScrollViewer, Canvas_Main);
Point mouseAtScrollViewer = e.GetPosition(ScrollViewer_CanvasMain);
ScaleTransform st = ViewBox_CanvasMain.LayoutTransform as ScaleTransform;
if (st == null)
{
st = new ScaleTransform();
ViewBox_CanvasMain.LayoutTransform = st;
}
if (e.Delta > 0)
{
st.ScaleX = st.ScaleY = st.ScaleX * 1.25;
if (st.ScaleX > 64) st.ScaleX = st.ScaleY = 64;
}
else
{
st.ScaleX = st.ScaleY = st.ScaleX / 1.25;
if (st.ScaleX < 1) st.ScaleX = st.ScaleY = 1;
}
#region [this step is critical for offset]
ScrollViewer_CanvasMain.ScrollToHorizontalOffset(0);
ScrollViewer_CanvasMain.ScrollToVerticalOffset(0);
this.UpdateLayout();
#endregion
Vector offset = Canvas_Main.TranslatePoint(mouseAtImage, ScrollViewer_CanvasMain) - mouseAtScrollViewer; // (Vector)middleOfScrollViewer;
ScrollViewer_CanvasMain.ScrollToHorizontalOffset(offset.X);
ScrollViewer_CanvasMain.ScrollToVerticalOffset(offset.Y);
this.UpdateLayout();
e.Handled = true;
}
}