silverlight 中 MultiScaleImage 与 Canvas绑定

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.

<UserControl x:Class="DeepZoomCanvas.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:DesignWidth="640" d:DesignHeight="480">
 <Grid x:Name="LayoutRoot">
 <MultiScaleImage x:Name="msi" Source=" http://www.uslot.com/ClientBin/dzc_output.xml"/>
 <Grid
 IsHitTestVisible="False"
 x:Name="overlay">
 <Grid.RenderTransform>
 <TransformGroup>
 <ScaleTransform x:Name="overlayScale"/>
 <TranslateTransform x:Name="overlayTranslate"/>
 </TransformGroup>
 </Grid.RenderTransform>
 <Border
 CornerRadius="8"
 Background="#44FFFFFF"
 VerticalAlignment="Center"
 HorizontalAlignment="Center"
 Padding="30">
 <StackPanel>
 <TextBlock
 FontFamily="Arial"
 FontSize="24"
 MaxWidth="400"
 FontWeight="Bold"
 TextWrapping="Wrap">
 This shows some elements which
 scale and translate locked to
 the underlying MultiScaleImage.
 Any layout items can be used here.
 </TextBlock>
 <Border Background="#55FF0000" Padding="10" HorizontalAlignment="Center" VerticalAlignment="Top">
 <StackPanel>
 <TextBlock
 FontFamily="Arial"
 FontSize="0.1"
 >This is a tiny line of text which still scales in concert with the deep zoom image.</TextBlock>
 </StackPanel>
 </Border>
 </StackPanel>
 </Border>
 </Grid>
 </Grid>
</UserControl>
01<UserControl x:Class="DeepZoomCanvas.MainPage"
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:

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DeepZoomCanvas
{
 public partial class MainPage : UserControl
 {
 public MainPage()
 {
 InitializeComponent();
 msi.MouseLeftButtonDown += new MouseButtonEventHandler(msi_MouseLeftButtonDown);
 msi.MouseMove += new MouseEventHandler(msi_MouseMove);
 msi.MouseLeftButtonUp += new MouseButtonEventHandler(msi_MouseLeftButtonUp);
 msi.MouseWheel += new MouseWheelEventHandler(msi_MouseWheel);

 // Track changes to the multi scale image
 msi.ViewportChanged += new RoutedEventHandler(msi_ViewportChanged);
 }

 /// <summary>
 /// This is the code which locks the overlay to the underlying deep zoom image.
 /// All it really does is set the scale factor and offset of the overlay
 /// based on the current setting of the deep zoom image.
 /// </summary>
 ///
<param name="sender">event sender</param>
 ///
<param name="e">event args</param>
 void msi_ViewportChanged(object sender, RoutedEventArgs e)
 {
 // This event is called during animations of the image.
 // Match the scaling of the canvas with the image
 Point viewportOrigin = msi.ViewportOrigin;
 double viewportWidth = msi.ViewportWidth;

 // The scale factor is just the inverse of the ViewportWidth
 overlayScale.ScaleX = 1 / viewportWidth;
 overlayScale.ScaleY = 1 / viewportWidth;

 // The offset is calculated by finding the location of the origin of the dzi
 // in element coordinates.
 Point newO = LogicalToElement(new Point(), viewportOrigin, viewportWidth);
 overlayTranslate.X = newO.X;
 overlayTranslate.Y = newO.Y;
 }

 private Point LogicalToElement(Point p, Point Origin, double Width)
 {
 return new Point(((p.X - Origin.X) / Width) * msi.ActualWidth,
 ((p.Y - Origin.Y) / Width) * msi.ActualWidth);
 }

 public Point ElementToLogical(Point p, Point Origin, double Width)
 {
 return new Point(Origin.X + (p.X * Width) / msi.ActualWidth,
 Origin.Y + (p.Y * Width) / msi.ActualWidth);
 }

#region Mouse handling
 void msi_MouseWheel(object sender, MouseWheelEventArgs e)
 {
 if (e.Delta < 0)
 {
 Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
 msi.ZoomAboutLogicalPoint(0.8, logicalPoint.X, logicalPoint.Y);

 }
 else
 {
 Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
 msi.ZoomAboutLogicalPoint(1.2, logicalPoint.X, logicalPoint.Y);
 }
 }

 void msi_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
 {
 if (IsMouseDown)
 {
 msi.ReleaseMouseCapture();
 if (IsDrag == false)
 {
 if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
 {
 Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
 msi.ZoomAboutLogicalPoint(0.8, logicalPoint.X, logicalPoint.Y);
 }
 else
 {
 Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
 msi.ZoomAboutLogicalPoint(1.2, logicalPoint.X, logicalPoint.Y);
 }
 }
 IsMouseDown = false;
 IsDrag = false;
 }
 }

 void msi_MouseMove(object sender, MouseEventArgs e)
 {
 lastMousePos = e.GetPosition(msi);
 if (IsMouseDown)
 {
 IsDrag = true;
 }
 if (IsDrag)
 {
 Point newPoint = lastMouseViewPort;
 newPoint.X += (lastMouseDownPos.X - lastMousePos.X) / msi.ActualWidth * msi.ViewportWidth;
 newPoint.Y += (lastMouseDownPos.Y - lastMousePos.Y) / msi.ActualWidth * msi.ViewportWidth;
 msi.ViewportOrigin = newPoint;
 }
 }

 bool IsMouseDown = false;
 bool IsDrag = false;
 private Point lastMouseDownPos = new Point();
 private Point lastMouseViewPort = new Point();
 private Point lastMousePos = new Point();

 void msi_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
 IsMouseDown = true;
 msi.CaptureMouse();
 lastMouseDownPos = e.GetPosition(msi);
 lastMouseViewPort = msi.ViewportOrigin;
 }

 #endregion    
 }
}
001using System;
002using System.Collections.Generic;
003using System.Linq;
004using System.Net;
005using System.Windows;
006using System.Windows.Controls;
007using System.Windows.Documents;
008using System.Windows.Input;
009using System.Windows.Media;
010using System.Windows.Media.Animation;
011using System.Windows.Shapes;
012  
013namespace 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 ///
035<param name="sender">event sender</param>
036 ///
037<param name="e">event args</param>
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(), viewportOrigin, viewportWidth);
052 overlayTranslate.X = newO.X;
053 overlayTranslate.Y = newO.Y;
054 }
055  
056 private Point LogicalToElement(Point p, Point Origin, 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, Point Origin, 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.X, logicalPoint.Y);
075  
076 }
077 else
078 {
079 Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
080 msi.ZoomAboutLogicalPoint(1.2, logicalPoint.X, 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.X, logicalPoint.Y);
095 }
096 else
097 {
098 Point logicalPoint = msi.ElementToLogicalPoint(lastMousePos);
099 msi.ZoomAboutLogicalPoint(1.2, logicalPoint.X, 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.

 

 

2 From http://blogs.msdn.com/b/lutzg/archive/2008/08/19/synchronizing-images-with-the-deep-zoom-content.aspx

 

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值