[C#] 텍스트 에디터(윈도우 메모장) 만들기 6

2024. 2. 6. 10:55[C#]/[C# 윈폼] 혼자해보는 윈도우 메모장 만들기

오늘은 메모장을 살펴보니

1. 찾기 & 다음 찾기 & 이전 찾기는 텍스트가 입력됐을 때 메뉴 활성화

2. 잘라내기 & 삭제 & 복사는 텍스트가 선택됐을 때 메뉴 활성화먼저 해주고

3. X를 눌러 종료 시 저장 여부를 추가해줘야한다.

4. 줄 이동


1. 찾기 & 다음 찾기 & 이전 찾기 메뉴 활성화

아무것도 입력 안했을 때
텍스트 입력 시 찾기 기능 활성화

  • 우선 속성에서 해당 내용들의 Enable을 false로 변경해준다.
// 텍스트 변경 여부 확인 메소드
private void MyTextArea_TextChanged(object sender, EventArgs e)
{
    isTextChanged = true;
    UpdateFormTitle();
    if (MyTextArea.Text.Length > 0)
    {
        FindTextToolTip.Enabled = true;
        FindNextToolTip.Enabled = true;
        FindBeforeToolTip.Enabled = true;
    }
    else
    {
        FindTextToolTip.Enabled = false;
        FindNextToolTip.Enabled = false;
        FindBeforeToolTip.Enabled = false;
    }
}
  • Text길이가 0 이상이면 찾기 기능 등을 활성화 시켜주고 아니면 다시 비활성화 시켜준다.

 

2. 잘라내기 & 삭제 & 복사 메뉴 활성화

  • 텍스트 선택 시 활성화 시켜준다.
  private void MyTextArea_SelectionChanged(object sender, EventArgs e)
  {
      // 텍스트가 선택되었는지 여부를 확인하여 메뉴의 활성화 여부를 설정
      bool isTextSelected = MyTextArea.SelectionLength > 0;
      CutTextToolTip.Enabled = isTextSelected;
      CopyTextToolTip.Enabled = isTextSelected;
      DeleteTextToolTip.Enabled = isTextSelected;
  }

 

 

3. X를 눌러 종료 시 저장 여부, 끝내기

  • 여기서 시간을 엄청 잡아먹었다.
  • 코드를 추가하고 기존 코드를 변경하는 작업에서 코드가 꼬인 것 같다.

기존 코드

private void ExitToolTip_Click(object sender, EventArgs e)
 {
		if (isTextChanged)
            {
                DialogResult result = MessageBox.Show("변경된 내용을 저장하시겠습니까?", "저장 확인", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

                if (result == DialogResult.Yes)
                {
                    // 저장
                    SaveFile();
                }
                else if (result == DialogResult.Cancel)
                {
                    // 취소
                    return;
                }
            }

            Close();
 }

 

   private void 메모장_FormClosing(object sender, FormClosingEventArgs e)
   {
       // 현재 열려있는 폼이 없으면 프로그램 종료
       if (Application.OpenForms.Count == 0)
       {
           Application.Exit();
       }
   }

=>  

  private void 메모장_FormClosing(object sender, FormClosingEventArgs e)
  {
      if (isTextChanged)
      {
          DialogResult result = MessageBox.Show("변경된 내용을 저장하시겠습니까?", "저장 확인", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

          if (result == DialogResult.Yes)
          {
              SaveFile();
          }
          else if (result == DialogResult.Cancel)
          {
              e.Cancel = true; // 취소 선택 시 창을 닫지 않음
          }
      }
      if (Application.OpenForms.Count == 0)
      {
          Application.Exit();
      }
  }
  • 처음에는 끝내기 버튼을 눌렀을 때, 텍스트가 변경되었다면 저장 여부를 물어보는 로직만 구현했었다.
  • 근데 타이틀바에 X표시 부분을 눌렀을 때도, 똑같은 로직으로 돌아가야하는데 FormClosing 이벤트에 저장 여부를 묻는 코드를 넣자 다이얼로그가 두번 호출되는 버그가 생겼다.

 

변경된 코드

 //끝내기
 private void ExitToolTip_Click(object sender, EventArgs e)
 {
     // FormClosing 이벤트를 수동으로 호출하여 종료할 때의 동작을 수행
     메모장_FormClosing(this, new FormClosingEventArgs(CloseReason.UserClosing, false));
 }
 private void 메모장_FormClosing(object sender, FormClosingEventArgs e)
 {
     if (isTextChanged)
     {
         DialogResult result = MessageBox.Show("변경된 내용을 저장하시겠습니까?", "저장 확인", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

         if (result == DialogResult.Yes)
         {
             SaveFile();
         }
         else if (result == DialogResult.Cancel)
         {
             e.Cancel = true; // 취소 선택 시 창을 닫지 않음
         }
     }
 }


 #endregion

 private void 메모장_FormClosed(object sender, FormClosedEventArgs e)
 {
     // 현재 열려있는 폼이 없으면 프로그램 종료
     if (Application.OpenForms.Count == 0)
     {
         Application.Exit();
     }
 }
  • Form이 닫힐 때, 텍스트가 변경됐다면 저장 여부를 물어보는 로직을 작성하고
  • 이를 끝내기를 눌렀을 때도 FormClosing을 호출하여 처리해줬다.
  • "새 창" 기능에서 여러 폼을 띄워줄 때 부모 폼이 죽으면 자식폼들도 다 죽는 문제를 해결하기 위해 Program.cs를 수정한 적이 있다. 이 부분에서 폼을 하나씩 닫는 로직은 돌아가지만 모든 폼을 다 닫아도 프로젝트가 종료되지 않는 버그를 수정하기 위해 FormClosed 이벤트에 코드를 추가해줬다.

 

 

 

6. 이동(줄 이동)

public class MoveDialog : Form
{
    private Label findLabel;
    private RichTextBox textBoxToMove;
    private Button moveButton;
    private Button cancleButton;

    public event EventHandler MoveEvent;
    public int TargetLine => int.Parse(textBoxToMove.Text);
    public string MoveText => textBoxToMove.Text;
    
    public MoveDialog(RichTextBox richtextBox)
    {
        textBoxToMove = richtextBox;
        InitializeUI();
        this.Text = "줄 이동";
        this.Width = 270;
        this.Height = 160;
    }

    private void InitializeUI()
    {
        findLabel = new Label();
        textBoxToMove = new RichTextBox();
        moveButton = new Button();
        cancleButton = new Button();

        //라벨
        findLabel.Text = "줄 번호(L) :";
        findLabel.Location = new Point(10, 10);
        findLabel.AutoSize = true;

        //찾을 내용 TextBox
        textBoxToMove.Height = 30;
        textBoxToMove.Width = 230;
        textBoxToMove.Location = new Point(10, 40);
        textBoxToMove.KeyPress += TextBoxToMove_KeyPress;

        //이동 버튼
        moveButton.Text = "이동";
        moveButton.Height = 25;
        moveButton.Width = 70;
        moveButton.Location = new Point(90, 80);
        moveButton.Click += MoveButton_Click;

        //취소 버튼
        cancleButton.Text = "취소";
        cancleButton.Height = 25;
        cancleButton.Width = 70;
        cancleButton.Location = new Point(170, 80);
        cancleButton.Click += CancelButton_Click;

        // Add controls to the form
        Controls.Add(findLabel);
        Controls.Add(textBoxToMove);
        Controls.Add(moveButton);
        Controls.Add(cancleButton);
    }

    private void MoveButton_Click(object sender, EventArgs e)
    {
        MoveEvent?.Invoke(this, EventArgs.Empty);
    }

    private void CancelButton_Click(object sender, EventArgs e)
    {
        // 취소 버튼 클릭 시 폼 닫기
        this.Close();
    }

    // TextBox 입력 제한 - 숫자만 입력 가능하도록
    private void TextBoxToMove_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
        {
            e.Handled = true;
        }
    }

}
  • 폼을 추가해주고, 숫자만 입력할 수 있게 TextBoxToMove_KeyPress에 입력했다. 원래는 문자를 입력하면 툴팁을 추가하려 했으나 추후에 추가할 예정이다.
  •  
//이동(Ctrl+G)
private void MoveTextToolTip_Click(object sender, EventArgs e)
{
    ShowDialogs("move");
}

// 찾기 다이얼로그 표시
private void ShowDialogs(string spec)
{
    if(spec == "find")
    {
        if (findDialog == null || findDialog.IsDisposed)
        {
            findDialog = new FindDialog(MyTextArea);
            findDialog.Show();
        }
        else
        {
            findDialog.BringToFront();
        }
    }
    else if(spec == "move")
    {
        if (moveDialog == null || moveDialog.IsDisposed)
        {
            moveDialog = new MoveDialog(MyTextArea);
            moveDialog.MoveEvent += MoveDialog_MoveEvent;
            moveDialog.Show();
        }
        else
        {
            moveDialog.BringToFront();
        }

    }
}



 // 줄 이동 이벤트 핸들러
 private void MoveDialog_MoveEvent(object sender, EventArgs e)
 {
     // MoveDialog에서 줄 이동 버튼을 클릭하면 호출되는 이벤트 핸들러
     MoveDialog moveDialog = sender as MoveDialog;
     if (moveDialog != null)
     {
         // 줄 이동 처리
         int targetLine = moveDialog.TargetLine;
         if (targetLine >= 1 && targetLine <= MyTextArea.Lines.Length)
         {
             // 해당 줄로 커서 이동
             MyTextArea.SelectionStart = MyTextArea.GetFirstCharIndexFromLine(targetLine - 1);
             MyTextArea.ScrollToCaret();
             MyTextArea.Focus();
             moveDialog.Close();
         }
         else
         {
             // 유효하지 않은 줄 번호일 경우 메시지 표시
             MessageBox.Show("유효하지 않은 줄 번호입니다.", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
         }
     }
 }

 

반응형