using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; // 둥근 모서리 그리기(GraphicsPath)에 필수 using System.IO; using System.Text; using System.Windows.Forms; using System.Xml.Linq; namespace ControlServer { public partial class Form3 : Form { private Form1 _mainForm; // 메인 폼 참조 변수 private string _targetIP; private string _filePath; // 둥근 모서리 반지름 설정 (정수형 int로 선언) private const int CornerRadiusValue = 15; public Form3(Form1 mainForm, string terminalName, string terminalIP, string filePath) { InitializeComponent(); // 1. 버튼 테두리 없애고 Flat 스타일 및 이벤트 연결 ConfigureModernStyles(); // 2. 그리드 디자인을 오른쪽 시안 스타일에 맞게 초기화 InitDataGridViewStyles(); _mainForm = mainForm; _targetIP = terminalIP; _filePath = filePath; lblTerminalName.Text = terminalName; lblTerminalIP.Text = terminalIP; string[] timeData = _mainForm.GetProgramTimesByIp(_targetIP); txtBoxDisplay.Text = string.IsNullOrEmpty(timeData[0]) ? "5" : timeData[0]; if (!string.IsNullOrEmpty(timeData[1]) && timeData[1].Contains(":")) { string[] onTime = timeData[1].Split(':'); txtBoxOnYY.Text = onTime[0]; txtBoxOnMM.Text = onTime[1]; txtBoxOnSS.Text = onTime[2]; } if (!string.IsNullOrEmpty(timeData[2]) && timeData[2].Contains(":")) { string[] offTime = timeData[2].Split(':'); txtBoxOffYY.Text = offTime[0]; txtBoxOffMM.Text = offTime[1]; txtBoxOffSS.Text = offTime[2]; } txtBoxOnYY.TextChanged += ValidateInputs; txtBoxOnMM.TextChanged += ValidateInputs; txtBoxOnSS.TextChanged += ValidateInputs; txtBoxOffYY.TextChanged += ValidateInputs; txtBoxOffMM.TextChanged += ValidateInputs; txtBoxOffSS.TextChanged += ValidateInputs; txtBoxOnYY.PlaceholderText = "YY"; txtBoxOnMM.PlaceholderText = "MM"; txtBoxOnSS.PlaceholderText = "SS"; txtBoxOffYY.PlaceholderText = "YY"; txtBoxOffMM.PlaceholderText = "MM"; txtBoxOffSS.PlaceholderText = "SS"; LoadAndParseFile(); } // ⭐️ 오른쪽 그리드뷰 디자인 시안을 반영한 메서드 // ⭐️ 오른쪽 그리드뷰 디자인 시안을 반영한 메서드 private void InitDataGridViewStyles() { grdDataList.AllowUserToResizeRows = false; grdDataList.AllowUserToResizeColumns = false; grdDataList.RowHeadersVisible = false; // 왼쪽 화살표 열 숨김 grdDataList.AllowUserToAddRows = false; // 빈 칸 추가 방지 grdDataList.ReadOnly = true; // 읽기 전용 grdDataList.SelectionMode = DataGridViewSelectionMode.FullRowSelect; // 행 선택 grdDataList.MultiSelect = false; // 커스텀 헤더 스타일을 적용하기 위해 반드시 false 설정 grdDataList.EnableHeadersVisualStyles = false; // 그리드 전체 배경 및 기본 테두리 제거 (시안처럼 하얗고 투명하게) grdDataList.BackgroundColor = Color.White; grdDataList.BorderStyle = BorderStyle.None; // 셀 구분선 설정 (옅은 회색 가로선만 남김) grdDataList.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal; grdDataList.GridColor = Color.FromArgb(240, 242, 245); // [상단 헤더 스타일 수정] DataGridViewCellStyle headerStyle = new DataGridViewCellStyle(); headerStyle.BackColor = Color.FromArgb(44, 62, 80); headerStyle.ForeColor = Color.White; headerStyle.Font = new Font("맑은 고딕", 9.5F, FontStyle.Bold); headerStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; // 행을 선택하더라도 헤더의 선택 배경색/글자색이 기본 배경색과 똑같이 유지되도록 고정 headerStyle.SelectionBackColor = Color.FromArgb(44, 62, 80); headerStyle.SelectionForeColor = Color.White; grdDataList.ColumnHeadersDefaultCellStyle = headerStyle; grdDataList.ColumnHeadersHeight = 38; // 헤더 높이 여유롭게 조정 grdDataList.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing; // 행 선택 시 헤더 스타일 상속으로 인한 테두리 및 색상 왜곡 방지 grdDataList.EnableHeadersVisualStyles = false; // [일반 데이터 셀 스타일 수정] DataGridViewCellStyle cellStyle = new DataGridViewCellStyle(); cellStyle.BackColor = Color.White; cellStyle.ForeColor = Color.FromArgb(50, 50, 50); // 부드러운 차콜 블랙 cellStyle.Font = new Font("맑은 고딕", 9F, FontStyle.Regular); cellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; // 행 선택(클릭) 시 파란색 대신 부드러운 회청색으로 채우기 cellStyle.SelectionBackColor = Color.FromArgb(242, 246, 252); cellStyle.SelectionForeColor = Color.FromArgb(50, 50, 50); grdDataList.DefaultCellStyle = cellStyle; // 시안처럼 행 간격(높이)을 시원하게 벌려 가시성을 높임 grdDataList.RowTemplate.Height = 38; grdDataList.ClearSelection(); } private void ConfigureModernStyles() { // 수정 버튼 스타일 설정 (테두리 삭제) btnUpdate.FlatStyle = FlatStyle.Flat; btnUpdate.FlatAppearance.BorderSize = 0; // 취소 버튼 스타일 설정 (테두리 삭제 및 부드러운 회색) btnCancel.FlatStyle = FlatStyle.Flat; btnCancel.FlatAppearance.BorderSize = 0; btnCancel.BackColor = Color.FromArgb(230, 235, 240); btnCancel.ForeColor = Color.FromArgb(80, 80, 80); // 둥근 처리를 위한 Paint 이벤트 연결 if (lblBox != null) lblBox.Paint += lblBox_Paint; btnUpdate.Paint += Button_Paint; btnCancel.Paint += Button_Paint; } // 둥근 모서리 경로를 만들어주는 공통 메서드 private GraphicsPath GetRoundPath(Rectangle bounds, int radius) { GraphicsPath path = new GraphicsPath(); int diameter = radius * 2; path.AddArc(bounds.X, bounds.Y, diameter, diameter, 180, 90); path.AddArc(bounds.Right - diameter, bounds.Y, diameter, diameter, 270, 90); path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90); path.AddArc(bounds.X, bounds.Bottom - diameter, diameter, diameter, 90, 90); path.CloseAllFigures(); return path; } // 흰색 배경 박스 둥글게 자르기 private void lblBox_Paint(object? sender, PaintEventArgs e) { if (sender is Control control) { e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; using (GraphicsPath path = GetRoundPath(new Rectangle(0, 0, control.Width, control.Height), CornerRadiusValue)) { control.Region = new Region(path); } } } // 버튼들 둥글게 자르기 private void Button_Paint(object? sender, PaintEventArgs e) { if (sender is Button button) { e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; using (GraphicsPath path = GetRoundPath(new Rectangle(0, 0, button.Width, button.Height), 10)) { button.Region = new Region(path); } } } private void ValidateInputs(object? sender, EventArgs e) { bool isOnYYEmpty = string.IsNullOrWhiteSpace(txtBoxOnYY.Text); bool isOnMMEmpty = string.IsNullOrWhiteSpace(txtBoxOnMM.Text); bool isOnSSEmpty = string.IsNullOrWhiteSpace(txtBoxOnSS.Text); bool isOffYYEmpty = string.IsNullOrWhiteSpace(txtBoxOffYY.Text); bool isOffMMEmpty = string.IsNullOrWhiteSpace(txtBoxOffMM.Text); bool isOffSSEmpty = string.IsNullOrWhiteSpace(txtBoxOffSS.Text); bool isTimeValid = (isOnYYEmpty && isOnMMEmpty && isOnSSEmpty && isOffYYEmpty && isOffMMEmpty && isOffSSEmpty) || (!isOnYYEmpty && !isOnMMEmpty && !isOnSSEmpty && !isOffYYEmpty && !isOffMMEmpty && !isOffSSEmpty); btnUpdate.Enabled = isTimeValid; if (isTimeValid) { btnUpdate.BackColor = Color.FromArgb(52, 152, 219); // 활성화 스카이블루 btnUpdate.ForeColor = Color.White; } else { btnUpdate.BackColor = Color.FromArgb(180, 210, 235); // 비활성화 흐린 블루 btnUpdate.ForeColor = Color.White; } } private void btnCancel_MouseClick(object sender, MouseEventArgs e) { this.Close(); } private void btnUpdate_MouseClick(object sender, MouseEventArgs e) { string displayTime = txtBoxDisplay.Text; string onTime = txtBoxOnYY.Text + ":" + txtBoxOnMM.Text + ":" + txtBoxOnSS.Text; string offTime = txtBoxOffYY.Text + ":" + txtBoxOffMM.Text + ":" + txtBoxOffSS.Text; _mainForm.SaveProgramTimesToGrid(_targetIP, displayTime, onTime, offTime); string protocol = $"SET_TIME|{displayTime}\n"; _mainForm.SendDataToSingleAgent(_targetIP, protocol); MessageBox.Show("수정되었습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); this.Close(); } private void LoadAndParseFile() { if (!File.Exists(_filePath)) return; string fileContent = File.ReadAllText(_filePath); if (string.IsNullOrWhiteSpace(fileContent)) return; ParseAndBindServerData(fileContent); } private void ParseAndBindServerData(string rawData) { try { var doc = System.Xml.Linq.XDocument.Parse(rawData); grdDataList.Rows.Clear(); foreach (var item in doc.Descendants("AuctionTable")) { grdDataList.Rows.Add( item.Element("aucDate")?.Value, item.Element("itemName")?.Value, item.Element("origin")?.Value, item.Element("totalQty")?.Value, item.Element("fixPriceQty")?.Value, item.Element("totalAmt")?.Value, item.Element("fixPriceAmt")?.Value ); } } catch (Exception ex) { MessageBox.Show("XML 파싱 에러: " + ex.Message, "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }