想象一下,您有一個畫布,您想要縮放到很高的值,然後允許「平移」。翻譯一個高比例的WPF畫布因素並非離原點平滑
一個很好的例子是一個地理工具,需要允許從整個地球範圍到幾米範圍內的「縮放」級別進行平移。
我發現,如果您縮放到超過500,000,翻譯變得非常不穩定,但只有當您遠離畫布的0,0原點觀看!
我試着翻譯使用畫布的RenderTransform,我試圖通過在縮放畫布上移動另一個畫布。我在別人的在線示例應用程序中也看到了同樣的問題。
以下示例代碼提供了在兩個不同縮放位置進行平移(單擊並拖動)。如果你實現了代碼,你可以點擊一個按鈕放大到0,0,你會發現很好的,平滑的鼠標平移。然後使用其他按鈕放大到200,200平滑平移不再!
任何想法爲什麼這是或如何可以解決它?
XAML樣品:
<Window x:Class="TestPanZoom.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="500" Width="500" Loaded="Window_Loaded">
<Grid PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown" MouseMove="Grid_MouseMove">
<Canvas Name="canvas1"></Canvas>
<Button Height="31"
Name="button1"
Click="button1_Click"
Margin="12,12,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Left" Width="270">
Zoom WAY in to 0,0 and get smooth panning
</Button>
<Button Height="31"
Name="button2"
Click="button2_Click"
Margin="12,49,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="270">
Zoom WAY in to 200, 200 -- NO smooth panning
</Button>
</Grid>
</Window>
代碼樣品:
using System;
using System.Collections.Generic;
using System.Linq;
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 TestPanZoom
{
/// <summary>
/// Interaction logic for Window1.xaml
/// Demo of an issue with translate transform when scale is very high
/// but ONLY when you are far from the canvas's 0,0 origin.
/// Why? Is their a fix?
/// </summary>
public partial class Window1 : Window
{
Point m_clickPoint;
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Add a 2x2 black ellipse centered at 0,0
Ellipse el = new Ellipse();
el.Fill = Brushes.Black;
el.Width = 2;
el.Height = 2;
el.HorizontalAlignment = HorizontalAlignment.Left;
el.VerticalAlignment = VerticalAlignment.Top;
el.Margin = new Thickness(0 - el.Width/2, 0 - el.Height/2, 0, 0);
canvas1.Children.Add(el);
// Add a 1x1 red rectangle with its top/left corner at 0,0
Rectangle r = new Rectangle();
r.Fill = Brushes.Red;
r.Width = 1;
r.Height = 1;
r.HorizontalAlignment = HorizontalAlignment.Left;
r.VerticalAlignment = VerticalAlignment.Top;
r.Margin = new Thickness(0, 0, 0, 0);
canvas1.Children.Add(r);
// Add a 2x2 purple ellipse at a point 200,200
Point otherPoint = new Point(200, 200);
el = new Ellipse();
el.Fill = Brushes.Purple;
el.Width = 2;
el.Height = 2;
el.HorizontalAlignment = HorizontalAlignment.Left;
el.VerticalAlignment = VerticalAlignment.Top;
el.Margin = new Thickness(otherPoint.X - el.Width/2, otherPoint.Y - el.Height/2, 0, 0);
canvas1.Children.Add(el);
// Add a 1x1 blue rectangle with its top/left corner at 200,200
r = new Rectangle();
r.Fill = Brushes.Blue;
r.Width = 1;
r.Height = 1;
r.HorizontalAlignment = HorizontalAlignment.Left;
r.VerticalAlignment = VerticalAlignment.Top;
r.Margin = new Thickness(otherPoint.X, otherPoint.Y, 0, 0);
canvas1.Children.Add(r);
}
private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
m_clickPoint = e.GetPosition(this);
}
// Pan with the mouse when left-mouse is down
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point mousePosition = e.GetPosition(this);
double xDiff = mousePosition.X - m_clickPoint.X;
double yDiff = mousePosition.Y - m_clickPoint.Y;
TranslateTransform tt = new TranslateTransform(xDiff, yDiff);
TransformGroup tg = new TransformGroup();
tg.Children.Add(canvas1.RenderTransform);
tg.Children.Add(tt);
canvas1.RenderTransform = tg;
m_clickPoint = e.GetPosition(this);
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
TransformGroup tg = new TransformGroup();
double scale = 1000000;
double xCenter = 0;
double yCenter = 0;
double xOffset = (canvas1.ActualHeight/2.0 - xCenter);
double yOffset = (canvas1.ActualWidth/2.0 - yCenter);
ScaleTransform st = new ScaleTransform(scale, scale);
st.CenterX = xCenter;
st.CenterY = yCenter;
TranslateTransform tt = new TranslateTransform(xOffset, yOffset);
tg.Children.Add(st);
tg.Children.Add(tt);
canvas1.RenderTransform = tg;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
TransformGroup tg = new TransformGroup();
double scale = 1000000;
double xCenter = 200;
double yCenter = 200;
double xOffset = (canvas1.ActualHeight/2.0 - xCenter);
double yOffset = (canvas1.ActualWidth/2.0 - yCenter);
ScaleTransform st = new ScaleTransform(scale, scale);
st.CenterX = xCenter;
st.CenterY = yCenter;
TranslateTransform tt = new TranslateTransform(xOffset, yOffset);
tg.Children.Add(st);
tg.Children.Add(tt);
canvas1.RenderTransform = tg;
}
}
}
感謝您的回覆! 恐怕這個建議的解決方案有兩個問題。首先,我不想拖動特定的視覺項目,我試圖平移整個畫布(移動畫布上的所有視覺效果)。在一個現實世界的例子中,放大可以顯示很多可視對象的細節。我需要圍繞這些物體平移,就好像轉移畫布一樣。 其次,我恐怕你的拖動效果在200,200還不夠流暢!檢查一下 - 你可以抓住方塊,但你必須拖動鼠標一點點,然後跳到一個地方,然後再拖一下,再跳一遍,等等。 – FTLPhysicsGuy 2010-08-20 14:16:16
它在我的筆記本電腦上運行平穩,並不是速度的惡魔:)關於第二個想法1mln zoom + paning與許多元素(整個地球egzample)的整個畫布是不可能的。 ScaleTransform不會超過1000個IMO。當你拖動200.200時,沒有必要轉換和渲染0.0平方。你需要渲染你在屏幕上看到的+ 40%,50%的光滑平移,並且在放大/縮小時應該動態加載獨立級別的解鎖。換句話說,渲染你看到的和更多一點。製造這種容器是很多工作。 – 2010-08-20 15:13:42