WPF绘图

简介: 添加一个绘图面板项目按右键添加类DrawingPanel using System; using System.Collections.Generic; using System.Linq; using System.

添加一个绘图面板
项目按右键添加类DrawingPanel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Drawing

{
    class DrawingPanel :Panel

    {
        //所有图形
        private  List<Visual> visuals = new List<Visual>();
        private List<DrawingVisual> hits=new List<DrawingVisual>();


        protected override Visual GetVisualChild(int index)
        {
            return   visuals[index];
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return visuals.Count;
            }
        }


        public void addVisuals(Visual visual)
        {
            visuals.Add(visual);
            base.AddVisualChild(visual);
            base.AddLogicalChild(visual);
        }

        public void DeleteVisual(Visual visual)
        {
            visuals.Remove(visual);
            base.RemoveVisualChild(visual);
            base.RemoveLogicalChild(visual);
        }

        public DrawingVisual GetVisual(Point point)
        {

            HitTestResult hitTestResult = VisualTreeHelper.HitTest(this,point);
            return hitTestResult.VisualHit as DrawingVisual;
        }

        //
        public List<DrawingVisual> GetVisuals(Geometry geometry)
        {
            hits.Clear();
            GeometryHitTestParameters parameters = new GeometryHitTestParameters(geometry);
            HitTestResultCallback callback = new HitTestResultCallback(this.HitTestResultCallback);
            VisualTreeHelper.HitTest(this, null, callback, parameters);
            return hits;
        }

        //HitTest回调函数
        private HitTestResultBehavior HitTestResultCallback(HitTestResult result)
        {
            GeometryHitTestResult geometryHitTest = (GeometryHitTestResult)result;
            DrawingVisual visual = geometryHitTest.VisualHit as DrawingVisual;
            if (visual != null && geometryHitTest.IntersectionDetail == IntersectionDetail.FullyInside)
            {
                hits.Add(visual);
            }
            return HitTestResultBehavior.Continue;
        }
    }
}

然后是在窗口中使用
以下是布局VisualLayer.xmal代码(项目按右键-WPF项目-窗口)

<Window x:Class="WpfApp1.VisualLayer"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:local1="clr-namespace:Drawing"
        mc:Ignorable="d"
        Title="VisualLayer" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions >
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <ToolBarTray Orientation="Vertical">
            <ToolBar>
                <RadioButton Margin="0,3" Name="cmdSelectMove">
                    <StackPanel>
                        <Image Source="pointer.png" Width="35" Height="35"></Image>
                        <TextBlock>Select/Move</TextBlock>
                    </StackPanel>
                </RadioButton>
                <RadioButton Margin="0,3" IsChecked="True" Name="cmdAdd">
                    <StackPanel>
                        <Rectangle Width="30" Height="30" Stroke="SteelBlue" StrokeThickness="3" Fill="AliceBlue">
                        </Rectangle>
                        <TextBlock>Add Square</TextBlock>
                    </StackPanel>
                </RadioButton>
                <RadioButton Margin="0,3" Name="cmdDelete">
                    <StackPanel>
                        <Path Stroke="SteelBlue" StrokeThickness="4" StrokeEndLineCap="Round" Fill="Red" HorizontalAlignment="Center">
                            <Path.Data>
                                <GeometryGroup>
                                    <PathGeometry>
                                        <PathFigure StartPoint="0,0">
                                            <LineSegment Point="18,18"></LineSegment>
                                        </PathFigure>
                                        <PathFigure StartPoint="0,18">
                                            <LineSegment Point="18,0"></LineSegment>
                                        </PathFigure>
                                    </PathGeometry>
                                </GeometryGroup>
                            </Path.Data>
                        </Path>
                        <TextBlock> Delete Square</TextBlock>
                    </StackPanel>
                </RadioButton>
                <RadioButton Margin="0,3" Name="cmdSelecMultipe" >
                    <StackPanel>
                        <Image Source="pointer.png" Width="35" Height="35"></Image>
                        <TextBlock>Select Multiple</TextBlock>
                    </StackPanel>
                </RadioButton>
            </ToolBar>
        </ToolBarTray>
        <Border Grid.Column="1" Margin="3" BorderBrush="SteelBlue" BorderThickness="1">
            <local1:DrawingPanel x:Name="drawingSurface" Background="White" ClipToBounds="True" MouseLeftButtonDown="drawingSurface_MouseLeftButtonDown"
                MouseLeftButtonUp="drawingSurface_MouseLeftButtonUp"
                MouseMove="drawingSurface_MouseMove">
            </local1:DrawingPanel>
        </Border>
    </Grid>
</Window>

