첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ MainWindow.xaml

<Window x:Class="TestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="800"
    Height="600"
    Title="Polygon 클래스 : 다각형 그리기/편집하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.CommandBindings>
        <CommandBinding Command="New"
            CanExecute="NewCommand_CanExecute"
            Executed="NewCommand_Executed" />
        <CommandBinding Command="Open"
            CanExecute="OpenCommand_CanExecute"
            Executed="OpenCommand_Executed" />
        <CommandBinding Command="SaveAs"
            CanExecute="SaveAsCommand_CanExecute"
            Executed="SaveAsCommand_Executed" />
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Gesture="Ctrl+S" Command="SaveAs" />
    </Window.InputBindings>
    <DockPanel>
        <Menu DockPanel.Dock="Top"
            Padding="5"
            FontFamily="나눔고딕코딩"
            FontSize="16">
            <MenuItem Header="파일(_F)">
                <MenuItem Command="New"  />
                <MenuItem Command="Open" />
                <MenuItem Command="SaveAs"
                    InputGestureText="Ctrl+S" />
                <Separator />
                <MenuItem Name="exitMenuItem"
                    Header="종료(_E)" />
            </MenuItem>
        </Menu>
        <StackPanel DockPanel.Dock="Top"
            Margin="10"
            Orientation="Horizontal">
            <RadioButton Name="drawRadioButton"
                Margin="5"
                IsChecked="True"
                Content="그리기" />
            <RadioButton Name="editRadioButton"
                Margin="5"
                Content="편집" />
        </StackPanel>
        <Border Name="border"
            Margin="10"
            BorderBrush="Gray"
            BorderThickness="1">
            <Canvas Name="canvas"
                Background="Transparent"
                ClipToBounds="True" />
        </Border>
    </DockPanel>
</Window>

 

728x90

 

▶ MainWindow.xaml.cs

