2010-10-16 5 views
2

Я реализовал следующий Silverlight приложения после просмотра this instructions, вот мой код:Держите относительное положение над MultiScaleImage

public partial class MainPage : UserControl 
{ 
    private Point lastMousePos = new Point(); 
    private double zoom = 1; 
    private Point lastMouseLogicaPos = new Point(); 
    private Point lastMouseViewPort = new Point(); 
    private bool duringDrag = false; 
    private bool duringOpen = false; 
    private List<Dot> dots = new List<Dot>(); 
    private bool addDot = false; 

    public MainPage() 
    { 
     InitializeComponent(); 

     this.MouseMove += delegate(object sender, MouseEventArgs e) 
     { this.lastMousePos = e.GetPosition(this.ZoomImage); }; 

     ZoomImage.MouseWheel += new MouseWheelEventHandler(ZoomImage_MouseWheel); 
     this.ZoomImage.UseSprings = false; 
    } 

    private void ZoomImage_MouseWheel(object sender, MouseWheelEventArgs e) 
    { 
     double newzoom = zoom; 

     if (e.Delta > 0) 
     { newzoom /= 1.3; } 
     else 
     { newzoom *= 1.3; } 

     Point logicalPoint = this.ZoomImage.ElementToLogicalPoint(this.lastMousePos); 
     this.ZoomImage.ZoomAboutLogicalPoint(zoom/newzoom, logicalPoint.X, logicalPoint.Y); 

     zoom = newzoom; 
     e.Handled = true; 
    } 

    private void ZoomImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     lastMouseLogicaPos = e.GetPosition(LayoutRoot); 
     lastMouseViewPort = this.ZoomImage.ViewportOrigin; 

     foreach (var dot in this.dots) 
     { dot.LastMouseLogicPos = e.GetPosition(LayoutRoot); } 

     if (!this.addDot) 
     { duringDrag = true; } 
    } 

    private void ZoomImage_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     if (this.addDot) 
     { 
      Dot dot = new Dot(this.lastMouseLogicaPos.X, this.lastMouseLogicaPos.Y) 
           { Name = "Dot" + (this.dots.Count + 1).ToString() }; 

      this.dots.Add(dot); 
      this.DotCanvas.Children.Add(dot); 
     } 
     else 
     { duringDrag = false; } 
    } 

    private void ZoomImage_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) 
    { 
     if (duringDrag) 
     { 
      double zoomFactor = 1/this.ZoomImage.ViewportWidth; 
      Point newPoint = lastMouseViewPort; 
      Point thisMouseLogicalPos = e.GetPosition(LayoutRoot); 
      newPoint.X += (lastMouseLogicaPos.X - thisMouseLogicalPos.X)/(this.ZoomImage.ActualWidth * zoomFactor); 
      newPoint.Y += (lastMouseLogicaPos.Y - thisMouseLogicalPos.Y)/(this.ZoomImage.ActualWidth * zoomFactor); 
      this.ZoomImage.ViewportOrigin = newPoint; 

      foreach (var dot in this.dots) 
      { 
       Point dotLogicPoint = this.ZoomImage.ElementToLogicalPoint(new Point(dot.X, dot.Y)); 
       thisMouseLogicalPos = e.GetPosition(LayoutRoot); 

       dotLogicPoint.X -= (dot.LastMouseLogicPos.X - thisMouseLogicalPos.X)/((1/1.8) * this.ZoomImage.ViewportWidth); 
       dotLogicPoint.Y -= (dot.LastMouseLogicPos.Y - thisMouseLogicalPos.Y)/(this.ZoomImage.ActualWidth * this.ZoomImage.ViewportWidth); 

       dot.X = (this.ZoomImage.LogicalToElementPoint(locLogicPoint).X); 
       dot.Y = (this.ZoomImage.LogicalToElementPoint(locLogicPoint).Y); 
      } 
     } 
    } 

    private void ZoomImage_ImageOpenSucceeded(object sender, System.Windows.RoutedEventArgs e) 
    { duringOpen = true; } 

    private void ZoomImage_MotionFinished(object sender, System.Windows.RoutedEventArgs e) 
    { 
     if (duringOpen) 
     { duringOpen = false; } 
    } 

    private void Button_Click(object sender, System.Windows.RoutedEventArgs e) 
    { 
     this.addDot = !this.addDot; 

     if (this.addDot) 
     { this.btnAddDot.Content = "Click on Image"; } 
     else 
     { this.btnAddDot.Content = "Add Dot"; } 
    } 
} 

С этим я могу увеличивать и кастрюлю на MultiScaleImage и добавить свой собственный объект Dot на холст DotCanvas. Вот XAML:

<UserControl x:Class="DeepZoomSample.MainPage" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="d" 
d:DesignHeight="300" d:DesignWidth="400" Width="800" Height="600"> 

<Grid x:Name="LayoutRoot" Background="Black" Margin="0,0,-98,-86"> 

    <MultiScaleImage x:Name="ZoomImage" Source="GeneratedImages/dzc_output.xml" 
        Margin="8,8,0,0" MouseLeftButtonDown="ZoomImage_MouseLeftButtonDown" 
        MouseLeftButtonUp="ZoomImage_MouseLeftButtonUp" MouseMove="ZoomImage_MouseMove" ImageOpenSucceeded="ZoomImage_ImageOpenSucceeded" 
        MotionFinished="ZoomImage_MotionFinished" Height="584" VerticalAlignment="Top" HorizontalAlignment="Left" Width="784"/> 

    <Canvas x:Name="DotCanvas" HorizontalAlignment="Left" Height="584" Margin="8" VerticalAlignment="Top" Width="784" MouseLeftButtonUp="LocationCanvas_MouseLeftButtonUp"/> 
    <Button x:Name="btnAddDot" Content="Add Location" HorizontalAlignment="Right" Height="44" Margin="0,0,24,24" VerticalAlignment="Bottom" Width="112" Click="Button_Click"/> 

