2010-08-19 3 views
3

매우 높은 값으로 배율을 조정 한 후 "패닝"을 허용하려는 캔버스가 있다고 가정 해보십시오.높은 비율로 WPF 캔버스를 원점을 벗어나 부드럽게 변환하지 못합니다.

좋은 예는 전체 지구의 넓이에서 수 미터 정도까지 "확대/축소"레벨로 패닝을 허용해야하는 지리적 도구입니다.

내가 50 만 개가 넘는 말로 변환하면 매우 불규칙하게 변하지 만 캔버스의 0,0 원점에서 멀리 떨어져있는 경우에만 볼 수 있습니다.

저는 캔버스의 RenderTransform을 사용하여 번역을 시도했으며 크기가 조정 된 캔버스 위에 anothercanvas를 말 그대로 옮겨 봤습니다. 나는 다른 누군가의 샘플 앱 온라인에서도 같은 문제를 겪어왔다.

다음 예제 코드는 두 개의 다른 줌 위치에서 패닝 (클릭 및 드래그)을 제공합니다. 코드를 구현하면 버튼 하나를 클릭하여 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; 
     } 

    } 
} 

답변

1

. 고급 렌더링을 위해서는 잘 수행되지 않습니다. 당신은 Visual 클래스를 사용해야합니다 Insted. 조금 더 어렵지만 낮은 레벨 렌더링의 장점을 얻습니다. 여기

Solution download

코드이다 MainWindow.xaml

<Window x:Class="VisualTest.MainWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="VisualLayer" Height="350.4" Width="496.8" 
xmlns:local="clr-namespace:VisualTest" 
> 
<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="Auto"></ColumnDefinition> 
     <ColumnDefinition></ColumnDefinition> 
    </Grid.ColumnDefinitions> 

    <StackPanel Orientation="Vertical"> 
     <Button Name="button1" Click="button1_Click" Margin="5" Padding="5,0"> 
      Zoom WAY in to 0,0 
     </Button> 
     <Button Name="button2" Click="button2_Click" Margin="5" Padding="5,0"> 
      Zoom WAY in to 200, 200 
     </Button> 
     <Button Name="button3" Click="button3_Click" Margin="5" Padding="5,0"> 
      Zoom back 
     </Button> 
    </StackPanel> 
    <local:DrawingCanvas Grid.Column="1" x:Name="drawingSurface" Background="White" ClipToBounds="True" 
         MouseLeftButtonDown="drawingSurface_MouseLeftButtonDown" 
      MouseLeftButtonUp="drawingSurface_MouseLeftButtonUp" 
      MouseMove="drawingSurface_MouseMove"> 
    </local:DrawingCanvas> 
</Grid> 

