[WPF로 도형그리기] 4. 도형 모델의 이해(C) - 원, 네모, 텍스트

2025. 3. 10. 21:57C#,WPF

이어서 원, 네모, 텍스트를 그려보겠습니다. 

<그림 1. 원, 네모, 텍스트 도형의 모습>

 

먼저 EllipseModel과 RectanlgeModel입니다.

// RectangleModel.cs
public class RectangleModel : ShapeBase
{
    protected override Geometry CreateGeometry()
    {
        return new RectangleGeometry(new Rect(StartPoint, EndPoint));
    }
}

// EllipseModel.cs
public class EllipseModel : ShapeBase
{
    protected override Geometry CreateGeometry()
    {
        return new EllipseGeometry(new Rect(StartPoint, EndPoint));
    }
}

두 모델 모두 바로 Rect와 Ellipse 클래스를 반환하는 것이 아닌 Geometry로 감싸서 반환하는 Rect나 Ellipse 클래스만으로 WPF 앱에서 그리기에 충분하지 못하기 때문입니다.

 

다음은 TextAnnotation 입니다. 이 도형의 경우 '네모 + 텍스트박스' 조합으로 구성됩니다. 해당 클래스의 코드 전체 입니다. 이어서 부분별로 살펴보겠습니다. 

public class TextAnnotation : ShapeBase
{
    private TextBox _textBox;
    private const int TextBoxMargin = 5;

    public TextAnnotation()
    {
        _textBox = new TextBox
        {
            Text = "RIVMT",
            Foreground = Brushes.Red,
            FontSize = 20,
            FontWeight = FontWeights.Bold,
            TextWrapping = TextWrapping.Wrap,
            BorderBrush = Brushes.Transparent,
            BorderThickness = new Thickness(0)
        };

        Loaded += (s, e) => AttachTextBox();
        LayoutUpdated += (s, e) => UpdateTextBoxPosition();
    }

    private void AttachTextBox()
    {
        Canvas cnvDrawing = VisualTreeHelper.GetParent(this) as Canvas;
        if (!cnvDrawing.Children.Contains(_textBox))
        {
            cnvDrawing.Children.Add(_textBox);

            UpdateTextBoxPosition();
        }
    }

    private void UpdateTextBoxPosition()
    {
        if (_textBox != null)
        {
            double x = Math.Min(StartPoint.X, EndPoint.X);
            double y = Math.Min(StartPoint.Y, EndPoint.Y);

            _textBox.Margin = new Thickness(x + TextBoxMargin, y + TextBoxMargin, TextBoxMargin, TextBoxMargin);
        }
    }
    protected override Geometry CreateGeometry()
    {
        double width = Math.Max(0, Math.Abs(EndPoint.X - StartPoint.X));
        double height = Math.Max(0, Math.Abs(EndPoint.Y - StartPoint.Y));

        var geometry = new RectangleGeometry(new Rect(StartPoint, EndPoint));


        return new RectangleGeometry(new Rect(StartPoint, EndPoint));
    }
}

 

 

public TextAnnotation()
{
    _textBox = new TextBox
    {
        Text = "RIVMT",
        Foreground = Brushes.Red,
        FontSize = 20,
        FontWeight = FontWeights.Bold,
        TextWrapping = TextWrapping.Wrap,
        BorderBrush = Brushes.Transparent,
        BorderThickness = new Thickness(0)
    };

    Loaded += (s, e) => AttachTextBox();
    LayoutUpdated += (s, e) => UpdateTextBoxPosition();
}

생성자에서 TextBox 생성을합니다. 파워포인트의 텍스트 상자와 비슷한 기능을 구현하기 위해 TextWrapping 처리하였고, 텍스트 영역에 Bodrder는 제거하였습니다. 이어서 해당 UI Element가 로드될 때, 레이아웃이 변경될 때 이벤트핸들러를 추가했습니다. 

 

private void AttachTextBox()
{
    Canvas cnvDrawing = VisualTreeHelper.GetParent(this) as Canvas;
    if (!cnvDrawing.Children.Contains(_textBox))
    {
        cnvDrawing.Children.Add(_textBox);

        UpdateTextBoxPosition();
    }
}

private void UpdateTextBoxPosition()
{
    if (_textBox != null)
    {
        double x = Math.Min(StartPoint.X, EndPoint.X);
        double y = Math.Min(StartPoint.Y, EndPoint.Y);

        _textBox.Margin = new Thickness(x + TextBoxMargin, y + TextBoxMargin, TextBoxMargin, TextBoxMargin);
    }
}

Canvas를 얻어와 TextBox를 추가하고  위치를 정해줍니다. TextBox의 Margin 속성이 없다면 텍스트 박스와 네모가 겹쳐져 원하는 기능을 구현할 수 없습니다.