지난번 포스팅에 이어서 2025년 지방기능경기대회 정보기술 직종 1 과제 C# 과제 풀이를 작성해 보겠다. 지난 포스팅에서는 공통조건 처리를 위한 SolutionUtil 클래스와 메인폼 풀이까지만 살펴봤었다. 간단하게 정리가 끝날 거라 생각했는데, 내가 풀이했던 것을 글로만 보는 독자들을 위해 풀어서 설명하려고 하니 내용이 매우 방대해지는 것 같다.
로그인 폼 풀이
과제에서는 상단의 RadioButton으로 타입을 선택하여 ID, Email, 휴대폰으로 각각 로그인을 할 수 있게 만들길 원하고 있는 상황이다. 또한, 각 로그인 타입에 맞춰서 화면에 표시되는 Label의 Text도 변경되길 원하고 있다. 이 밖의 다른 조건들은 다음과 같다.
1) 초기 화면 [그림 2-1]과 같이 나타내시오.
2) 상단의 옵션 버튼을 변경하면 [그림 2-2], [그림 2-3]와 같이 화면에 맞게 전환되도록 제어하시오.
3) [로그인] 버튼을 다음과 같이 제어한다.
가. 빈칸이 있을 경우 -> 경고: 빈칸이 있습니다.
나. 로그인 정보가 맞지 않을 경우 -> 경고: 일치하는 회원 정보가 없습니다.
다. 로그인 정보가 일치할 경우 -> 정보: OOO회원님 환영합니다. -> 폼을 닫고 [그림 3-1] 회 원 메뉴 폼으로 이동하시오.
로그인 폼 코딩을 하기에 앞서, 먼저 로그인 폼 디자인은 어떤 식으로 했는지 살펴보도록 하자.
메인폼 디자인에 비해 훨씬 간결하다. RadioButton들은 FlowLayoutPanel 안에 모두 속하게 하여, 별도 코딩 없이 한 그룹에서 1개의 RadioButton만 선택될 수 있게 하였다. 간단한 디자인이니 바로 코드를 살펴보자.
전체 코드
public partial class LoginForm : TemplateForm
{
private int loginType = 1;
public LoginForm()
{
InitializeComponent();
}
/// <summary>
/// Radio Button Click Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_Clicked(object sender, EventArgs e)
{
RadioButton radio = (RadioButton)sender;
this.loginType = int.Parse(radio.Tag.ToString());
switch (this.loginType)
{
case 1: this.lbID.Text = "ID"; break;
case 2: this.lbID.Text = "Email"; break;
case 3: this.lbID.Text = "휴대폰"; break;
}
}
/// <summary>
/// 로그인 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLogin_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(this.txtID.Text) || string.IsNullOrEmpty(this.txtPW.Text))
{
SolutionUtil.ShowErrMessage("빈칸이 있습니다.");
return;
}
user user = null;
switch (this.loginType)
{
case 1: user = this.db.user.SingleOrDefault(x => x.id == this.txtID.Text && x.pw == this.txtPW.Text); break;
case 2: user = this.db.user.SingleOrDefault(x => x.email == this.txtID.Text && x.pw == this.txtPW.Text); break;
case 3: user = this.db.user.SingleOrDefault(x => x.phone == this.txtID.Text && x.pw == this.txtPW.Text); break;
}
if (user == null)
{
SolutionUtil.ShowErrMessage("일치하는 회원 정보가 없습니다.");
return;
}
SolutionUtil.loginUser = user;
SolutionUtil.ShowInfoMessage($"{user.name}회원님 환영합니다.");
this.Close();
new UserMenuForm().ShowDialog();
}
}
변수 설명
private int loginType = 1;
- RadioButton 클릭값을 Form 클래스 내부 변수로 저장하여, 사용자가 로그인 버튼을 클릭했을 때 이 변수 값에 따라서 알맞게 동작할 수 있게 하기 위해 사용한 변수다.
각 이벤트 메서드 코드 설명
/// <summary>
/// Radio Button Click Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_Clicked(object sender, EventArgs e)
{
RadioButton radio = (RadioButton)sender;
this.loginType = int.Parse(radio.Tag.ToString());
switch (this.loginType)
{
case 1: this.lbID.Text = "ID"; break;
case 2: this.lbID.Text = "Email"; break;
case 3: this.lbID.Text = "휴대폰"; break;
}
}
- 개별 RadioButton 별로 Click 이벤트 코드를 생성하는 것이 아니라, 하나의 이벤트 코드만 작성하고 이를 각 RadioButton에 연결하였다. sender 매개변수를 통해 사용자가 클릭한 RadioButton이 어떤 것인지 알아낼 수 있기 때문에 이렇게 구현하는 것이 가능하다.
/// <summary>
/// 로그인 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLogin_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(this.txtID.Text) || string.IsNullOrEmpty(this.txtPW.Text))
{
SolutionUtil.ShowErrMessage("빈칸이 있습니다.");
return;
}
user user = null;
switch (this.loginType)
{
case 1: user = this.db.user.SingleOrDefault(x => x.id == this.txtID.Text && x.pw == this.txtPW.Text); break;
case 2: user = this.db.user.SingleOrDefault(x => x.email == this.txtID.Text && x.pw == this.txtPW.Text); break;
case 3: user = this.db.user.SingleOrDefault(x => x.phone == this.txtID.Text && x.pw == this.txtPW.Text); break;
}
if (user == null)
{
SolutionUtil.ShowErrMessage("일치하는 회원 정보가 없습니다.");
return;
}
SolutionUtil.loginUser = user;
SolutionUtil.ShowInfoMessage($"{user.name}회원님 환영합니다.");
this.Close();
new UserMenuForm().ShowDialog();
}
- if (string.IsNullOrEmpty(this.txtID.Text) || string.IsNullOrEmpty(this.txtPW.Text))
{
SolutionUtil.ShowErrMessage("빈칸이 있습니다.");
return;
}
txtID와 txtPW에 값이 입력되어 있는지 유무를 확인하고 있는 코드다. string에 있는 IsNullOrEmpty 메서드를 사용해서 해당 입력란에 내용이 입력되어 있는지를 확인했다. - user user = null;
switch (this.loginType)
{
case 1: user = this.db.user.SingleOrDefault(x => x.id == this.txtID.Text && x.pw == this.txtPW.Text); break;
case 2: user = this.db.user.SingleOrDefault(x => x.email == this.txtID.Text && x.pw == this.txtPW.Text); break;
case 3: user = this.db.user.SingleOrDefault(x => x.phone == this.txtID.Text && x.pw == this.txtPW.Text); break;
}
로그인 user를 찾기 위해 먼저 변수를 초기화하고, loginTyep 변수 값에 따라 알맞은 user 정보를 찾아오는 코드다. SingleOrDefault 메서드를 사용해서 사용자가 입력한 id/email/phone 값과 Pw 값이 동시에 일치하는 user 정보를 찾아오게끔 했다. 만약, 해당하는 user가 존재하지 않으면 이 메서드는 null 값을 반환하게 된다. - if (user == null)
{
SolutionUtil.ShowErrMessage("일치하는 회원 정보가 없습니다.");
return;
}
해당하는 user 정보를 찾지 못한 경우 요구사항에 적혀 있는 경고 메시지가 나타나게 했다. - SolutionUtil.loginUser = user;
SolutionUtil.ShowInfoMessage($"{user.name}회원님 환영합니다.");
this.Close();
new UserMenuForm().ShowDialog();
SolutionUtil 클래스에 로그인에 성공한 user의 정보를 저장하고, 요구사항에 맞는 정보 메시지를 출력한 이후 "회원 메뉴폼"이 나타나게 했다.
이렇게 작성하고 올바르게 이벤트 코드를 각 버튼에 설정하였다면, 정상적으로 동작할 것이다. 실제 프로그램이 정상 동작하는지를 테스트해 보자.
회원 메뉴 폼 풀이
1) [그림 3-1]과 같이 좌측 상단에 로그인한 회원의 정보(이름, 연령구분)를 나타내시오.
2) ‘열차 예매’ 버튼을 클릭하면 폼을 닫고 [그림 4-1] 예매 폼으로 이동하시오.
3) ‘마이페이지’ 버튼을 클릭하면 폼을 닫고 [그림 9-1] 마이페이지 폼으로 이동하시오.
4) ‘메인’ 버튼을 클릭하면 [그림 1-1] 메인 폼으로 이동하시오.
회원메뉴 폼의 경우 디자인도 간단하고, 처리할 기능도 단순하다. 버튼 3개와 PictureBox만 배치하여 보이게 하면 끝이다. 그래서 바로 전체 풀이 코드 설명으로 넘어가자.
전체 코드
public partial class UserMenuForm : TemplateForm
{
public UserMenuForm()
{
InitializeComponent();
}
private void UserMenuForm_Load(object sender, EventArgs e)
{
this.lbName.Text = $"{SolutionUtil.loginUser.name} [{SolutionUtil.ConvertAgeToStringCategory(SolutionUtil.loginUser)}]";
}
/// <summary>
/// 열차 예매 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnReservation_Click(object sender, EventArgs e)
{
this.Hide();
new ReservationMenuForm().ShowDialog();
this.Show();
}
/// <summary>
/// 마이페이지 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMyPage_Click(object sender, EventArgs e)
{
this.Hide();
new MyPageForm().ShowDialog();
this.Show();
}
/// <summary>
/// 메인 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMain_Click(object sender, EventArgs e)
{
this.Close();
}
}
private void UserMenuForm_Load(object sender, EventArgs e)
{
this.lbName.Text = $"{SolutionUtil.loginUser.name} [{SolutionUtil.ConvertAgeToStringCategory(SolutionUtil.loginUser)}]";
}
- Form이 Load 될 때 SolutionUtil 클래스에 저장한 로그인 한 User의 이름과 연령 구분을 label에 출력하는 코드다.
/// <summary>
/// 열차 예매 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnReservation_Click(object sender, EventArgs e)
{
this.Hide();
new ReservationMenuForm().ShowDialog();
this.Show();
}
- 로그인 한 회원이 열차 예매 버튼을 클릭하면 열차 예매 폼이 나타나게 하는 코드다. 다시 이전 화면으로 돌아와야 하는 공통조건이 있기 때문에 이 또한, 현재 폼을 숨기고 새로운 폼을 Dialog로 연 후, 다시 현재 폼을 활성화시키는 로직으로 작성하였다.
/// <summary>
/// 마이페이지 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMyPage_Click(object sender, EventArgs e)
{
this.Hide();
new MyPageForm().ShowDialog();
this.Show();
}
- 마이페이지 버튼도 동일한 로직으로 작성하였다.
/// <summary>
/// 메인 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMain_Click(object sender, EventArgs e)
{
this.Close();
}
- 사용자가 메인 버튼을 클릭하면 현재 폼을 닫기만 하면 이전 메인폼이 자동으로 Show() 될 것이기 때문에 Close 메서드만 호출하면 된다.
회원 메뉴폼은 매우 간단해서 디자인과 코드도 설명할 것이 없다. 바로 다음 폼으로 넘어가자.
예매폼 풀이
예매폼이 처음에 켜질 때는 위 그림과 같이 나타나야 하고, 사용자가 각 입력란 출발지 / 도착지 / 운행 스케줄 / 좌석 배치도 정보를 선택하면 최종적으로는 다음과 같이 나타나야 한다.
이 예매 폼은 이 폼에서 모든 값을 입력하는 것이 아니라, 각각 다른 폼들과 연계해서 해당 폼에서 사용자가 입력한 값을 받아와야 하는 구조다. 요구사항을 읽으면 이해할 수 있다.
1) 입력란은 모든 항목은 직접 입력이 불가능하게 제어하시오.
2) ‘출발지/도착지 선택’ 입력란을 다음과 같이 제어하시오.
가. ‘출발지 선택>’ 입력란을 클릭하면 [그림 5-1] 출발지 선택 폼으로 이동하시오.
나. 출발지 선택을 완료하지 않고 도착지 선택을 시도할 경우 -> 경고: 먼저 출발지를 선택해 주세요.
다. 출발지 선택을 완료하고 ‘도착지 선택>’ 입력란을 클릭하면 [그림 5-2] 도착지 선택 폼으로 이동하시오.
라. 출발지나 도착지가 변경되면 출발지와 도착지를 제외한 모든 항목이 초기화되게 하시오.
3) 아이콘을 클릭하면 다음과 같이 처리하시오.
가. ‘출발지/도착지’를 선택하지 않은 경우 -> 경고: 먼저 출발지와 도착지를 모두 선택해 주세요.
나. 조건을 만족한 경우 -> [그림 6-1] 달력 폼으로 이동하시오.
다. ‘좌석배치도’ 란이 공란이 아닐 경우 해당 입력란만 [그림 4-1]과 같이 초기화되게 하시오.
4) 아이콘을 클릭하면 다음과 같이 처리하시오.
가. ‘출발지/도착지’를 선택하지 않은 경우 -> 경고: 먼저 출발지와 도착지를 모두 선택해 주세요.
나. ‘운행스케줄’이 입력되지 않은 경우 -> 경고: 먼저 운행스케줄을 조회해 주세요.
다. 모든 조건을 만족할 경우 -> [그림 8-1] 좌석배치도 폼으로 이동하시오.
5) 모든 정보가 입력 또는 변경되면 [그림 4-2]와 같이 ‘할인’,‘금액’ 란에 할인되는 금액과 할인율을 적용한 합계 결제 금액이 (-)로 입력되게 하시오.
6) ‘예매’ 버튼을 클릭하면 다음과 같이 처리하시오.
가. 빈칸이 있을 경우 -> 경고: 선택하지 않은 항목이 있습니다.
나. 조건에 만족할 경우 -> 정보: 예매가 완료되었습니다. -> DB 저장 -> 폼을 닫고 [그림 3-1] 회원 메뉴 폼으로 이동하시오.
각각, 모든 입력을 다른 폼에서 사용자와의 상호작용을 통해서 값을 받아와서 보여줘야 하는 형태다. 출발지 / 도착지 / 좌석배치도의 경우 바로 나타는 폼에서 사용자가 선택한 값을 받아오면 되는 구조지만, 운행 스케줄의 경우 뒤의 요구사항을 읽어 보면 날짜 선택 폼이 나타난 후 스케줄 선택 폼에서 선택한 값을 받아와야 하는 구조다.
바로 나타나는 폼에서 사용자가 선택한 값을 받아오는 방법은 어렵지 않다. ShowDialog()로 폼을 열었을 때 DialogResult 상태 값에 따라서 해당 폼에 저장되어 있는 값을 가져오면 되기 때문이다. (아래 코드를 보면 이게 무슨 말인지 이해할 수 있다.)
그러나 날짜 선택 폼 > 운행 스케줄 선택 폼으로 진행되는 구조에서는 이 방법은 쉽지 않다. 그래서 나는 SolutionUtil 클래스를 적극 활용하여 사용자가 예매 프로세스에서 선택한 값을 저장해서 사용할 수 있게 하였다. 그래서 최종적으로 SolutionUtil 클래스에는 다음과 같은 변수와 메서드가 추가됐다.
internal class SolutionUtil
{
...
public static location departure { get; set; }
public static location arrival { get; set; }
public static DateTime? selectedDate { get; set; }
public static schedule selectedSchedule { get; set; }
public static void ClearReservationDatas()
{
SolutionUtil.departure = null;
SolutionUtil.arrival = null;
SolutionUtil.selectedSchedule = null;
SolutionUtil.selectedDate = null;
}
...
}
각각 출발지, 도착지를 저장하는 변수와 운행 스케줄을 찾기 위해 사용자가 선택한 날짜, 사용자가 최종적으로 선택한 스케줄 정보를 SolutionUtil에서 저장할 수 있게 하였다. 과제 요구사항의 ERD를 참고해서 살펴보게 되면 Schedule 정보를 찾기 위해선 출발지 / 도착지 / 날짜 정보 이 세 가지가 모두 필요하다. 각기 다른 폼에서 선택한 정보들을 운행 스케줄 폼으로 끌어와서 사용해야 하기 때문에 정적 변수를 사용해서 이 문제를 해결하는 것이다.
Winform 예매 폼은 위 그림처럼 디자인하였다. 각 입력란은 TextBox가 아닌 Label로 과제에 있는 그림과 최대한 비슷하게 만들었다.
위 폼 레이아웃을 참고하면서 이제 코드 설명을 살펴보자.
전체 코드
public partial class ReservationMenuForm : TemplateForm
{
private int? carNo = null;
private string seat = null;
public ReservationMenuForm()
{
InitializeComponent();
SolutionUtil.ClearReservationDatas();
}
/// <summary>
/// 출발지 선택 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lbDeparture_Click(object sender, EventArgs e)
{
LocationSelectForm form = new LocationSelectForm(true);
if (form.ShowDialog() == DialogResult.OK)
{
SolutionUtil.departure = form.selectedLocation;
this.lbDeparture.Text = SolutionUtil.departure.lname; ;
ClearScheduleAndSeat();
}
}
/// <summary>
/// 도착지 선택 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lbArrival_Click(object sender, EventArgs e)
{
if(SolutionUtil.departure == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지를 선택해주세요.");
return;
}
LocationSelectForm form = new LocationSelectForm(false);
if (form.ShowDialog() == DialogResult.OK)
{
SolutionUtil.arrival = form.selectedLocation;
this.lbArrival.Text = SolutionUtil.arrival.lname;
ClearScheduleAndSeat();
}
}
private void ClearScheduleAndSeat()
{
SolutionUtil.selectedDate = null;
SolutionUtil.selectedSchedule = null;
this.carNo = null;
this.seat = null;
this.lbDate.Text = "";
this.lbDepartureTime.Text = "";
this.lbArrivalTime.Text = "";
this.lbCar.Text = "";
this.lbSeat.Text = "";
this.lbDiscount.Text = "";
this.lbPrice.Text = "";
}
/// <summary>
/// 운행 스케줄 아이콘 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void picSchedule_Click(object sender, EventArgs e)
{
if (SolutionUtil.departure == null || SolutionUtil.arrival == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지와 도착지를 모두 선택해주세요.");
return;
}
if(new CalendarForm().ShowDialog() == DialogResult.OK)
{
this.lbDate.Text = SolutionUtil.selectedSchedule.date.Value.ToString("yyyy-MM-dd");
this.lbDepartureTime.Text = SolutionUtil.selectedSchedule.time.Value.ToString();
int minutes = SolutionUtil.CalcMinutesBetweenLocations(SolutionUtil.selectedSchedule.location, SolutionUtil.selectedSchedule.location1);
TimeSpan timeVal = new TimeSpan(0, minutes, 0);
this.lbArrivalTime.Text = SolutionUtil.selectedSchedule.time.Value.Add(timeVal).ToString();
}
}
/// <summary>
/// 좌석 배치도 아이콘 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void picSeat_Click(object sender, EventArgs e)
{
if (SolutionUtil.departure == null || SolutionUtil.arrival == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지와 도착지를 모두 선택해주세요.");
return;
}
if(SolutionUtil.selectedSchedule == null)
{
SolutionUtil.ShowErrMessage("먼저 운행스케줄을 조회해주세요.");
return;
}
CarAndSeatSelectForm seatForm = new CarAndSeatSelectForm();
if (seatForm.ShowDialog() == DialogResult.OK)
{
this.carNo = seatForm.CarNo;
this.seat = seatForm.Seat;
this.lbCar.Text = this.carNo.ToString();
this.lbSeat.Text = this.seat;
int price = SolutionUtil.CalcPriceBetweenLocations(SolutionUtil.selectedSchedule.location,
SolutionUtil.selectedSchedule.location1);
int discountPrice = (int)(price * SolutionUtil.CalcDiscount(SolutionUtil.loginUser));
this.lbDiscount.Text = "-" + discountPrice.ToString("#,##0");
this.lbPrice.Text = price.ToString("#,##0");
}
}
/// <summary>
/// 예매 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnReservation_Click(object sender, EventArgs e)
{
if (SolutionUtil.departure == null || SolutionUtil.arrival == null ||
SolutionUtil.selectedSchedule == null || this.seat == null)
{
SolutionUtil.ShowErrMessage("선택하지 않은 항목이 있습니다.");
return;
}
reservation res = new reservation();
res.uno = SolutionUtil.loginUser.uno;
res.sno = SolutionUtil.selectedSchedule.sno;
res.carno = this.carNo;
res.seat = this.seat;
this.db.reservation.Add(res);
this.db.SaveChanges();
SolutionUtil.ShowInfoMessage("예매가 완료되었습니다.");
this.Close();
}
}
변수 설명
private int? carNo = null;
private string seat = null;
- 좌석배치도 폼에서 선택한 정보의 경우 별도로 SolutionUtil 클래스에 저장할 필요가 굳이 없기 때문에, 현재 이 폼에서 갖고 있을 수 있게끔 변수로 선언하였다.
출발지 선택 Label Click 이벤트 설명
/// <summary>
/// 출발지 선택 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lbDeparture_Click(object sender, EventArgs e)
{
LocationSelectForm form = new LocationSelectForm(true);
if (form.ShowDialog() == DialogResult.OK)
{
SolutionUtil.departure = form.selectedLocation;
this.lbDeparture.Text = SolutionUtil.departure.lname; ;
ClearScheduleAndSeat();
}
}
- 출발지 선택 폼을 실행하여 사용자가 원하는 지역을 선택할 수 있게 하였다. 생성자 매개변수로 true 값을 보내고 있는데, 이는 나중에 설명하겠지만 true이면 출발지 선택 false이면 도착지 선택을 의미하여 해당 폼에서 동작이 다르게 되게 하고 있다. 출발지의 경우 지역이 파란색으로 보여야 하고 도착지의 경우 빨간색으로 보여야 하기 때문이다. (폼 title도 변경이 필요하다.)
- 코드를 보게 되면 ShowDialog() 메서드를 사용해서 해당 폼을 Dialog 상태로 실행할 뿐 아니라 실행 후의 DialogResult 값을 받아와서 비교하여 처리할 수 있게끔 짤 수 있다는 것을 볼 수 있다. 이렇게 해서 사용자가 해당 Dialog 화면에서 처리한 결과에 따라 그다음 절차를 수행하는 코드를 짤 수 있는 것이다.
- 출발지를 선택했다는 DialogResult 값인 DialogResult.OK 값을 받게 되면 출발지 정보를 SolutionUtil 클래스에 선언되어 있는 변수에 저장하고, "출발지 선택>" Label의 Text를 사용자가 선택한 출발지 이름으로 변경함과 동시에 사용자가 선택한 다른 정보들 도착지, 운행 스케줄, 좌석 등의 모든 정보를 Clear 하는 메서드를 호출한다.
도착지 선택 Label Click 이벤트 설명
/// <summary>
/// 도착지 선택 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lbArrival_Click(object sender, EventArgs e)
{
if(SolutionUtil.departure == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지를 선택해주세요.");
return;
}
LocationSelectForm form = new LocationSelectForm(false);
if (form.ShowDialog() == DialogResult.OK)
{
SolutionUtil.arrival = form.selectedLocation;
this.lbArrival.Text = SolutionUtil.arrival.lname;
ClearScheduleAndSeat();
}
}
- 도착지 선택의 경우 먼저 출발지가 선택이 돼야만 하기 때문에, 선택하지 않은 경우 경고 메시지가 나타나게 하였다. 그리고 출발지 선택과 같은 폼을 매개변수만 달리 하여 실행하고 값을 받아와서 SolutionUtil 클래스에 선언해 둔 변수에 저장하였다.
ClearScheduleAndSeat() 메서드 설명
private void ClearScheduleAndSeat()
{
SolutionUtil.selectedDate = null;
SolutionUtil.selectedSchedule = null;
this.carNo = null;
this.seat = null;
this.lbDate.Text = "";
this.lbDepartureTime.Text = "";
this.lbArrivalTime.Text = "";
this.lbCar.Text = "";
this.lbSeat.Text = "";
this.lbDiscount.Text = "";
this.lbPrice.Text = "";
}
- 사용자가 출발지 또는 도착지를 선택했을 경우 하단에 이전에 선택했던 운행스케줄, 좌석 정보들이 모두 초기화될 수 있게 하는 메서드다.
운행 스케줄 아이콘 (PictureBox) Click 이벤트 설명
/// <summary>
/// 운행 스케줄 아이콘 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void picSchedule_Click(object sender, EventArgs e)
{
if (SolutionUtil.departure == null || SolutionUtil.arrival == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지와 도착지를 모두 선택해주세요.");
return;
}
if(new CalendarForm().ShowDialog() == DialogResult.OK)
{
this.lbDate.Text = SolutionUtil.selectedSchedule.date.Value.ToString("yyyy-MM-dd");
this.lbDepartureTime.Text = SolutionUtil.selectedSchedule.time.Value.ToString();
int minutes = SolutionUtil.CalcMinutesBetweenLocations(SolutionUtil.selectedSchedule.location, SolutionUtil.selectedSchedule.location1);
TimeSpan timeVal = new TimeSpan(0, minutes, 0);
this.lbArrivalTime.Text = SolutionUtil.selectedSchedule.time.Value.Add(timeVal).ToString();
}
}
- if (SolutionUtil.departure == null || SolutionUtil.arrival == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지와 도착지를 모두 선택해 주세요.");
return;
}
출발지와 도착지가 모두 선택되어 있는지를 먼저 체크한다. - if(new CalendarForm().ShowDialog() == DialogResult.OK)
{
this.lbDate.Text = SolutionUtil.selectedSchedule.date.Value.ToString("yyyy-MM-dd");
this.lbDepartureTime.Text = SolutionUtil.selectedSchedule.time.Value.ToString();
int minutes = SolutionUtil.CalcMinutesBetweenLocations(SolutionUtil.selectedSchedule.location, SolutionUtil.selectedSchedule.location1);
TimeSpan timeVal = new TimeSpan(0, minutes, 0);
this.lbArrivalTime.Text = SolutionUtil.selectedSchedule.time.Value.Add(timeVal).ToString();
}
폼을 ShowDialog()로 실행하는 경우, 날짜 선택 > 운행 스케줄 선택 폼은 모두 실행 순서가 Stack 구조로 쌓여서 실행이 되기에 최종적으로 ClendarForm()의 ShowDialog() 결과가 OK인 경우에는 사용자가 정상적으로 날짜도 선택하고 스케줄도 선택했다는 결과가 된다. 따라서 SolutionUtil 클래스에 저장해 둔 변수의 값을 사용해서 화면에 정보를 표시하는 코드다.
좌석 배치도 아이콘(PictureBox) Click 이벤트 설명
/// <summary>
/// 좌석 배치도 아이콘 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void picSeat_Click(object sender, EventArgs e)
{
if (SolutionUtil.departure == null || SolutionUtil.arrival == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지와 도착지를 모두 선택해주세요.");
return;
}
if(SolutionUtil.selectedSchedule == null)
{
SolutionUtil.ShowErrMessage("먼저 운행스케줄을 조회해주세요.");
return;
}
CarAndSeatSelectForm seatForm = new CarAndSeatSelectForm();
if (seatForm.ShowDialog() == DialogResult.OK)
{
this.carNo = seatForm.CarNo;
this.seat = seatForm.Seat;
this.lbCar.Text = this.carNo.ToString();
this.lbSeat.Text = this.seat;
int price = SolutionUtil.CalcPriceBetweenLocations(SolutionUtil.selectedSchedule.location,
SolutionUtil.selectedSchedule.location1);
int discountPrice = (int)(price * SolutionUtil.CalcDiscount(SolutionUtil.loginUser));
this.lbDiscount.Text = "-" + discountPrice.ToString("#,##0");
this.lbPrice.Text = price.ToString("#,##0");
}
}
- if (SolutionUtil.departure == null || SolutionUtil.arrival == null)
{
SolutionUtil.ShowErrMessage("먼저 출발지와 도착지를 모두 선택해 주세요.");
return;
}
if(SolutionUtil.selectedSchedule == null)
{
SolutionUtil.ShowErrMessage("먼저 운행스케줄을 조회해 주세요.");
return;
}
좌석배치도 선택은 앞선 데이터 선택 작업이 선행이 되어야 하기 때문에 먼저 정상적으로 값이 선택이 되었는지를 체크한다. - CarAndSeatSelectForm seatForm = new CarAndSeatSelectForm();
if (seatForm.ShowDialog() == DialogResult.OK)
{
this.carNo = seatForm.CarNo;
this.seat = seatForm.Seat;
...
}
좌석배치도 폼을 ShowDialog()로 실행하고 그 결과가 OK 일 때, 좌석배치도 폼에 사용자가 선택한 호차와 좌석 정보를 갖고 있는 변수를 받아와서 저장하고, 그에 따른 정보를 화면에 표시하는 코드다.
이렇게 하기 위해서는 좌석배치도 폼의 CarNo와 Seat 변수는 각각 Public으로 선언이 되어 있어야만 접근이 가능하다.
예매 버튼 Click 이벤트 설명
/// <summary>
/// 예매 버튼 클릭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnReservation_Click(object sender, EventArgs e)
{
if (SolutionUtil.departure == null || SolutionUtil.arrival == null ||
SolutionUtil.selectedSchedule == null || this.seat == null)
{
SolutionUtil.ShowErrMessage("선택하지 않은 항목이 있습니다.");
return;
}
reservation res = new reservation();
res.uno = SolutionUtil.loginUser.uno;
res.sno = SolutionUtil.selectedSchedule.sno;
res.carno = this.carNo;
res.seat = this.seat;
this.db.reservation.Add(res);
this.db.SaveChanges();
SolutionUtil.ShowInfoMessage("예매가 완료되었습니다.");
this.Close();
}
- if (SolutionUtil.departure == null || SolutionUtil.arrival == null || SolutionUtil.selectedSchedule == null || this.seat == null)
{
SolutionUtil.ShowErrMessage("선택하지 않은 항목이 있습니다.");
return;
}
사용자가 예매에 필요한 모든 정보(출발지 / 도착지 / 운행 스케줄 / 좌석)를 선택했는지 체크하는 코드다. - reservation res = new reservation();
res.uno = SolutionUtil.loginUser.uno;
res.sno = SolutionUtil.selectedSchedule.sno;
res.carno = this.carNo;
res.seat = this.seat;
this.db.reservation.Add(res);
this.db.SaveChanges();
SolutionUtil.ShowInfoMessage("예매가 완료되었습니다.");
this.Close();
신규 예매 정보를 DB에 저장하기 위해 새 reservation 객체를 생성하고, 해당 객체에 사용자가 선택한 값들을 할당하고 이를 DbContext 객체인 db 변수안에 있는 reservation 콜렉션에 추가하고 저장하여 실질적으로 DB에 신규 reseravtion 데이터가 생성될 수 있게 하는 코드다.
다른 폼과의 소통을 ShowDialog() 메서드와 그 Return 값인 DialogResult로 할 수 있다는 것이 이번 포스팅에서의 핵심일 것 같다. 예매 폼 풀이는 여기까지 마무리하고 다음 포스팅에서 이어서 진행하겠다.
'C# > 기능경기대회 정보기술 과제 풀이' 카테고리의 다른 글
2025년 지방기능경기대회 정보기술 1과제 C# 과제 풀이 (6) (0) | 2025.05.30 |
---|---|
2025년 지방기능경기대회 정보기술 1과제 C# 과제 풀이 (5) (1) | 2025.05.30 |
2025년 지방기능경기대회 정보기술 1과제 C# 과제 풀이 (4) (0) | 2025.05.30 |
2025년 지방기능경기대회 정보기술 1과제 C# 과제 풀이 (3) (0) | 2025.05.30 |
2025년 지방기능경기대회 정보기술 1과제 C# 과제 풀이 (1) (2) | 2025.05.30 |