using Microsoft.Win32;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 신규 다각형
        /// </summary>
        private Polyline newPolyline = null;

        /// <summary>
        /// 편집 여부
        /// </summary>
        private bool isEditing = false;

        /// <summary>
        /// 편집시 다각형 히트 타입
        /// </summary>
        private PolygonHitType editPolygonHitType = PolygonHitType.None;

        /// <summary>
        /// 편집 다각형
        /// </summary>
        private Polygon editPolygon = null;

        /// <summary>
        /// 편집 다각형 파트 인덱스
        /// </summary>
        private int editPolygonPartIndex = -1;

        /// <summary>
        /// 편집 마지막 포인트
        /// </summary>
        private Point editLastPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);

        /// <summary>
        /// 파일 열기 대화 상자
        /// </summary>
        private OpenFileDialog openFileDialog = new OpenFileDialog();

        /// <summary>
        /// 파일 저장 대화 상자
        /// </summary>
        private SaveFileDialog saveFileDialog = new SaveFileDialog();

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            this.openFileDialog.Filter     = "DRW Files (*.drw)|*.drw|All files (*.*)|*.*";
            this.openFileDialog.DefaultExt = "drw";

            this.saveFileDialog.Filter     = "DRW Files (*.drw)|*.drw|All files (*.*)|*.*";
            this.saveFileDialog.DefaultExt = "drw";

            this.exitMenuItem.Click    += exitMenuItem_Click;
            this.editRadioButton.Click += editRadioButton_Click;
            this.canvas.MouseDown      += canvas_MouseDown;
            this.canvas.MouseMove      += canvas_MouseMove;
            this.canvas.MouseUp        += canvas_MouseUp;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 새로 만들기 명령 실행 가능 여부 구하기 - NewCommand_CanExecute(sender, e)

        /// <summary>
        /// 새로 만들기 명령 실행 가능 여부 구하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void NewCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanExecute(true);
        }

        #endregion
        #region 새로 만들기 명령 실행시 처리하기 - NewCommand_Executed(sender, e)

        /// <summary>
        /// 새로 만들기 명령 실행시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.canvas.Children.Clear();
        }

        #endregion
        #region 열기 명령 실행 가능 여부 구하기 - OpenCommand_CanExecute(sender, e)

        /// <summary>
        /// 열기 명령 실행 가능 여부 구하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void OpenCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanExecute(false);
        }

        #endregion
        #region 열기 명령 실행시 처리하기 - OpenCommand_Executed(sender, e)

        /// <summary>
        /// 열기 명령 실행시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if(this.openFileDialog.ShowDialog().Value)
            {
                string xaml = File.ReadAllText(this.openFileDialog.FileName);

                this.canvas = (Canvas)XamlReader.Parse(xaml);

                this.border.Child = this.canvas;

                this.canvas.MouseMove += canvas_MouseMove;
                this.canvas.MouseDown += canvas_MouseDown;
                this.canvas.MouseUp   += canvas_MouseUp;
            }
        }

        #endregion
        #region 다른 이름으로 저장 명령 실행 가능 여부 구하기 - SaveAsCommand_CanExecute(sender, e)

        /// <summary>
        /// 다른 이름으로 저장 명령 실행 가능 여부 구하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void SaveAsCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanExecute(true);
        }

        #endregion        
        #region 다른 이름으로 저장 명령 실행시 처리하기 - SaveAsCommand_Executed(sender, e)

        /// <summary>
        /// 다른 이름으로 저장 명령 실행시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void SaveAsCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if(this.saveFileDialog.ShowDialog().Value)
            {
                string xaml = XamlWriter.Save(this.canvas);

                File.WriteAllText(this.saveFileDialog.FileName, xaml);
            }
        }

        #endregion

        #region 종료 메뉴 항목 클릭시 처리하기 - exitMenuItem_Click(sender, e)

        /// <summary>
        /// 종료 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void exitMenuItem_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        #endregion
        #region 편집 라디오 버튼 클릭시 처리하기 - editRadioButton_Click(sender, e)

        /// <summary>
        /// 편집 라디오 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void editRadioButton_Click(object sender, RoutedEventArgs e)
        {
            FinishPolygon();
        }

        #endregion
        #region 캔버스 마우스 DOWN 처리하기 - canvas_MouseDown(sender, e)

        /// <summary>
        /// 캔버스 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if(this.drawRadioButton.IsChecked.Value)
            {
                #region 그리기시

                if(e.RightButton == MouseButtonState.Pressed)
                {
                    FinishPolygon();

                    return;
                }

                Point mousePoint = e.GetPosition(this.canvas);

                if(this.newPolyline == null)
                {
                    this.newPolyline = new Polyline();

                    this.newPolyline.Stroke          = Brushes.Red;
                    this.newPolyline.StrokeThickness = 1;
                    this.newPolyline.StrokeDashArray = new DoubleCollection();

                    this.newPolyline.StrokeDashArray.Add(5);
                    this.newPolyline.StrokeDashArray.Add(5);

                    this.newPolyline.Points.Add(mousePoint);

                    this.canvas.Children.Add(this.newPolyline);
                }

                this.newPolyline.Points.Add(mousePoint);

                #endregion
            }
            else
            {
                #region 편집시

                if(this.editPolygon == null)
                {
                    return;
                }

                this.editLastPoint = e.GetPosition(canvas);

                this.isEditing = true;

                this.canvas.CaptureMouse();

                #endregion
            }
        }

        #endregion
        #region 캔버스 마우스 이동시 처리하기 - canvas_MouseMove(sender, e)

        /// <summary>
        /// 캔버스 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point mousePoint = e.GetPosition(this.canvas);

            if(this.drawRadioButton.IsChecked.Value)
            {
                if(this.newPolyline == null)
                {
                    return;
                }

                this.newPolyline.Points[this.newPolyline.Points.Count - 1] = mousePoint;
            }
            else
            {
                if(this.isEditing)
                {
                    if(this.editPolygonHitType == PolygonHitType.Vertex)
                    {
                        MoveVertex(e);
                    }
                    else if(this.editPolygonHitType == PolygonHitType.Edge)
                    {
                        MovePolygon(e);
                    }
                }
                else
                {
                    Cursor newCursor = null;

                    SetEditPolygon(mousePoint);

                    if(this.editPolygonHitType == PolygonHitType.Vertex)
                    {
                        newCursor = Cursors.Cross;
                    }
                    else if(this.editPolygonHitType == PolygonHitType.Edge)
                    {
                        newCursor = Cursors.SizeAll;
                    }

                    if(this.canvas.Cursor != newCursor)
                    {
                        this.canvas.Cursor = newCursor;
                    }
                }
            }
        }

        #endregion
        #region 캔버스 마우스 UP 처리하기 - canvas_MouseUp(sender, e)

        /// <summary>
        /// 캔버스 마우스 UP 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this.isEditing = false;

            this.canvas.ReleaseMouseCapture();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 실행 가능 여부 구하기 - CanExecute(requirePolygon)

        /// <summary>
        /// 실행 가능 여부 구하기
        /// </summary>
        /// <param name="requirePolygon">다각형 필요 여부</param>
        /// <returns>실행 가능 여부 구하기</returns>
        private bool CanExecute(bool requirePolygon)
        {
            if(this.newPolyline != null)
            {
                return false;
            }

            if(this.editPolygon != null)
            {
                return false;
            }

            if(requirePolygon)
            {
                return this.canvas.Children.Count > 0;
            }

            return true;
        }

        #endregion
        #region 다각형 끝내기 - FinishPolygon()

        /// <summary>
        /// 다각형 끝내기
        /// </summary>
        private void FinishPolygon()
        {
            if(this.newPolyline == null)
            {
                return;
            }

            if(this.newPolyline.Points.Count > 3)
            {
                this.newPolyline.Points.RemoveAt(this.newPolyline.Points.Count - 1);

                Polygon newPolygon = new Polygon();

                newPolygon.Stroke          = Brushes.Blue;
                newPolygon.StrokeThickness = 2;
                newPolygon.Points          = this.newPolyline.Points;

                this.canvas.Children.Add(newPolygon);
            }

            this.canvas.Children.Remove(this.newPolyline);

            this.newPolyline = null;
        }

        #endregion
        #region 꼭지점 이동하기 - MoveVertex(e)

        /// <summary>
        /// 꼭지점 이동하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        private void MoveVertex(MouseEventArgs e)
        {
            Point mousePoint = e.GetPosition(this.canvas);

            double deltaX = mousePoint.X - this.editLastPoint.X;
            double deltaY = mousePoint.Y - this.editLastPoint.Y;

            Point newPoint = new Point
            (
                this.editPolygon.Points[this.editPolygonPartIndex].X + deltaX,
                this.editPolygon.Points[this.editPolygonPartIndex].Y + deltaY
            );

            this.editPolygon.Points[this.editPolygonPartIndex] = newPoint;

            this.editLastPoint = mousePoint;
        }

        #endregion
        #region 다각형 이동하기 - MovePolygon(e)

        /// <summary>
        /// 다각형 이동하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        private void MovePolygon(MouseEventArgs e)
        {
            Point mousePoint = e.GetPosition(this.canvas);

            double deltaX = mousePoint.X - this.editLastPoint.X;
            double deltaY = mousePoint.Y - this.editLastPoint.Y;

            int pointCount = this.editPolygon.Points.Count;

            for(int i = 0; i < pointCount; i++)
            {
                Point newPoint = new Point
                (
                    this.editPolygon.Points[i].X + deltaX,
                    this.editPolygon.Points[i].Y + deltaY
                );

                this.editPolygon.Points[i] = newPoint;
            }

            this.editLastPoint = mousePoint;
        }

        #endregion
        #region 편집 다각형 설정하기 - SetEditPolygon(point)

        /// <summary>
        /// 편집 다각형 설정하기
        /// </summary>
        /// <param name="point">포인트</param>
        private void SetEditPolygon(Point point)
        {
            this.editPolygon          = null;
            this.editPolygonHitType   = PolygonHitType.None;
            this.editPolygonPartIndex = -1;

            foreach(UIElement element in this.canvas.Children)
            {
                Polygon polygon = element as Polygon;

                if(polygon == null)
                {
                    continue;
                }

                if(polygon.IsAt(point, out this.editPolygonHitType, out this.editPolygonPartIndex))
                {
                    this.editPolygon = polygon;

                    return;
                }
            }
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요