티스토리 뷰
C# 프로그래밍을 하다 보면 글자색이나 배경색을 선택할 때 위의 그림처럼 사용자 지정, 웹, 시스템으로 크게 나누어 미리 정의된 색상을 선택할 수 있다. 물론 각 색상에는 영문으로 색상 이름이 부여되어 있기도 하다. 통상 색상을 선택하거나 바꾸는 작업에는 별 문제가 없다. 그런데, 사용자로 하여금 특정 요소의 색상을 바꿀 수 있도록 기능을 제공하는 경우는 이야기가 조금 달라진다. 이런 기능을 제공할 때는 색상 선택, 선택한 색상 정보 저장, 저장한 색상 정보를 컨트롤에 적용하는 단계들이 개입하는데 각 단계별로 색상 정보를 어떻게 다룰지가 전체적인 기능 구현에 영향을 미친다.
색상을 선택하는 단계는 ColorDialog 컨트롤을 가져다 사용하면 간단히 해결된다고 생각할 수 있다. 물론 대화창 인스턴스의 Color 속성을 가지고 텍스트의 색상이나 배경색으로 바로 적용시키는 경우는 문제가 될 것이 없다. 문제는 이렇게 사용자가 선택한 색상 정보를 데이터베이스나 파일에 저장하고 읽어서 컨트롤에 적용하는 경우이다. Color 속성의 멤버를 보면 .Name 으로는 문자열로 된 색상 이름을 얻을 수 있고 .R, .G, .B, .A 멤버들로는 상세한 색상 정보를 얻을 수 있는데 코드 가독성이나 편리성 등을 생각하면 당연히 영문으로 된 색상 이름을 사용하는 것이 좋다. 그런데 ColorDialog 컨트롤에서 선택하는 색상 모두에 이름이 붙어 있는 것이 아니라는 것에 문제점이 있는 것이다. 이름이 없는 색상을 선택한 경우 영문 문자열의 색상 이름이 아니기 때문에 나중에 Color.FromName("White")과 같은 방식으로 색상을 생성하는 경우 원하지 않는 결과를 초래하게 되는 것이다.
만약 프로그램의 성격이 아주 상세한 색상을 선택할 수도 있는 경우라면 Color 속성의 .R, .G, .B, .A 멤버들을 모두 저장하고 Color.FromArgb()로 색상을 생성하는 것이 적절한 방법이다. 그렇지 않고 프로그램의 색상 선택이 단순하고 코드의 가독성과 편리성을 유지하고 싶다면 이름이 붙은 색상만 선택할 수 있는 별도의 대화창 컨트롤을 제작해야만 한다. 이번 포스팅에서는 이름이 붙은 웹 색상만 선택하는 대화창으로 이 문제를 해결하는 과정을 다루고자 한다.
새롭게 만들 컨트롤의 구조는 컨트롤을 생성할 때 기존 색상을 넘겨주면, 클래스가 생성되고 로드되면서 폼에 이름이 있는 웹 색상들을 동적 컨트롤로 추가하고 선택한 색상을 .selected 속성으로 전달받을 수 있도록 하는 것이다. 폼에서 특정 색상을 클릭하면 화살표로 어떤 색상이 선택되고 있는지 표시한다. 일단 윈도 폼을 추가하고 위와 같이 동적 컨트롤을 담을 빈 공간과 대화창 종료를 위한 버튼을 추가한다. 필자의 경우 가로 364, 세로 427 픽셀 크기로 폼을 제작했다.
public partial class WebColorDlg : Form
{
const int column_cnt = 12;
string initclr = "";
public WebColorDlg(string clr)
{
initclr = clr;
InitializeComponent();
}
public Color selected { get; set; }
private void WebColors_Load(object sender, EventArgs e)
{
var webColors =
Enum.GetValues(typeof(KnownColor))
.Cast<KnownColor>()
.Where(k => k >= KnownColor.Transparent && k < KnownColor.ButtonFace)
.Select(k => Color.FromKnownColor(k))
.OrderBy(c => c.GetHue())
.ThenBy(c => c.GetSaturation())
.ThenBy(c => c.GetBrightness()).ToList();
int boxsize = ClientSize.Width / column_cnt;
for (int i = 0; i < webColors.Count; i++)
{
Label lbl = new Label();
lbl.Text = "";
lbl.AutoSize = false;
lbl.Size = new Size(boxsize - 2, boxsize - 2);
lbl.BackColor = webColors[i];
lbl.BorderStyle = BorderStyle.FixedSingle;
lbl.Location = new Point(boxsize * (i % column_cnt) + 1, boxsize * (i / column_cnt) + 1);
if (initclr.Equals(webColors[i].Name))
{
curclr.Location = lbl.Location;
}
lbl.Click += (ss, ee) =>
{
selected = lbl.BackColor;
curclr.Location = lbl.Location;
};
Controls.Add(lbl);
}
}
private void cb_cancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void button2_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
코드를 보면 column_cnt에 한 줄에 표시할 색상의 개수를 지정하면 되는데 이 값을 기준으로 대화창의 폭을 이 값으로 나누어 색상을 표시할 박스의 크기를 계산한다. ColorDialog에서는 각 색상을 선택할 때 외부에 박스를 덧 입히는 방식으로 현재 색상을 표시하지만 위의 예제에서는 curclr이라는 아이콘 이미지를 현재 색상에 위치시키는 방식을 적용했다.
이름이 있는 웹 색상만 표시하는 대화창의 원리는 위의 그림에서 보듯이 KnownColor 라는 시스템 정의 enum에서 찾을 수 있는데 열거형 자료에서 투명색 이후부터 KnownColor.ButtonFace 이전 까지를 웹 색상으로 간주하고 각 색상의 색조, 채도, 밝기를 기준으로 정렬해서 비슷한 계열의 색상끼리 모아 주는 작업을 LINQ 구문으로 해결하고 있다.
이렇게 웹 색상만을 표시하는 대화창을 사용하면 Color 속성의 .Name으로 색상 정보를 추출하고 저장한 다음 다시 Color.FromName()로 색상을 생성하는 과정 전체에 걸쳐 문제없이 처리가 가능하다.
'프로그래밍' 카테고리의 다른 글
C# 압축 라이브러리 적용하기 (0) | 2020.04.30 |
---|---|
C# 32비트와 64비트를 모두 지원하는 SQLite 응용 프로그램 개발하기 (0) | 2020.04.30 |
C# 자주쓰는 타입 변환 - byte, string, int (0) | 2020.02.19 |
C#에서 시스템 시간 설정하기, 관리자 권한으로 시작프로그램 등록하기 (0) | 2020.01.23 |
WinForm에 가상 키보드 내장시키기 - C# (0) | 2019.12.18 |