MainWindow.xaml.cs를

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 VisualTest 
{ 
/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    // Variables for dragging shapes. 
    private bool isDragging = false; 
    private Vector clickOffset; 
    private DrawingVisual selectedVisual; 
    // Drawing constants. 
    private Brush drawingBrush = Brushes.Black; 
    private Brush selectedDrawingBrush = Brushes.LightGoldenrodYellow; 
    private Pen drawingPen = new Pen(Brushes.SteelBlue, 3); 
    private Size squareSize = new Size(10, 10); 

    public MainWindow() 
    { 
     InitializeComponent(); 
     DrawingVisual v = new DrawingVisual(); 
     DrawSquare(v, new Point(0, 0)); 
     drawingSurface.AddVisual(v); 
     v = new DrawingVisual(); 
     DrawSquare(v, new Point(200, 200)); 
     drawingSurface.AddVisual(v); 
    } 

    private void button1_Click(object sender, RoutedEventArgs e) 
    { 
     TransformGroup tg = new TransformGroup(); 
     double scale = 1000000; 
     double xCenter = 0; 
     double yCenter = 0; 
     double xOffset = (drawingSurface.ActualHeight/2.0 - xCenter); 
     double yOffset = (drawingSurface.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); 
     drawingSurface.RenderTransform = st; 
    } 

    private void button2_Click(object sender, RoutedEventArgs e) 
    { 
     TransformGroup tg = new TransformGroup(); 
     double scale = 1000000; 
     double xCenter = 200; 
     double yCenter = 200; 
     double xOffset = (drawingSurface.ActualHeight/2.0 - xCenter); 
     double yOffset = (drawingSurface.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); 
     drawingSurface.RenderTransform = st; 
    } 

    private void button3_Click(object sender, RoutedEventArgs e) 
    { 
     ScaleTransform st = new ScaleTransform(1, 1); 
     drawingSurface.RenderTransform = st; 
    } 

    private void drawingSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
    { 
     Point pointClicked = e.GetPosition(drawingSurface); 
     DrawingVisual visual = drawingSurface.GetVisual(pointClicked); 
     if (visual != null) 
     { 
      // Calculate the top-left corner of the square. 
      // This is done by looking at the current bounds and 
      // removing half the border (pen thickness). 
      // An alternate solution would be to store the top-left 
      // point of every visual in a collection in the 
      // DrawingCanvas, and provide this point when hit testing. 
      Point topLeftCorner = new Point(
       visual.ContentBounds.TopLeft.X , 
       visual.ContentBounds.TopLeft.Y); 
      DrawSquare(visual, topLeftCorner); 

      clickOffset = topLeftCorner - pointClicked; 
      isDragging = true; 

      if (selectedVisual != null && selectedVisual != visual) 
      { 
       // The selection has changed. Clear the previous selection. 
       ClearSelection(); 
      } 
      selectedVisual = visual; 
     }   
    } 

    // Rendering the square. 
    private void DrawSquare(DrawingVisual visual, Point topLeftCorner) 
    { 
     using (DrawingContext dc = visual.RenderOpen()) 
     { 
      Brush brush = drawingBrush; 
      dc.DrawRectangle(brush, null, 
       new Rect(topLeftCorner, squareSize)); 
     } 
    } 

    private void drawingSurface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
    { 
     isDragging = false; 
    } 

    private void ClearSelection() 
    { 
     Point topLeftCorner = new Point(
        selectedVisual.ContentBounds.TopLeft.X , 
        selectedVisual.ContentBounds.TopLeft.Y); 
     DrawSquare(selectedVisual, topLeftCorner); 
     selectedVisual = null; 
    } 

    private void drawingSurface_MouseMove(object sender, MouseEventArgs e) 
    { 
     if (isDragging) 
     { 
      Point pointDragged = e.GetPosition(drawingSurface) + clickOffset; 
      DrawSquare(selectedVisual, pointDragged); 
     } 
    } 
} 
} 

DrawingCanvas.cs

012,379,

끌기 시스템을 다시 작성해야합니다. 아이디어는 간단하지만 구현은 약간 복잡합니다.

+0

답장을 보내 주셔서 감사합니다. 이 제안 된 솔루션에는 두 가지 문제점이 있습니다. 첫째, 특정 시각적 항목을 드래그하려고하지 않고 전체 캔버스를 패닝 (캔버스의 모든 비주얼을 이동)하려고합니다. 실제 예에서, 확대하면 많은 시각적 객체가있는 세부 묘사가 나타납니다. 캔버스 주위를 움직이는 것처럼 그 물체를 둘러 봐야합니다. 둘째, 200,200에서의 드래그 효과가 여전히 원활하지 않다는 것이 두려워요. 그것을 확인하십시오 - 당신은 광장을 움켜 잡을 수 있습니다, 그러나 당신은 당신의 마우스를 조금 드래그해야합니다. 그리고 그 곳으로 점프하고, 조금 더 드래그하면 다시 점프합니다. – FTLPhysicsGuy

+0

그것은 내 랩탑에서 부드럽습니다. 속도의 악마 : 두 번째 생각에 많은 요소 (지구 전체 egzample)가있는 1mln zoom + paning 전체 캔버스는 불가능합니다. ScaleTransform은 1000 개 이상의 IMO가 될 수 없습니다. 더 많은 것은 200.200을 드래그 할 때 0.0 정사각형을 변형하고 렌더링 할 필요가 없다는 것입니다.화면에 보이는 것을 40 %, 50 %의 부드러운 패닝으로 렌더링해야하며, Certian 레벨은 확대/축소 중에 동적으로로드해야합니다. 즉, 보는 것을 렌더링하고 조금 더 렌더링하십시오. 그러한 용기를 만드는 것은 많은 일입니다. –