代码改变世界

迎接Multitouch时代系列之--传统的操作体验

2010-03-21 13:37  姜 萌@cnblogs  阅读(444)  评论(0编辑  收藏  举报

本篇实际上就是MSDN上HOL的实现。不过一开始没找到那个HOL源码下载,于是自己做了一个,改了些功能和设计。本身并不复杂,程序的效果就是可以在窗口的一个区域上添加各种RenderTransform样式图片图片,用户能够通过鼠标对每一张图片进行拖拽和缩放,拖拽的图片能够被置顶。看下效果:

image  image

 

呵呵就是个山寨的“传统版智能照片桌面”。

设计说明

相当于做一个User Control,遵循MVVM,对外提供ViewModel接口,暴露ViewModel属性(因为我一开始把ViewModel提出来了,设置ViewModel属性会自动设置DataContext)。出于演示目的,只写了演示用到的功能,望大家谅解

TouchFirst.SaApp:WPF程序宿主,这里充当一个测试控件的程序。

TouchFirst.ControlsLib:控件库,含有两个控件:ImageDecorator,ImagesTable以及对外暴露的ViewModel接口。

用到了”Sa.Wpf.mvvm.ViewModel.dll”中的ViewModelBase类,这是以前写的一个ViewModel基类,实现INotifyPropertyChanged、IDispose。

类型说明

ImageDecorator:简单包装一个Image控件,方便使用者直接配置RotateTransform的Angle等属性。

IImageDecoratorViewModel:ImageDecorator适用的ViewModel,方便MVVM开发中使用。

ImageTables:能够承载若干个ImageDecorator,提供一个AddImage方法用于使用者添加图片。

IInputSupport:负责注册ImageTables的事件以及相应添加图片操作。

TraditionalInputSupport:实现IInputSupport,使控件支持传统的操作方式。

MultitouchSupport:实现IInputSupport,使控件支持触控操作方式

细节说明

 

ImageDecorator.xaml
<UserControl x:Class="TouchFirst.ControlsLib.ImageDecorator"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" Background="Transparent">
    
<Image x:Name="Img" Source="{Binding Path=ImagePath}" Stretch="Fill" Height="Auto" Width="Auto" RenderTransformOrigin="0.5, 0.5">
        
<Image.RenderTransform>
            
<TransformGroup>
                
<RotateTransform Angle="{Binding Path=Angle}"></RotateTransform>
                
<ScaleTransform ScaleX="{Binding Path=ScaleX}" ScaleY="{Binding Path=ScaleY}"></ScaleTransform>
                
<TranslateTransform X="{Binding Path=X}" Y="{Binding Path=Y}"></TranslateTransform>
            
</TransformGroup>
        
</Image.RenderTransform>
    
</Image>
</UserControl>

 

使用绑定时所要遵循的接口

IImageDecoratorViewModel
namespace TouchFirst.ControlsLib
{
    
public interface IImageDecoratorViewModel
    {
        
#region Presentation Properties Contract
        ImageSource ImagePath { 
getset; }
        
double ScaleX { getset; }
        
double ScaleY { getset; }
        
double X { getset; }
        
double Y { getset; }
        
double Angle { getset; }
        
#endregion
    }
}

 

 

控件行为接口

IInputSupport

namespace TouchFirst.ControlsLib.External
{
    
public interface IInputSupport
    {
        
/// <summary>
        
/// 注册UI的事件
        
/// </summary>
        
/// <param name="control">承载图片的Canvas容器</param>
        void Register(UIElement control, Canvas OutterContainer);
        
/// <summary>
        
/// 增加图片路径
        
/// </summary>
        
/// <param name="path">图片路径</param>
        void AddImage(string path);
    }
}

 

控件行为对传统输入设备的实现

 

TraditionalInputSupport
namespace TouchFirst.ControlsLib.External
{
    
public class TraditionalInputSupport : IInputSupport
    {
        
#region Fields
        
private UIElement _control;
        
private Point _prevLocation;
        
private ImageDecorator _image;
        
private Canvas _outterContainer;
        
#endregion

        
#region IInputSupport 成员
        
public void Register(System.Windows.UIElement control, Canvas outterContainer)
        {
            _control 
= control;
            _outterContainer 
= outterContainer;
            control.MouseLeftButtonDown 
+= MouseLeftButtonDownHandler;
            control.MouseMove 
+= MouseMoveHandler;
            control.MouseLeftButtonUp 
+= MouseLeftButtonUpHandler;
            control.MouseWheel 
+= MouseWheelHandler;
        }
        
public void AddImage(string filePath)
        {
            ImageDecorator image 
= new ImageDecorator() { ImagePath = new BitmapImage(new Uri(filePath)) };
            
//RandomPropertyGenerator.Configure(image);
            image.SetValue(Canvas.LeftProperty, 1.0);
            image.SetValue(Canvas.TopProperty, 
1.0);
            _outterContainer.Children.Add(image);
        }
        
#endregion

        
#region Event Handler
        
private void MouseLeftButtonDownHandler(object sender, MouseButtonEventArgs args)
        {
            _prevLocation 
= args.GetPosition(_control);
            _image 
= findImage();
            takeItFront(_image);
        }

        
private void MouseMoveHandler(object sender, MouseEventArgs args)
        {
            
if (args.LeftButton == MouseButtonState.Released || _image == null)
                
return;
            Point currentLocation 
= args.GetPosition(_control);
            
double offsetX = currentLocation.X - _prevLocation.X;
            
double offsetY = currentLocation.Y - _prevLocation.Y;
            
double prevX = (double)_image.GetValue(Canvas.LeftProperty);
            
double prevY = (double)_image.GetValue(Canvas.TopProperty);
            _image.SetValue(Canvas.LeftProperty, prevX 
+ offsetX);
            _image.SetValue(Canvas.TopProperty, prevY 
+ offsetY);
            _prevLocation 
= currentLocation;
            Console.WriteLine(
"{0}, {1}", prevX, prevY);
        }

        
private void MouseLeftButtonUpHandler(object sender, MouseButtonEventArgs args)
        {
            
if(_image != null)
            {
                
//takeItBehind(_image);
                _image = null;
            }
        }

        
private void MouseWheelHandler(object sender, MouseWheelEventArgs args)
        {
            Point location 
= args.GetPosition(_control);
            ImageDecorator image 
= findImage();
            
if (image == null)
                
return;
            takeItFront(image);
            
double scalingFactor = 1 + args.Delta / 1000.0;
            image.ScaleX 
*= scalingFactor;
            image.ScaleY 
*= scalingFactor;
        }

        
private ImageDecorator findImage()
        {
            var list 
= _outterContainer.Children;
            
foreach(var elmt in list)
            {
                var uiElmt 
= elmt as ImageDecorator;
                
if(uiElmt == null)
                    
continue;
                
if(uiElmt.IsMouseOver)
                {
                    
return uiElmt;
                }
            }
            
return null;
        }
        
        
private void takeItFront(UIElement uiElmt)
        {
            
foreach(var elmt in _outterContainer.Children)
            {
                var uiElmt2 
= elmt as UIElement;
                
if(uiElmt2 != null)
                {
                    takeItBehind(uiElmt2);
                }
            }
            uiElmt.SetValue(Canvas.ZIndexProperty, 
99);
        }
        
private void takeItBehind(UIElement uiElmt)
        {
            uiElmt.SetValue(Canvas.ZIndexProperty, 
1);
        }
        
#endregion
    }
}

源码放到SkyDrive上了

http://public.blu.livefilestore.com/y1pdolMU6PHGnZitRCJtTlxRH_3_p0HyAv1zgJ2SM0-SqIrb37oC1_S_Qcixp_zl6y8Z8lCG5-nYGpipZOHgSvW2w/TouchFirst.rar?download

在下一篇会讨论如何修改这个程序以支持使用触控、手势操作(不过我这里也没有那种触控设备,所以只能勉强纸上谈兵)。