每周源代码18 – Deep Zoom(SeaDragon)Silverlight 2 MultiScaleImage鼠标滚轮缩放及平移

[原文发表地址]  The Weekly Source Code 18 - Deep Zoom (Seadragon) Silverlight 2 MultiScaleImage Mouse Wheel Zooming and Panning Edition

[原文发表时间] 2008-03-08 04:14

亲爱的读者,请阅读我的系列博文《每周源代码》的第十八篇。以下是我本周在Mix阅读及编写的代码。

我在研究Silverlight 2中的Deep Zoom,却发现居然没有支持鼠标滑轮框外的平移,缩放功能的“Hello DeepZoom World!”的样本这简直太令人讨厌了。而Vertigo的样本示例则和我想要的效果一样。你可以在这里查看我做的Deep Zoom示例样本,或者点击右边的图片。

Silverlight Project Test Page - Windows Internet Explorer (4)我们可以通过好几种方法让Silverlight支持鼠标滑轮。鼠标滑轮事件源于浏览器而非Silverlight本身(在我看来,这正如同你希望的那样,因为Silverlight内置于浏览器,它不能取代其特性)。

所以,你可以使用Adomas的Javascript代码,辅以Jeff Prosise编写好的代码进入Silverlight,调用方法。事件会在JavaScript中处理,Zoom函数也通过JavaScript的桥调用,转换成Silverlight的托管代码。

当然,你也可以调用内部托管代码,为DOM(JavaScript事件)设置管理处理,就像Pete Blois使用鼠标滑轮帮助器类进行操作那样。我直接从Pete的博客上下载了这个类并将其添加到了我的项目中。这非常棒,无需任何外部JavaScript文件。所有的事件都是由托管代码处理的。

    1: if (HtmlPage.IsEnabled) {
    2: HtmlPage.Window.AttachEvent("DOMMouseScroll", this.HandleMouseWheel);
    3: HtmlPage.Window.AttachEvent("onmousewheel", this.HandleMouseWheel);
    4: HtmlPage.Document.AttachEvent("onmousewheel", this.HandleMouseWheel);
    5: }

此外,我还在Yasser博客中Yasser MakramJohn的评论里截取了我最需要的代码段。

我看过很多用鼠标点击或者用键盘下键来实现缩放效果的示例,但我还想支持鼠标滑轮事件,就像在Mix中显示的那样。

这个更完整的样本将提供你:

• 拖动以平移

• 点击以放大,Shift点击以缩小

• 用鼠标滑轮来进行缩放

• 无需依靠JavaScript – 所有操作都是在管理代码中完成的。

首先,从DeepZoom的输出编辑器开始(我用这台机器上的Windows Wallpapers在编辑器中编写DeepZoom图像),然后将结果导出的文件夹结构拷贝至某处(为方便起见,我把它放在bin/debug下,不过你可以随意,只要源属性在XAML中整齐地排列):

    1: <UserControl
    2: xmlns="https://schemas.microsoft.com/client/2007"
    3: xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    4: x:Class="SilverlightApplication1.Page"
    5: Width="800" Height="600" >
    6:  
    7: <Grid
    8:  
    9: x:Name="LayoutRoot"
   10: Background="Gray">
   11: <MultiScaleImage 
   12: x:Name="msi"
   13: ViewportWidth="1.0"
   14: Source="https://www.yourdomain.com/foo/items.bin" />
   15: </Grid>
   16: </UserControl>

注意事项:你可以在MultiScaleImage上设置一个很酷的转换。设置UseSprings="false",它可以关掉缩放的动画效果。为什么要这么操作呢?因为特定的放大缩小动画让DeepZoom有机会能展示它的轻巧视觉操控和图像间的转换。当动画产生时,你不太会注意到平铺视图间的转换。当然,我很想搞清楚这一切到底是怎么实现的,所以我很喜欢看其中的转换。

你要知道现在是早上4点,所以这个代码有点儿靠不住,考虑也许不周全,因为我只在上面花了一个小时的时间。我想知道读者怎样利用这段代码,将其改进为一个经典的范本。这个示例还不够完善,我其实有点迟疑要不要把它贴在这里,因为它很杂乱,不过这也很有趣,不是吗?至少我觉得它行之有效。

有一个需要指出的是, control的名称是“msi”,它通过x:name=”msi”在上述的XAML中设定,这样你就可以看到我引用属性,比如下面XAML代码中的msi.thisandthat。还有,下面的“using akadia”是从我页面引用的Pete的MouseHandler代码的命名空间。

