本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续

这一次的目标是完成

两个任务。


画布

效果:

画布上,选择的方案是:直接以Image作为画布,使用RenderTargetBitmap绑定为Image的图片源,这样可以为后续的导出图片功能提供很大的便利。

对拖动栏XAML进行如下修改:

<ScrollViewer  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Column="1" Grid.Row="1">
<Canvas VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding ActualWidth,ElementName=ImageBorder}" SnapsToDevicePixels="False" Height="{Binding Path=ActualHeight,ElementName=ImageBorder}" Margin="50 50 0 0 " ClipToBounds="True">
<Border Name="ImageBorder" BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding DrawingBitmap}">
</Image>
</Border>
</Canvas>
</ScrollViewer>

相应的,ViewModel中也要添加代码。

private RenderTargetBitmap _drawingBitmap;

public RenderTargetBitmap DrawingBitmap
{
get { return _drawingBitmap; }
set
{
_drawingBitmap = value;
RaisePropertyChanged("DrawingBitmap");
}
}

到这里,画布的绑定就完成了。

现在要完成调节画布大小的相关代码,首先,在XAML增加两个输入框使得长宽由界面配置:

<TextBlock VerticalAlignment="Center"><Run Text="宽:"/></TextBlock>
<TextBox Width="50" Margin="0 0 10 0" Text="{Binding DrawingAreaWidth, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center"><Run Text="高:"/></TextBlock>
<TextBox Width="50" Margin="0 0 10 0" Text="{Binding DrawingAreaHeight, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
<Button Margin="10 0 10 0" Content="配置" Command="{Binding SetDrawingAreaSize}"/>

ViewModel部分:

private Int32 _drawingAreaWidth;
public Int32 DrawingAreaWidth
{
get { return _drawingAreaWidth; }
set
{
_drawingAreaWidth = value;
RaisePropertyChanged("DrawingAreaWidth");
}
} private Int32 _drawingAreaHeight;
public Int32 DrawingAreaHeight
{
get { return _drawingAreaHeight; }
set
{
_drawingAreaHeight = value;
RaisePropertyChanged("DrawingAreaHeight");
}
}

最后是Command SetDrawingAreaSize 的实现:

private ICommand _setDrawingAreaSize;
public ICommand SetDrawingAreaSize
{
get
{
return _setDrawingAreaSize ?? (_setDrawingAreaSize = new RelayCommand(() =>
{
DrawingBitmap = new RenderTargetBitmap(DrawingAreaWidth, DrawingAreaHeight,
96, 96, PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var context = drawingVisual.RenderOpen())
{
context.DrawRectangle(Brushes.White, null,
new Rect(0, 0, DrawingAreaWidth, DrawingAreaHeight));
}
DrawingBitmap.Render(drawingVisual);
}
, () => (DrawingAreaWidth != 0 && DrawingAreaHeight != 0)));
}
}

至此,本节最开始的效果就完成啦。


直线

效果如下:

XAML中需要引入两个命名空间:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:command=http://www.galasoft.ch/mvvmlight

引入后我们就可以为Image添加三个响应鼠标的命令。

