迎接Multitouch时代系列之--传统的操作体验
2010-03-21 13:37 姜 萌@cnblogs 阅读(434) 评论(0) 编辑 收藏 举报本篇实际上就是MSDN上HOL的实现。不过一开始没找到那个HOL源码下载,于是自己做了一个,改了些功能和设计。本身并不复杂,程序的效果就是可以在窗口的一个区域上添加各种RenderTransform样式图片图片,用户能够通过鼠标对每一张图片进行拖拽和缩放,拖拽的图片能够被置顶。看下效果:
![]() |
![]() |
呵呵就是个山寨的“传统版智能照片桌面”。
设计说明
相当于做一个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,使控件支持触控操作方式
细节说明

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>
使用绑定时所要遵循的接口

{
public interface IImageDecoratorViewModel
{
#region Presentation Properties Contract
ImageSource ImagePath { get; set; }
double ScaleX { get; set; }
double ScaleY { get; set; }
double X { get; set; }
double Y { get; set; }
double Angle { get; set; }
#endregion
}
}
控件行为接口
IInputSupport
{
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);
}
}
控件行为对传统输入设备的实现

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