zoukankan      html  css  js  c++  java
  • 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中需要引入两个命名空间:

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


     

    圆形

        效果:

        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

        源码

  • 相关阅读:
    从函数作用域和块级作用域看javascript的作用域链
    基于vue实现一个简单的MVVM框架(源码分析)
    发布-订阅模式
    希尔排序
    直接插入排序
    选择排序
    React Fiber源码分析 第三篇(异步状态)
    React Fiber源码分析 第二篇(同步模式)
    React Fiber源码分析 第一篇
    数据结构 之 树总结
  • 原文地址:https://www.cnblogs.com/E-WALKER/p/4463042.html
Copyright © 2011-2022 走看看