VisualLayer.xaml.cs代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfApp1
{
    /// <summary>
    /// VisualLayer.xaml 的交互逻辑
    /// </summary>
    public partial class VisualLayer : Window
    {

        private Brush drawingBrush = Brushes.AliceBlue;
        private Brush selectedDrawingBrush = Brushes.LightGoldenrodYellow;
        private Pen drawingPen = new Pen(Brushes.SteelBlue, 3);
        private Size squareSize = new Size(30, 30);
        private DrawingVisual drawingVisual;
        private Vector clickOffset;
        private bool isDragging;
        private DrawingVisual selectedVisual;
        private bool isMultiSelecting = false;
        private Point selectionSquareTopLeft;
        private Brush selectionSquareBrush = Brushes.Transparent;
        private Pen selectionSquarePen = new Pen(Brushes.Black, 2);
        private DrawingVisual selectionSquare;
        

        public VisualLayer()
        {
            InitializeComponent();
            DrawingVisual v = new DrawingVisual();
            DrawingSquare(v, new Point(10,10), false);
        }


        private void DrawingSquare(DrawingVisual v,Point topLeftCorner,bool isSelected)
        {
            using(DrawingContext dc = v.RenderOpen())
            {
                Brush brush = drawingBrush;
                if (isSelected) brush = selectedDrawingBrush;

                dc.DrawRectangle(brush, drawingPen, new Rect(topLeftCorner, squareSize));

            }
        }


        private void drawingSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point pointClicked = e.GetPosition(drawingSurface);
            if (cmdAdd.IsChecked==true)
            {
                DrawingVisual drawingVisual = new DrawingVisual();
                DrawingSquare(drawingVisual, pointClicked, false);
                drawingSurface.addVisuals(drawingVisual);
            }
            else if (cmdDelete.IsChecked == true)
            {
                DrawingVisual drawingVisual = drawingSurface.GetVisual(pointClicked);
                if (drawingVisual != null) drawingSurface.DeleteVisual(drawingVisual);
            }
            else if (cmdSelectMove.IsChecked == true)
            {
                DrawingVisual visual = drawingSurface.GetVisual(pointClicked);
                if (visual != null)
                {
                    Point topLeftCorner = new Point(
                        visual.ContentBounds.TopLeft.X + drawingPen.Thickness / 2,
                        visual.ContentBounds.TopLeft.Y + drawingPen.Thickness / 2);
                    DrawingSquare(visual, topLeftCorner, true);

                    clickOffset = topLeftCorner - pointClicked;
                    isDragging = true;

                    if (selectedVisual != null && selectedVisual != visual)
                    {
                        // The selection has changed. Clear the previous selection.
                        ClearSelection();
                    }
                    selectedVisual = visual;
                }

            } else if (cmdSelecMultipe.IsChecked == true)
            {
                selectionSquare = new DrawingVisual();

                drawingSurface.addVisuals(selectionSquare);

                selectionSquareTopLeft = pointClicked;
                isMultiSelecting = true;

                // Make sure we get the MouseLeftButtonUp event even if the user
                // moves off the Canvas. Otherwise, two selection squares could be drawn at once.
                drawingSurface.CaptureMouse();
            }
        }

        private void ClearSelection()
        {
            Point topLeftCorner = new Point(
                        selectedVisual.ContentBounds.TopLeft.X + drawingPen.Thickness / 2,
                        selectedVisual.ContentBounds.TopLeft.Y + drawingPen.Thickness / 2);
            DrawingSquare(selectedVisual, topLeftCorner, false);
            selectedVisual = null;
        }

        private void drawingSurface_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                Point pointDragged=e.GetPosition(drawingSurface)+clickOffset;
                DrawingSquare(selectedVisual, pointDragged, true);

            }
            else if(isMultiSelecting)
            {
                Point pointDragged = e.GetPosition(drawingSurface);
                DrawSelectionSquare(selectionSquareTopLeft, pointDragged);
            }
        }

        private void DrawSelectionSquare(Point point1, Point point2)
        {
            selectionSquarePen.DashStyle = DashStyles.Dash;
            using(DrawingContext dc= selectionSquare.RenderOpen())
            {
                dc.DrawRectangle(selectionSquareBrush, selectionSquarePen,
                    new Rect(point1, point2));
            }
        }

        private void drawingSurface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isDragging = false;
            if (isMultiSelecting)
            {
                RectangleGeometry geometry = new RectangleGeometry(
                    new Rect(selectionSquareTopLeft, e.GetPosition(drawingSurface)));
                List<DrawingVisual> visualsInRegion =
                    drawingSurface.GetVisuals(geometry);
                MessageBox.Show(String.Format("You selected {0} square(s).", visualsInRegion.Count));

                isMultiSelecting = false;
                drawingSurface.DeleteVisual(selectionSquare);
                drawingSurface.ReleaseMouseCapture();
            }
        }
    }
}

效果图
image

目录
相关文章
|
算法 C#
WPF特效-绘图
原文:WPF特效-绘图                  WPF玩起来还是挺炫酷的。我实现的效果:不同色块交叉,交叉部分颜色叠加显示。
1246 0
|
前端开发 测试技术 C#
WPF笔记(1.10 绘图)——Hello,WPF!
原文:WPF笔记(1.10 绘图)——Hello,WPF!书中的代码语法过时了,改写为以下(测试通过):                                                                                                                                                                                             Click!                     ScaleTransform将button放大3倍。
1061 0
|
6月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
374 0
|
6月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
134 1
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
|
3月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
101 0