ComboBoxEx 클래스, ComboBoxExItems 클래스의 정의가 굳이 필요하지 않음
ComboBoxEx 클래스에 findSelectKey, findSelectValue 메서드의 경우 자주 사용되는게 아니라면 정의할 필요가 없고, 자주 사용한다면 [#A03의 예시] 처럼 기능을 조금더 확장시켜 활용 가능
GetKey, GetValue 는 메서드로 정의할 필요 없음 [#A04 예시 참고]
public partial class ComboBoxEx : ComboBox
{
public ComboBoxEx()
{
DisplayMember = "DisplayEx";
ValueMember = "ValueEx";
this.ForeColor = Color.Black;
}
public ComboBoxEx(ComboBoxExItems items)
{
this.DisplayMember = "DisplayEx";
this.ValueMember = "ValueEx";
this.DataSource = items.getItems();
}
public void setDataSource(ComboBoxExItems items)
{
this.DataSource = items.getItems();
}
public void findSelectKey(string _key)
{
foreach (ComboBoxExItem cbitem in this.Items)
{
if (cbitem.DisplayEx.Contains(_key))
{
this.SelectedItem = cbitem;
break;
}
}
}
public bool findSelectValue(string _value)
{
foreach (ComboBoxExItem cbitem in this.Items)
{
if (cbitem.ValueEx == _value)
{
this.SelectedItem = cbitem;
return true;
}
}
return false;
}
public string GetKey()
{
if (this.SelectedItem != null)
return ((ComboBoxExItem)this.SelectedItem).DisplayEx;
return null;
}
public string GetValue()
{
if (this.SelectedItem != null)
return ((ComboBoxExItem)this.SelectedItem).ValueEx;
return null;
}
}
public class ComboBoxExItems
{
List<ComboBoxExItem> items = new List<ComboBoxExItem>();
public void Add(ComboBoxExItem item)
{
items.Add(item);
}
public void Clear()
{
items.Clear();
}
public List<ComboBoxExItem> getItems()
{
return items;
}
}
public class ComboBoxExItem
{
public string DisplayEx { get; set; }
public string ValueEx { get; set; }
}
[ good coding ]
#A01 - ComboBox의 Item으로 사용할 자료형 정의
필요에 따라 Property 멤버를 응용할 것
System.Windows.Forms.ComboBox 는 기본적으로 Items에 바인딩된 개체의 ToString 메서드를 호출하여 Item의 값을 화면에 표현하므로, Item으로 사용할 자료형에 ToString 메서드를 재정의하여 사용하면 ComboBox.DisplayMember, ComboBox.ValueMember 속성에 매핑이 필요하지 않음
internal class ComboBoxItem
{
public string Code { get; set; }
public string Name { get; set; }
public ComboBoxItem(string code, string name)
{
Code = code;
Name = name;
}
public override string ToString()
{
return Name;
}
}
#A02 - Items 초기화
고정된 항목 세팅 (DB로부터 코드,코드명 정보를 읽어오지 않아도 되는 경우)
private void InitComboBoxItems()
{
var items = new List<ComboBoxItem>();
// comboBox1 에 대한 처리
items.Add(new ComboBoxItem(string.Empty, string.Empty)); // 선택하지 않은 빈 값
items.Add(new ComboBoxItem("A", "에이"));
items.Add(new ComboBoxItem("B", "비"));
items.Add(new ComboBoxItem("C", "씨"));
items.Add(new ComboBoxItem("D", "디"));
comboBox1.Items.AddRange(items.ToArray());
comboBox1.SelectedIndex = 0;
// comboBox2 에 대한 처리
items.Clear();
items.Add(new ComboBoxItem(string.Empty, string.Empty));
items.Add(new ComboBoxItem("100", "코드100"));
items.Add(new ComboBoxItem("200", "코드200"));
comboBox2.Items.AddRange(items.ToArray());
comboBox2.SelectedIndex = 0;
}
DB에 저장된 코드,코드명 정보를 읽어와 항목 세팅
private void InitComboBoxItems()
{
DataSet result = SqlHelper.ExecuteSql("SELECT user_id, user_name FROM sysUserMaster WHERE rtrchk = 'N'");
DataTable resultTable = result?.Tables?.Cast<DataTable>()?.ElementAtOrDefault(0);
var items = new List<ComboBoxItem>();
items.Add(new ComboBoxItem(string.Empty, string.Empty)); // 선택하지 않은 빈 값
// 쿼리 결과가 있을때만 Item 세팅
if (resultTable != null)
{
foreach (DataRow row in resultTable.Rows)
{
string userId = row.GetValue("user_id")?.ToString() ?? string.Empty;
string userName = row.GetValue("user_name")?.ToString() ?? string.Empty;
items.Add(new ComboBoxItem(userId, userName));
}
}
comboBox1.Items.AddRange(items.ToArray());
comboBox1.SelectedIndex = 0;
}
#A03 - ComboBox에서 Item 찾기
internal static class InternalUtil
{
static bool FindComboItemByName(this ComboBox comboBox, string name, out string code)
{
var comboItem = comboBox.Items.Cast<ComboBoxItem>().FirstOrDefault(x => x.Name.Equals(name));
code = comboItem?.Code;
return comboItem != null;
}
static bool FindComboItemByKey(this ComboBox comboBox, string key, out string name)
{
var comboItem = comboBox.Items.Cast<ComboBoxItem>().FirstOrDefault(x => x.Name.Equals(key));
name = comboItem?.Name;
return comboItem != null;
}
}
#A04 - ComboBox에서 선택된 Item을 캐스팅하여 특정 멤버 참조
// ComboBox에 선택된 아이템의 Code 가져오기
string code = (comboBox1.SelectedItem as ComboBoxItem)?.Code;
// ComboBox에 선택된 아이템의 Name 가져오기
string name = (comboBox1.SelectedItem as ComboBoxItem)?.Name;
#B01 - Form.ShowDialog / 팝업을 통한 값 세팅
아래 코드는 유진제지 프로젝트에 불량 정보를 입력받는 폼(badqty class)에서 불량 유형을 선택하기 위해 불량 유형 폼(bad_NM class)으로부터 값을 처리위해 작성된 코드의 일부
※ 객체지향에서 일반적으로 어떤 객체가 다른 객체를 참조하여 멤버를 직접 업데이트하는 방식은 좋은 코딩이 아님
[ bad coding ]
솔루션팀에서 개발한 거의 모든 코드에 Form 간 데이터 참조 방식이 생성자에서 부모 폼의 객체를 전달하는 방식으로 이루어져 있음
실행하려는 팝업 폼을 클래스의 필드 멤버로 구성할 이유가 없음 (예시의 badqty 클래스에 private bad_NM bad_NM; )
캡슐화 위반 : bad_NM 클래스에서 생성자를 통해 전달받은 badqty Home 객체의 lbl_Bad_NM 멤버와 lbl_bad_code 멤버의 값을 수정하기 위해 public 으로 접근 제한자 지정 (badqty.Designer.cs 참고)
DataGridView.CellClick 이벤트가 발생하는 시점에 lbl_Bad_NM와 lbl_bad_code의 Text 속성에 값을 변경하고, button4_Click_1 이벤트 핸들러에서 불량 유형 선택 판단 조건에 lbl_Bad_NM.Text에 값 할당 여부를 확인하는 방식은 lbl_Bad_NM.Text 속성에 값이 언제 변경되는지를 확인해야 하므로 직관적이지 못함
[ good coding ]
불량 정보 입력 팝업(BadInfoSetPopup class)에서 불량 코드를 세팅하기 위한 팝업(BadCodePopup class)의 객체를 버튼이 클릭되는 시점에 생성하며, BadCode와 BadCodeName 멤버의 값을 참조하여 처리
DataGridView 컨트롤에 항목 초기화 (InitGridViewItem 메서드)
불량 유형 선택된 여부는 DataGridView.CurrentRow 속성으로 판단
/********** badqty.cs **********/
public partial class badqty : Form
{
MainForm Home;
private bad_NM bad_NM;
public badqty(MainForm home)
{
InitializeComponent();
this.Home = home;
}
// ...
private void btn_bad_Click_1(object sender, EventArgs e)
{
bad_NM = new bad_NM(this);
bad_NM.ShowDialog();
}
}
/********** badqty.Designer.cs **********/
partial class badqty
{
//...
public System.Windows.Forms.Label bad_code;
public System.Windows.Forms.Button btn_bad;
//...
}
/********** bad_NM.cs **********/
public partial class bad_NM : Form
{
badqty Home;
public bad_NM(badqty home)
{
InitializeComponent();
Home = home;
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
lbl_Bad_NM.Text = dataGridView1.CurrentRow.Cells["col_bad_type"].Value.ToString().Trim();
lbl_bad_code.Text = dataGridView1.CurrentRow.Cells["col_bad_code"].Value.ToString().Trim();
}
private void button4_Click_1(object sender, EventArgs e)
{
if (lbl_Bad_NM.Text != " " || lbl_Bad_NM != null)
{
Home.btn_bad.Text = lbl_Bad_NM.Text.ToString().Trim();
Home.bad_code.Text = lbl_bad_code.Text.ToString().Trim();
this.Close();
}
else
{
MessageBox.Show("불량유형을 터치해주세요.", "경고", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
/********** BadInfoSetPopup.cs **********/
public partial class BadInfoSetPopup : Form // 불량 정보 입력 팝업
{
public BadInfoSetPopup()
{
InitializeComponent();
btnBadCode.Click += OnClick;
}
private void OnClick(object sender, EventArgs e)
{
var popup = new BadCodePopup();
if (popup.ShowDialog() == DialogResult.OK)
{
lblBadCode.Text = popup.BadCode;
btnBadCode.Text = popup.BadCodeName;
}
}
}
/********** BadCodePopup.cs **********/
public partial class BadCodePopup : Form // 불량유형 선택 팝업
{
public string BadCode { get; private set; }
public string BadCodeName { get; private set; }
public BadCodePopup()
{
InitializeComponent();
dataGridView1.AllowUserToAddRows = false;
dataGridView1.ReadOnly = true;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.Columns.Add("code", "코드");
dataGridView1.Columns.Add("name", "코드명");
InitGridViewItem();
//InitGridViewItemFromDB(); // DB로부터 코드 세팅
btnOk.Click += OnClick;
dataGridView1.SelectionChanged += (s, e) => Console.WriteLine("SelectionChanged");
}
private void InitGridViewItem()
{
dataGridView1.Rows.Add("A", "에이");
dataGridView1.Rows.Add("B", "비");
}
private void InitGridViewItemFromDB()
{
string query = $@"
SELECT sub_code as code,
code_name as name
FROM comCodeMaster
WHERE group_code = 'QC002' ";
DataTable resultTable =
MainForm.DBMain.ExecuteSql(query)?
.Tables?
.Cast<DataTable>()?
.FirstOrDefault();
foreach (DataRow row in resultTable.Rows)
{
string code = row.GetValue("code")?.ToString() ?? string.Empty;
string name = row.GetValue("name")?.ToString() ?? string.Empty;
dataGridView1.Rows.Add(code, name);
}
}
private void OnClick(object sender, EventArgs e)
{
DataGridViewRow selectedRow = dataGridView1.CurrentRow;
if (selectedRow == null)
{
MessageBox.Show("불량유형을 터치해주세요.", "경고", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
this.BadCode = selectedRow.Cells["code"].Value.ToString();
this.BadCodeName = selectedRow.Cells["name"].Value.ToString();
DialogResult = DialogResult.OK;
this.Close();
}
#C01 - Timer
[ bad coding ]
public partial class Main : Form
{
System.Threading.Timer Main_Timer;
System.Threading.Timer Run_Timer;
System.Threading.Timer Main_Load_Timer;
public Main()
{
InitializeComponent();
Main_Timer = new System.Threading.Timer(Main_Callback);
Run_Timer = new System.Threading.Timer(Run_Callback);
Main_Load_Timer = new System.Threading.Timer(Main_Load_Callback);
}
void Main_Callback(object s)
{
작업경과시간 = stopwatch.Elapsed.Add(time).ToString().Split('.')[0];
SetLabel(lb_작업시간, 작업경과시간);
SetLabel(lb_생산수량, 생산수량.ToString());
}
void Main_Load_Callback(object s)
{
digitalDisplayControl1.DigitText = DateTime.Now.ToString("HH:mm:ss");
}
void Run_Callback(object s)
{
DB데이터업로드();
}
}
#D01 - 프로그램이 실행중인지 판단하여 중복 실행 제어
Form이 Load 되기 전에 처리하며, 자신의 프로세스ID를 제외한 같은 이름의 프로그램이 실행 중인지를 판단하여 실행된 응용 프로그램을 종료