</Grid> 

Теперь проблема заключается в том, что, так как Dots помещены в холст, что это за (объект ZoomImage) MultiScateImage когда панорамирование и масштабирование многоточие будет оставаться в их соответствующих место на холсте. Этот код имеет некоторые пропущенные попытки, пытаясь сохранить точки на месте, пока изображение панорамируется и масштабируется.

Вот образ приложения, синие точки вокруг мой заказ Dot объект:

alt text

Главный вопрос в том, как я могу держать Многоточие в их относительном месте над изображением в то время как пользовательские масштабирования и сковородки.

+0

Должны ли сами точки масштабироваться по масштабированию или поддерживать их размер и просто иметь относительный масштаб положения и панорамирование? – AnthonyWJones

+0

Я хочу, чтобы они сохранили свой размер и просто изменили свое относительное положение относительно MultiScaleImage при масштабировании и панорамировании. – FelixMM

+0

Это хорошо и избавит вас от серьезных проблем :-). Сохранение размера точек, синхронизированное с «родным» разрешением исходного изображения, даже сложнее, чем сохранение синхронизации позиции. –

ответ

1

Это сложно, но определенно можно сделать, я недавно сделал то же самое для аналогичного приложения. Я не буду лгать, мне потребовалось несколько часов, чтобы заставить его работать, поэтому приготовьтесь к посадке.

Есть в основном две вещи, связанные:

1. позиционирование точки в нужном месте

Math является вашим другом здесь. Вам нужно будет создать некоторые методы, которые переносят координаты на основе multiScaleImage в координаты вашего холста (т. Е. Окна просмотра).

Во-первых, вам нужно будет разобраться в деталях ViewPortOrigin и ViewPortWidth (this - это очень хороший старт). У них есть пара предостережений (например, я, кажется, помню, что viewPortHeight необходимо умножить на коэффициент изображения, чтобы получить фактическое значение или что-то подобное).

Чтобы указать на решение: вам нужно будет вычесть viewPortOrigin и умножить/разделить на viewPortWidth. Если вы будете терпеливы (и повезете ;-)) сегодня вечером, я посмотрю на свой проект и разместил код, но это хорошо, если вы действительно понимаете эти параметры. В противном случае будет сложно отладить и устранить неполадки.

Что-то, что помогло мне понять, что происходит, это разместить некоторые текстовые блоки и отобразить viewportWidth/Origin/etc. все время, перемещаясь в multiscaleImage.

Редактировать: вам повезло, я помнил это, так вот вот какой код, который должен помочь. Опять же, я предлагаю вам не просто скопировать & пасту без понимания, так как вы не доберетесь так далеко.

private Point CanvasToDeepZoom(MultiScaleImage msi, Point absoluteInsideCanvas) 
{ 
    // the only non-logical (to me) step: viewportOrigin.Y must be multiplied by the aspectRatio 
    var ViewportHeight = msi.ViewportWidth * msi.AspectRatio * msi.ActualHeight/msi.ActualWidth; 

    var relativeToCanvas = new Point(
     absoluteInsideCanvas.X/msi.ActualWidth, 
     absoluteInsideCanvas.Y/msi.ActualHeight); 

    return new Point(
     msi.ViewportOrigin.X + msi.ViewportWidth * relativeToCanvas.X, 
     msi.ViewportOrigin.Y * msi.AspectRatio + ViewportHeight * relativeToCanvas.Y); 
} 


private Point DeepZoomToCanvas(MultiScaleImage msi, Point relativeInsideDeepZoom) 
{ 
    var ViewportHeight = msi.ViewportWidth * msi.AspectRatio * msi.ActualHeight/msi.ActualWidth; 

    var relativeToCanvas = new Point(
     (relativeInsideDeepZoom.X - msi.ViewportOrigin.X)/msi.ViewportWidth, 
     (relativeInsideDeepZoom.Y - msi.ViewportOrigin.Y * msi.AspectRatio)/ViewportHeight); 

    return new Point(
     relativeToCanvas.X * msi.ActualWidth, 
     relativeToCanvas.Y * msi.ActualHeight); 
} 

2. сохранение точек в синхронизации во время масштабирования и панорамирования анимации.

Основная идея состоит в том, чтобы зацикливать анимацию, состоящую из 0 секунд, которая постоянно обновляет ваше положение точек на весь период масштабирования/панорамирования (1,5 секунды, если я правильно помню). техника объясняется очень хорошо here.В этом блоге вы также найдете другие полезные ресурсы для вашей конкретной проблемы.

+0

Спасибо за ответ. Я проверю ссылки и код на них последние сегодня. – FelixMM

0

Если вы видите Deep Composer Zoom, он также позволяет вам устанавливать области гиперссылок и иметь шаблоны для создания проектов исходного кода. Таким образом, точки могут быть самими изображениями (используя коллекцию изображений, чтобы вы могли даже включать/отключать субсайты [SubMultiScaleImage, если я хорошо помню класс] программно) и иметь гиперссылки на них и позволить MultiScaleImage обрабатывать коллекцию и сообщать вам, когда гиперссылки нажимаются (см. сгенерированный код)