我的代码通过匿名委托连接了一连串constructor里的事件,它们一同调用了单一Zoom()辅助方法。

    1: using System; 
    2: using System.Windows; 
    3: using System.Windows.Controls; 
    4: using System.Windows.Documents; 
    5: using System.Windows.Ink; 
    6: using System.Windows.Input; 
    7: using System.Windows.Media; 
    8: using System.Windows.Media.Animation; 
    9: using System.Windows.Shapes; 
   10: using System.Windows.Threading; 
   11: using akadia; 
   12:  
   13: namespace SilverlightApplication1 
   14: { 
   15: public partial class Page : UserControl 
   16: { 
   17: Point lastMousePos = new Point(); 
   18:  
   19: double _zoom = 1; 
   20: bool mouseButtonPressed = false; 
   21: bool mouseIsDragging = false; 
   22: Point dragOffset; 
   23: Point currentPosition; 
   24:  
   25: public double ZoomFactor 
   26: { 
   27: get { return _zoom; } 
   28: set { _zoom = value; } 
   29: } 
   30:  
   31: public Page() 
   32: { 
   33: this.InitializeComponent(); 
   34:  
   35: this.MouseMove += delegate(object sender, MouseEventArgs e) 
   36: { 
   37: if (mouseButtonPressed) 
   38: { 
   39: mouseIsDragging = true; 
   40: } 
   41:  
   42: this.lastMousePos = e.GetPosition(this.msi);
   43:  
   44: }; 
   45:  
   46: this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e) 
   47:  
   48: { 
   49:  
   50: mouseButtonPressed = true; 
   51: mouseIsDragging = false; 
   52: dragOffset = e.GetPosition(this); 
   53: currentPosition = msi.ViewportOrigin; 
   54:  
   55: }; 
   56:  
   57: this.msi.MouseLeave += delegate(object sender, MouseEventArgs e) 
   58: { 
   59: mouseIsDragging = false; 
   60:  
   61: }; 
   62:  
   63: this.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e) 
   64: { 
   65: mouseButtonPressed = false; 
   66: if (mouseIsDragging == false) 
   67: { 
   68: bool shiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift; 
   69:  
   70: ZoomFactor = 2.0; 
   71: if(shiftDown) ZoomFactor = 0.5; //back out when shift is down 
   72: Zoom(ZoomFactor, this.lastMousePos); 
   73:  
   74: } 
   75:  
   76: mouseIsDragging = false; 
   77:  
   78: }; 
   79:  
   80: this.MouseMove += delegate(object sender, MouseEventArgs e) 
   81: { 
   82: if (mouseIsDragging) 
   83: { 
   84:  
   85: Point newOrigin = new Point(); 
   86: newOrigin.X = currentPosition.X - (((e.GetPosition(msi).X - dragOffset.X) / msi.ActualWidth) * msi.ViewportWidth); 
   87: newOrigin.Y = currentPosition.Y - (((e.GetPosition(msi).Y - dragOffset.Y) / msi.ActualHeight) * msi.ViewportWidth); 
   88: msi.ViewportOrigin = newOrigin; 
   89: } 
   90: }; 
   91:  
   92: new MouseWheelHelper(this).Moved += delegate(object sender, MouseWheelEventArgs e) 
   93: { 
   94:  
   95: e.Handled = true; 
   96: if (e.Delta > 0) 
   97: ZoomFactor = 1.2; 
   98: else 
   99: ZoomFactor = .80; 
  100:  
  101: Zoom(ZoomFactor, this.lastMousePos); 
  102:  
  103: }; 
  104:  
  105: } 
  106:  
  107: public void Zoom(double zoom, Point pointToZoom) 
  108:  
  109: { 
  110:  
  111: Point logicalPoint = this.msi.ElementToLogicalPoint(pointToZoom); 
  112: this.msi.ZoomAboutLogicalPoint(zoom, logicalPoint.X, logicalPoint.Y); 
  113:  
  114: } 
  115:  
  116: } 
  117:  
  118: }

我也碰到一个麻烦,不过因为我还没安装Silverlight 2 Beta 1工具安装程序Silverlight 2 Beta 1工具安装程序故障排除),我还没在调试器上试过。我刚在Expression Blend 2.5和Notepad2中创建了它。我太享受这过程了,都不想停下来去安装其他东西了。

  • 我发现当我应该用一个absolute factor的时候,我却用了相对计算的ZoomFactor。
  • 第三,我想避免图像平移出可视范围(就是说我不想大家迷失方向),而且我希望能给缩小放大设置合理的上限/下限。
  • 不要忘了在主机IIS上将 .xap文件设置为mime 类型应用 / x-silverlight-app

这一切实在太棒了,而且也不难。我也会在上午再琢磨下还有没有其他办法实现此操作。