클래스의 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 이와 같은 디자인 유형을 싱글턴 패턴(Singleton pattern)이라고 한다1. 정적 클래스(Static Class)와 비교해 싱글턴은 인터페이스 구현, 비동기 지원2 등 OOP에 더 어울리는 장점3을 가지고 있다.
Winform 응용프로그램에서 단일 인스턴스를 유지하고 이에 따라 하위 Winform이 프로그램 종료 시점까지 그 상태를 유지하기 원한다면 Winform을 싱글턴 패턴으로 만들어 사용하면 된다. 즉 하위 폼을 호출할 때 새로운 인스턴스(new)가 생성되는 것을 방지하고 처음에 생성한 폼을 지속해서 유지할 때 유용하다. 더욱 자세한 소스는 여기(github)에서 볼 수 있다.
Program.cs
using System;
using System.Windows.Forms;
namespace WinFormsApp
{
internal static class Program
{
[STAThread]
private static void Main()
{
// https://docs.microsoft.com/dotnet/api/system.windows.forms.highdpimode
// DpiUnaware, DpiUnawareGdiScaled, PerMonitor, PerMonitorV2, SystemAware
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
Application.EnableVisualStyles(); // Theme Enable
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(FormMain.Go); // Singleton Form
}
}
}
FormMain.cs
// 메인폼, 메인패널(하위폼컨테이터), 버튼A(하위폼A호출), 버튼B(하위폼B호출)
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WinFormsApp
{
public partial class FormMain : Form
{
private static readonly Lazy<FormMain> instance = new(() => new FormMain());
public static FormMain Go => instance.Value;
private FormMain()
{
InitializeComponent();
SetScreenSize(ScreenSize.Normal);
}
public enum ScreenSize
{
Full,
Max,
Normal,
MaxNoneBS
}
public Panel PanelFormMain
{
get => PanelMain;
set => PanelMain = value;
}
public string LogMessage
{
get => TextBoxMain.Text;
set => TextBoxMain.Text = $"[{DateTime.Now:yyyy-MM-dd HH.mm.ss}] {value}";
}
private bool IsSystemShutdown = false;
private void ButtonFormA_Click(object sender, System.EventArgs e)
{
FormA.Go.Show();
FormA.Go.BringToFront();
}
private void ButtonFormB_Click(object sender, System.EventArgs e)
{
FormB.Go.Show();
FormB.Go.BringToFront();
}
private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
{
FormB.Go.Dispose();
FormA.Go.Dispose();
}
// Desktop FullScreen or Maximized
private void SetScreenSize(ScreenSize screenSize)
{
switch (screenSize)
{
case ScreenSize.Full:
WindowState = FormWindowState.Normal;
FormBorderStyle = FormBorderStyle.None;
Bounds = Screen.PrimaryScreen.Bounds;
//this.Bounds = Screen.GetBounds(this);
break;
case ScreenSize.Max:
WindowState = FormWindowState.Maximized;
FormBorderStyle = FormBorderStyle.Sizable;
break;
case ScreenSize.Normal:
WindowState = FormWindowState.Normal;
FormBorderStyle = FormBorderStyle.Sizable;
break;
case ScreenSize.MaxNoneBS:
var rectangle = Screen.FromControl(this).Bounds;
FormBorderStyle = FormBorderStyle.None;
Size = new Size(rectangle.Width, rectangle.Height);
Location = new Point(0, 0);
Rectangle workingRectangle = Screen.PrimaryScreen.WorkingArea;
Size = new Size(workingRectangle.Width, workingRectangle.Height);
break;
}
}
}
}
FormA.cs
using System;
using System.Windows.Forms;
namespace WinFormsApp
{
public sealed partial class FormA : Form
{
private static readonly Lazy<FormA> instance = new(() => new FormA());
public static FormA Go => instance.Value;
private FormA()
{
InitializeComponent();
TopLevel = false;
FormBorderStyle = FormBorderStyle.None;
Dock = DockStyle.Fill;
FormMain.Go.PanelFormMain.Controls.Add(this);
FormMain.Go.PanelFormMain.Tag = this;
}
private void ButtonA_Click(object sender, EventArgs e)
{
// (Application.OpenForms[nameof(FormMain)].Controls["TextBoxMain"] as TextBox).Text = "Form A LOG";
FormMain.Go.LogMessage = "Form A LOG"; // Property Access
Hide();
}
}
}
FormB.cs
using System;
using System.Windows.Forms;
namespace WinFormsApp
{
public sealed partial class FormB : Form
{
private static readonly object locker = new();
private static FormB instance;
public static FormB Go
{
get
{
if (instance == null || instance.IsDisposed)
{
lock (locker)
{
if (instance == null || instance.IsDisposed)
{
instance = new FormB();
}
}
}
return instance;
}
}
private FormB()
{
InitializeComponent();
TopLevel = false;
FormBorderStyle = FormBorderStyle.None;
Dock = DockStyle.Fill;
FormMain.Go.PanelFormMain.Controls.Add(this);
FormMain.Go.PanelFormMain.Tag = this;
}
private void ButtonB_Click(object sender, EventArgs e)
{
FormMain.Go.LogMessage = "Form B LOG"; // Property Access
Close();
}
}
}