1 from http://jimlynn.wordpress.com/2009/09/17/scale-other-content-on-top-of-a-deep-zoom-image/
Marthinus asked,in a comment:
“is there ANY way to overwrite this,so that I can make the msi use a multiscalesubimage WITH an extra canvas ontop (which would then move and scale WITH the subimage)?”
Now,I’ve never done overlays with collections and sub-images,and one thing you definitely can’t do is interleave other content with subimages of a deep zoom image (since the MultiScaleImage is a single UI element). But I have done things which have locked an overlay panel to the movement of the MultiScaleImage. Here’s a sample. First,the Xaml.
01 |
< UserControl x:Class = "DeepZoomCanvas.MainPage"
|
03 |
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
|
04 |
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
|
05 |
mc:Ignorable = "d" d:DesignWidth = "640" d:DesignHeight = "480" >
|
06 |
< Grid x:Name = "LayoutRoot" >
|
07 |
< MultiScaleImage x:Name = "msi" Source = "http://www.uslot.com/ClientBin/dzc_output.xml" />
|
08 |
< Grid
|
09 |
IsHitTestVisible = "False"
|
10 |
x:Name = "overlay" >
|
11 |
< Grid.RenderTransform >
|
12 |
< TransformGroup >
|
13 |
< ScaleTransform x:Name = "overlayScale" />
|
14 |
< TranslateTransform x:Name = "overlayTranslate" />
|
15 |
</ TransformGroup >
|
16 |
</ Grid.RenderTransform >
|
17 |
< Border
|
18 |
CornerRadius = "8"
|
19 |
Background = "#44FFFFFF"
|
20 |
VerticalAlignment = "Center"
|
21 |
HorizontalAlignment = "Center"
|
22 |
Padding = "30" >
|
23 |
< StackPanel >
|
24 |
< TextBlock
|
25 |
FontFamily = "Arial"
|
26 |
FontSize = "24"
|
27 |
MaxWidth = "400"
|
28 |
FontWeight = "Bold"
|
29 |
textwrapping = "Wrap" >
|
30 |
This shows some elements which
|
31 |
scale and translate locked to
|
32 |
the underlying MultiScaleImage.
|
33 |
Any layout items can be used here.
|
34 |
</ TextBlock >
|
35 |
< Border Background = "#55FF0000" Padding = "10" HorizontalAlignment = "Center" VerticalAlignment = "Top" >
|
36 |
< StackPanel >
|
37 |
< TextBlock
|
38 |
FontFamily = "Arial"
|
39 |
FontSize = "0.1"
|
40 |
>This is a tiny line of text which still scales in concert with the deep zoom image.</ TextBlock >
|
41 |
</ StackPanel >
|
42 |
</ Border >
|
43 |
</ StackPanel >
|
44 |
</ Border >
|
45 |
</ Grid >
|
46 |
</ Grid >
|
47 |
</ UserControl >
|
And the corresponding C# code:
001 |
using System;
|
002 |
using System.Collections.Generic;
|
003 |
using System.Linq;
|
004 |
using System.Net;
|
005 |
using System.Windows;
|
006 |
using System.Windows.Controls;
|
007 |
using System.Windows.Documents;
|
008 |
using System.Windows.Input;
|
009 |
using System.Windows.Media;
|
010 |
using System.Windows.Media.Animation;
|
011 |
using System.Windows.Shapes;
|
012 |
|
013 |
namespace DeepZoomCanvas
|
014 |
{ |
015 |
public partial class MainPage : UserControl
|
016 |
{
|
017 |
public MainPage()
|
018 |
{
|
019 |
InitializeComponent();
|
020 |
msi.MouseLeftButtonDown += new MouseButtonEventHandler(msi_MouseLeftButtonDown);
|
021 |
msi.MouseMove += new MouseEventHandler(msi_MouseMove);
|
022 |
msi.MouseLeftButtonUp += new MouseButtonEventHandler(msi_MouseLeftButtonUp);
|
023 |
msi.MouseWheel += new MouseWheelEventHandler(msi_MouseWheel);
|
024 |
|
025 |
// Track changes to the multi scale image
|
026 |
msi.ViewportChanged += new RoutedEventHandler(msi_ViewportChanged);
|
027 |
}
|
028 |
|
029 |
/// <summary>
|
030 |
/// This is the code which locks the overlay to the underlying deep zoom image.
|
031 |
/// All it really does is set the scale factor and offset of the overlay
|
032 |
/// based on the current setting of the deep zoom image.
|
033 |
/// </summary>
|
034 |
///
|
036 |
///
|
038 |
void msi_ViewportChanged( object sender,RoutedEventArgs e)
|
039 |
{
|
040 |
// This event is called during animations of the image.
|
041 |
// Match the scaling of the canvas with the image
|
042 |
Point viewportOrigin = msi.ViewportOrigin;
|
043 |
double viewportWidth = msi.ViewportWidth;
|
044 |
|
045 |
// The scale factor is just the inverse of the ViewportWidth
|
046 |
overlayScale.ScaleX = 1 / viewportWidth;
|
047 |
overlayScale.ScaleY = 1 / viewportWidth;
|
048 |
|
049 |
// The offset is calculated by finding the location of the origin of the dzi
|
050 |
// in element coordinates.
|
051 |
Point newO = LogicaltoElement( new Point(),viewportWidth);
|
052 |
overlayTranslate.X = newO.X;
|
053 |
overlayTranslate.Y = newO.Y;
|
054 |
}
|
055 |
|
056 |
private Point LogicaltoElement(Point p, double Width)
|
057 |
{
|
058 |
return new Point(((p.X - Origin.X) / Width) * msi.ActualWidth,
|
059 |
((p.Y - Origin.Y) / Width) * msi.ActualWidth);
|
060 |
}
|
061 |
|
062 |
public Point ElementToLogical(Point p, double Width)
|
063 |
{
|
064 |
return new Point(Origin.X + (p.X * Width) / msi.ActualWidth,
|
065 |
Origin.Y + (p.Y * Width) / msi.ActualWidth);
|
066 |
}
|
067 |
|
068 |
#region Mouse handling |
069 |
void msi_MouseWheel( object sender,MouseWheelEventArgs e)
|
070 |
{
|
071 |
if (e.Delta < 0)
|
072 |
{
|
073 |
Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
|
074 |
msi.ZoomAboutLogicalPoint(0.8,logicalPoint.Y);
|
075 |
|
076 |
}
|
077 |
else
|
078 |
{
|
079 |
Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
|
080 |
msi.ZoomAboutLogicalPoint(1.2,logicalPoint.Y);
|
081 |
}
|
082 |
}
|
083 |
|
084 |
void msi_MouseLeftButtonUp( object sender,MouseButtonEventArgs e)
|
085 |
{
|
086 |
if (IsMouseDown)
|
087 |
{
|
088 |
msi.ReleaseMouseCapture();
|
089 |
if (IsDrag == false )
|
090 |
{
|
091 |
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
|
092 |
{
|
093 |
Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
|
094 |
msi.ZoomAboutLogicalPoint(0.8,logicalPoint.Y);
|
095 |
}
|
096 |
else
|
097 |
{
|
098 |
Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
|
099 |
msi.ZoomAboutLogicalPoint(1.2,logicalPoint.Y);
|
100 |
}
|
101 |
}
|
102 |
IsMouseDown = false ;
|
103 |
IsDrag = false ;
|
104 |
}
|
105 |
}
|
106 |
|
107 |
void msi_MouseMove( object sender,MouseEventArgs e)
|
108 |
{
|
109 |
lastMousePos = e.GetPosition(msi);
|
110 |
if (IsMouseDown)
|
111 |
{
|
112 |
IsDrag = true ;
|
113 |
}
|
114 |
if (IsDrag)
|
115 |
{
|
116 |
Point newPoint = lastMouseViewPort;
|
117 |
newPoint.X += (lastMouseDownPos.X - lastMousePos.X) / msi.ActualWidth * msi.ViewportWidth;
|
118 |
newPoint.Y += (lastMouseDownPos.Y - lastMousePos.Y) / msi.ActualWidth * msi.ViewportWidth;
|
119 |
msi.ViewportOrigin = newPoint;
|
120 |
}
|
121 |
}
|
122 |
|
123 |
bool IsMouseDown = false ;
|
124 |
bool IsDrag = false ;
|
125 |
private Point lastMouseDownPos = new Point();
|
126 |
private Point lastMouseViewPort = new Point();
|
127 |
private Point lastMousePos = new Point();
|
128 |
|
129 |
void msi_MouseLeftButtonDown( object sender,MouseButtonEventArgs e)
|
130 |
{
|
131 |
IsMouseDown = true ;
|
132 |
msi.CaptureMouse();
|
133 |
lastMouseDownPos = e.GetPosition(msi);
|
134 |
lastMouseViewPort = msi.ViewportOrigin;
|
135 |
}
|
136 |
|
137 |
#endregion
|
138 |
}
|
139 |
} |
All the work is done in the ViewportChanged event handler (which fires every time the viewport changes,even during animations). I’ve set a scale transform and a translate transform on the grid (which Could be a canvas if you want) and we adjust the scale and translate values to match the underlying image.
All the rest of the code is just bog-standard deep zoom image mouse handling.
Try zooming into the red square to see a very small line of text.
Hope this helps.
Synchronizing other content with Deep Zoom
When you set ViewportOrigin or ViewportWidth (or use the ZoomAboutPoint helper),deep zoom simply kicks off an animation to the new location. This animation happens entirely under the hood and is transparent to the developer. That's very nice for the dead simple zooming application,since the developer doesn't have to worry about making pretty animations when zooming and panning. Pretty animations are built in.
However,this behavior does cause a problem if you want to make other things in Silverlight move along with zooming and panning of the Deep Zoom image.
So what's the trick? Everytime a user pans or zooms,this application sets of a 0-length-storyboard that tracks what Deep Zoom is doing,and adjust size and position for the pins. The 0-length-storyboard does its thing for 1500 ms,which is exactly the amount of time Deep Zoom allocates for each animation.
Using powerlaw scaling
Another nifty little feature that was put into this application is powerlaw scaling. The size of the pins only grows at a fraction of the rate of the rest of the map. This needs to be done,since if the size of the pins is kept constant,our eyes fall victim to an optical illusion: when zooming in,the pins would look like they are shrinking,and when zooming out,the appear to be growing.Power-law scaling makes this visual effect much less noticeable.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。