<Image Source="{Binding DrawingBitmap}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<command:EventToCommand Command="{Binding MouseMoveCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseDown" >
<command:EventToCommand Command="{Binding MouseDownCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseUp" >
<command:EventToCommand Command="{Binding MouseUpCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>

在界面上绘制Line控件,用于动态操作时的显示,操作完毕,隐藏控件,在Image上绘图。

<Line Stroke="Black" StrokeThickness="1" Visibility="{Binding LineVisibility}"
X1="{Binding PositionX1}" X2="{Binding PositionX2}"
Y1="{Binding PositionY1}" Y2="{Binding PositionY2}"/>

ViewModel添加以下属性,X2,Y1,Y2略。

private Visibility _lineVisibility = Visibility.Hidden;
public Visibility LineVisibility
{
get { return _lineVisibility; }
set
{
_lineVisibility = value;
RaisePropertyChanged("LineVisibility");
}
} private Double _positionX1;
public Double PositionX1
{
get { return _positionX1; }
set
{
_positionX1 = value;
RaisePropertyChanged("PositionX1");
}
}

为了让ViewModel能知道当前的绘图状态(直线,圆,矩形)在加一些数据绑定:

<RadioButton Style="{StaticResource StatusBarButton}" IsChecked="{Binding LineModeEnable}">
<Line X1="0" Y1="0" X2="15" Y2="15" Stroke="Black" StrokeThickness="1"></Line>
</RadioButton>
<RadioButton Style="{StaticResource StatusBarButton}" IsChecked="{Binding RectangleModeEnable}">
<Rectangle Width="20" Height="15" Stroke="Black" StrokeThickness="1"></Rectangle>
</RadioButton>
<RadioButton Style="{StaticResource StatusBarButton}" IsChecked="{Binding EllipseModeEnable}">
<Ellipse Width="20" Height="20" Stroke="Black" StrokeThickness="1"></Ellipse>
</RadioButton>

最后,我们编写三个鼠标相应的指令:

public ICommand SetDrawingAreaSize
{
get
{
return _setDrawingAreaSize ?? (_setDrawingAreaSize = new RelayCommand(() =>
{
DrawingBitmap = new RenderTargetBitmap(DrawingAreaWidth, DrawingAreaHeight,
96, 96, PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var context = drawingVisual.RenderOpen())
{
context.DrawRectangle(Brushes.White, null,
new Rect(0, 0, DrawingAreaWidth, DrawingAreaHeight));
}
DrawingBitmap.Render(drawingVisual);
}
, () => (DrawingAreaWidth != 0 && DrawingAreaHeight != 0)));
}
} private ICommand _mouseMoveCommand;
public ICommand MouseMoveCommand
{
get
{
return _mouseMoveCommand ?? (_mouseMoveCommand = new RelayCommand<MouseEventArgs>((e) =>
{
PositionX2 = e.GetPosition((IInputElement)e.Source).X;
PositionY2 = e.GetPosition((IInputElement)e.Source).Y;
}
, (e) => true));
}
} private ICommand _mouseDownCommand;
public ICommand MouseDownCommand
{
get
{
return _mouseDownCommand ?? (_mouseDownCommand = new RelayCommand<MouseEventArgs>((e) =>
{
if(LineModeEnable)
LineVisibility = Visibility.Visible;
PositionX1 = e.GetPosition((IInputElement)e.Source).X;
PositionY1 = e.GetPosition((IInputElement)e.Source).Y;
}
, (e) => true));
}
} private ICommand _mouseUpCommand;
public ICommand MouseUpCommand
{
get
{
return _mouseUpCommand ?? (_mouseUpCommand = new RelayCommand<MouseEventArgs>((e) =>
{
var drawingVisual = new DrawingVisual();
using (var context = drawingVisual.RenderOpen())
{
//此处的-1用于消除画布边界带来的偏差,因为目前都是固定的,所以没有使用数据绑定。
if(LineModeEnable)
context.DrawLine(new Pen(Brushes.Black, 1), new Point(PositionX1 - 1, PositionY1 - 1), new Point(PositionX2 - 1, PositionY2 - 1 ));
}
DrawingBitmap.Render(drawingVisual);
LineVisibility = Visibility.Hidden;
}
, (e) => true));
}
}

圆形

效果:

XAML代码:

<Ellipse Stroke="Black" StrokeThickness="1" Visibility="{Binding EllipseVisibility}"
Canvas.Left="{Binding PositionX1}" Canvas.Top="{Binding PositionY1}"
Width="{Binding ShapeWidth}" Height="{Binding ShapeHeight}"></Ellipse>

MouseDown增加:

if (EllipseModeEnable)
{
ShapeWidth = ShapeHeight = 0;
EllipseVisibility = Visibility.Visible;
}

Move增加:

ShapeWidth = Math.Abs(PositionX2 - PositionX1);
ShapeHeight = Math.Abs(PositionY2 - PositionY1);

Up增加:

if(EllipseModeEnable)
context.DrawEllipse(new SolidColorBrush(Colors.White), new Pen(Brushes.Black, 1),
new Point(PositionX1 + ShapeWidth / 2 - 1, PositionY1 + ShapeHeight / 2 - 1), ShapeWidth / 2, ShapeHeight / 2);

矩形

效果如上,代码与圆形相似,故省略。

下一节将会完成图形的放大、缩小、移动,颜色的填充。

开发环境VS2013, .NET4.5

源码

WPF学习11:基于MVVM Light 制作图形编辑工具(2)的更多相关文章

  1. WPF学习12:基于MVVM Light 制作图形编辑工具(3)

    本文是WPF学习11:基于MVVM Light 制作图形编辑工具(2)的后续 这一次的目标是完成 两个任务. 本节完成后的效果: 本文分为三个部分: 1.对之前代码不合理的地方重新设计. 2.图形可选 ...

  2. WPF学习10:基于MVVM Light 制作图形编辑工具(1)

    图形编辑器的功能如下图所示: 除了MVVM Light 框架是一个新东西之外,本文所涉及内容之前的WPF学习0-9基本都有相关介绍. 本节中,将搭建编辑器的界面,搭建MVVM Light 框架的使用环 ...

  3. WPF学习笔记-用Expression Design制作矢量图然后导出为XAML

    WPF学习笔记-用Expression Design制作矢量图然后导出为XAML 第一次用Windows live writer写东西,感觉不错,哈哈~~ 1.在白纸上完全凭感觉,想象来画图难度很大, ...

  4. WPF学习08:MVVM 预备知识之COMMAND

    WPF内建的COMMAND是GOF 提出的23种设计模式中,命令模式的实现. 本文是WPF学习07:MVVM 预备知识之数据绑定的后续,将说明实现COMMAND的三个重点:ICommand  Comm ...

  5. 【Telerik控件学习】-建立自己的图形编辑工具(Diagram)

    Telerik提供了RadDiagram控件,用于图形元素的旋转,拖拽和缩放.更重要的是,它还拓展了许多绑定的命令(复制,剪切,粘贴,回退等等). 我们可以用来组织自己的图形编辑工具. Step1.定 ...

  6. MAPZONE GIS SDK接入Openlayers3之五——图形编辑工具

    图形编辑工具提供对要素图形进行增.删.改的功能,具体包括以下几种工具类型: 浏览工具 选择工具 创建要素工具 删除命令 分割工具 合并命令 节点编辑工具 修边工具 撤销命令 重做命令 工具的实现基本上 ...

  7. WPF学习07:MVVM 预备知识之数据绑定

    MVVM是一种模式,而WPF的数据绑定机制是一种WPF内建的功能集,两者是不相关的. 但是,借助WPF各种内建功能集,如数据绑定.命令.数据模板,我们可以高效的在WPF上实现MVVM.因此,我们需要对 ...

  8. WPF学习笔记-用Expression Blend制作自定义按钮

    1.从Blend工具箱中添加一个Button,按住shift,将尺寸调整为125*125; 2.右键点击此按钮,选择Edit control parts(template)>Edit a cop ...

  9. WPF学习笔记:MVVM模式下,ViewModel如何关闭View?

    原文:http://blog.csdn.net/leftfist/article/details/32349731 矫枉过正,从一个极端走向另一个极端.MVVM模式,View只负责呈现,虽然也有后台代 ...

随机推荐

  1. IIS+Asp.Net Mvc必须知道的事(解决启动/重启/自动回收站点后第一次访问慢问题)

    问题现象: Asp.net Mvc站点部署在IIS上后,第一个用户第一次访问站点,都会比较慢,确切的说是访问站点的Action页面(即非静态页面,因为静态页面直接由IIS处理返回给用户即完成请求,而A ...

  2. Redis入门教程(二)— 基本数据类型

    阅读以下内容时,手边打开一个redis-cli一起输入,输入命令敲击回车键前在心中想好你的答案,如果结果不合你的预期,请分析原因,使极大地提高学习效率.如果没有条件,每个数据类型后有代码运行结果,供你 ...

  3. JavaScript语句-流程控制语句

    JavaScript定义了一组语句,语句通常用于执行一定的任务.语句可以很简单,也可以很复杂. 选择结构,可以在程序中创建交叉结构来指定程序流的可能方向.JavaScript中有四种选择结构: 1.单 ...

  4. 小贝_mysql优化学习

    mysql优化 简要: 1.数据库设计优化 2.sql语句优化 3.表切割 4.读写分离技术 一.数据库设计优化 1.表设计要符合三范式.当然,有时也须要适当的逆范式 2.什么是三范式 一范式: 具有 ...

  5. GIS+=地理信息+云计算技术——Spark集群部署

    第一步:安装软件         Spark 1.5.4:wget http://www.apache.org/dyn/closer.lua/spark/spark-1.5.2/spark-1.5.2 ...

  6. HDU1251 统计难题 【trie树】

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others) Total Subm ...

  7. 一个JS引发的跨域问题

    忽然遇上跨域错误. 我们有张页面,使用了EXT.js,在本地运行正常,部署到服务器上,出不来数据.F12调试,提示有跨域错误? XMLHttpRequest cannot load http://19 ...

  8. rip是典型的距离矢量动态路由协议。Ospf是链路状态型的协议

    网络工程师十个常见面试问题-看准网 https://m.kanzhun.com/k-mianshiwenti/1465113.html 两者都属于IGP协议,rip是典型的距离矢量动态路由协议.Osp ...

  9. 使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,【原创】

    关键词:Android  linux hrtimer 蜂鸣器  等待队列 信号量 字符设备 平台信息:内核:linux3.4.39 系统:android/android5.1平台:S5P4418  作 ...

  10. ubuntu下nginx的安裝

    本系列的lnmp的大框架基本上是按照http://www.linuxzen.com/lnmphuan-jing-da-jian-wan-quan-shou-ce-si-lnmpda-jian-yuan ...