你的位置:首页 > ASP.net教程

[ASP.net教程]WPF学习11:基于MVVM Light 制作图形编辑工具(2)


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

    这一次的目标是完成image

    两个任务。


 

画布

    效果:

    imageimage

    画布上,选择的方案是:直接以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");  }}

    imageimageimage

    最后是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)));  }}

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

   


 

直线

    效果如下:

    imageimage

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


    引入后我们就可以为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));  }}


 

圆形

    效果:

    image    

    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);


 

矩形

    image

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

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

 

    开发环境VS2013, .NET4.5

    源码