bad coding practice (FA Team)
ComboBox 패턴
[ bad coding ]
아래 코드는 유진제지 프로젝트에 ComboBoxEx.cs 파일에 작성된 코드
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를 제외한 같은 이름의 프로그램이 실행 중인지를 판단하여 실행된 응용 프로그램을 종료
private void MainForm_Load(object sender, EventArgs e)
{
Process[] running_process = Process.GetProcessesByName("GST_IOT");
if(running_process.Length > 1)
{
Messagebox = new POPUP.MessageBox("프로그램이 이미 실행중입니다.", true);
Messagebox.ShowDialog();
Close();
}
else
{
//...
}
}static class Program
{
[STAThread]
static void Main()
{
Process[] Processes = Process.GetProcessesByName(nameof(IoT468));
if (Processes.Any(x => !x.Id.Equals(Process.GetCurrentProcess().Id)))
{
MessageBox.Show("이미 실행중");
Application.Exit();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}etc.
Last updated
Was this helpful?