Winform Chart Control Example (4)
이번에는 기본 가로 막대형 차트에서 조금 변형을 준 차트를 해보려고 한다.
기본적인 가로 막대 차트에서 최댓값에 해당하는 막대만 다른 색상으로 표현하는 예제를 정리해보고자 한다.
뿐만 아니라, 차트 막대를 클릭했을 때 클릭한 막대를 다른 색으로 표현하는 방법도 한 번 이번 포스팅에서 정리를 해보려고 한다. 정리는 아래의 순서에 맞게 진행하고자 한다.
- 기본 데이터 세팅하기
- 차트 기본 세팅하기
- 차트 데이터 설정 및 최댓값 막대 컬러 변경하기 (FindMaxByValue)
- 사용자가 클릭한 막대 컬러 변경하기 (Chart MouseClickEevnt and HitTest)
기본 데이터 세팅하기
public partial class ChartExample_4_Form : TemplateForm
{
private DataTable dataSource;
public ChartExample_4_Form()
{
InitializeComponent();
this.SetData();
}
private void SetData()
{
this.dataSource = new DataTable("ChartData");
string[] columns = new string[] { "한국", "싱가포르", "유럽", "일본", "중국ㆍ홍콩", "미국"};
int[] values = new int[] { 3800, 3290, 3170, 2780, 2600, 2300 };
this.dataSource.Columns.Add("Country");
this.dataSource.Columns.Add("Price");
for (int i = 0; i < columns.Length; i++)
{
DataRow row = this.dataSource.NewRow();
row[0] = columns[i];
row[1] = values[i];
this.dataSource.Rows.Add(row);
}
}
}
차트를 표현하기 위한 데이터는 하나씩 직접 작업해 줬다. 지난번 포스팅에서 다뤘던 데이터 내용과는 조금 차이가 있으나 구조는 변한 게 없으므로 넘어가겠다.
차트 기본 세팅하기
private void SetChart()
{
this.chartControl.Titles.Add(
new Title("□□벅스 카페라떼 가격 국가별 비교", Docking.Top,
new Font("Malgun Gothic", 20, FontStyle.Bold), Color.Black)
);
this.chartControl.Legends.RemoveAt(0);
ChartArea chArea = this.chartControl.ChartAreas[0];
chArea.BackColor = Color.LightGray;
chArea.AxisX.MajorGrid.Enabled = false;
chArea.AxisX.MajorTickMark.TickMarkStyle = TickMarkStyle.InsideArea;
chArea.AxisX.Minimum = 0.5;
chArea.AxisX.Maximum = this.dataSource.Rows.Count + 0.5;
chArea.AxisX.LabelStyle.IntervalOffset = 0.5;
chArea.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dot;
chArea.AxisY.MajorTickMark.TickMarkStyle = TickMarkStyle.InsideArea;
chArea.AxisY.Interval = 500;
chArea.AxisY.LabelStyle.Format = "#,##0;#,##0;-";
Series chartSeries = this.chartControl.Series[0];
chartSeries.Color = Color.LightBlue;
chartSeries.BorderColor = Color.Black;
chartSeries.IsValueShownAsLabel = true;
chartSeries.SetCustomProperty("PointWidth", "0.5");
chartSeries.LabelFormat = "#,##0;#,##0;-";
}
차트의 Title을 먼저 설정해 줬다. 그 이후에 Chart Control을 Form에 추가하고 기본적으로 생성되어 있는 Legend를 제거하여 줬다. 그 외에는 일반적인 설정들만 해줬다.
이전 포스팅과 다른 점은 단 한 가지. 아직 차트에 데이터를 세팅하지 않았다는 점이다. 여기까지 작성하고 프로그램을 실행해 보면 다음과 같은 차트를 확인할 수 있다.
당연하게도 데이터를 세팅하지 않았기 때문에 차트로 표현할 수가 없는 상태다. 근데 왜 아직 차트 데이터를 설정하지 않았을까? 그 이유는 다음 챕터에 나온다.
차트 데이터 설정 및 최댓값 막대 컬러 변경하기 (FindMaxByValue)
데이터를 아직 설정하지 않은 상태인데, 이번에는 지난번 포스팅과는 다르게 다른 방법으로 데이터를 세팅하려고 남겨뒀다. 이전에는 Chart Control에 DataSource 속성값을 우리가 미리 만들어 놓은 DataTable 객체로 연결하여 사용했는데, 이번에는 Chart Series 객체에 Points 컬렉션 객체를 사용하여 하나씩 값을 추가하는 방법으로 데이터를 세팅했다. (차트의 데이터를 설정하는 방법이 여러 개가 있음을 알려주고 싶었다..?)
차트 데이터를 설정하는 코드는 간단하다.
private void SetChart()
{
...
for(int i = 0; i < this.dataSource.Rows.Count; i++)
{
string country = this.dataSource.Rows[i][0].ToString();
int price = int.Parse(this.dataSource.Rows[i][1].ToString());
chartSeries.Points.AddXY(country, price);
}
}
Chart Series 객체에 있는 Points 컬렉션을 사용해서 해당 컬렉션에서 제공하고 있는 AddXY 메서드를 사용해서 차트 데이터를 추가해 주면 되는 간단한 구조다.
이렇게 하면 어느 정도 우리가 구현해야 하는 차트의 모습은 거의 완성이 되었다.
이제 남은 것은 차트 막대 중에서 최댓값에 해당하는 막대의 컬러를 변경하는 일이다. Series 객체의 Points 컬렉션에 AddXY 메서드를 사용해서 Points 컬렉션 데이터를 등록한 상태이기 때문에, 우리는 Points 컬렉션에서 제공하고 있는 메서드를 사용해서 아주 쉽고 간단하게 이 문제를 해결할 수 있다. 다음의 솔루션을 보자.
private void SetChart()
{
...
chartSeries.Points.FindMaxByValue().Color = Color.Orange;
}
// 어떤 메서드인지 확인하기 위한 용도로 넣은 것 입니다. //
public DataPoint FindMaxByValue()
{
return FindMaxByValue("Y");
}
Points 컬렉션에서는 FindMaxByValue()라고 하는 메서드를 제공하고 있다. 메서드 명을 보면 바로 알 수 있듯이 최댓값을 찾아 반환해 주는 메서드이다. FindMaxByValue 메서드가 어떤 구조로 이루어져 있는지 함수 원형을 찾아서 같이 첨부했는데, Y축 값을 대상으로 최댓값 DataPoint 객체를 찾아 반환하는 구조다.
Points 컬렉션은 DataPoint 객체들을 저장하고 있는 자료구조로서 그중의 최댓값 갖고 있는 DataPoint를 반환해 주는 아주 유용한 메서드인 것이다. 그래서 우리는 이 메서드를 사용해서 DataPoint 객체를 찾고 그 객체의 컬러만 변경해 주면 쉽게 최댓값에 해당하는 막대의 컬러를 변경할 수 있다.
아쉽게도 FindMaxByValue() 메서드는 DataPoint 객체 하나만을 return 하기 때문에 최댓값이 여러 개가 존재하는 차트의 경우 별도로 우리가 코딩을 해서 해줘야만 한다.
사용자가 클릭한 막대 컬러 변경하기 (Chart MouseClickEevnt and HitTest)
마지막 챕터의 내용은 사실상 부가 기능인데, 한 번 심심풀이로 해보고 블로그 포스팅으로 정리하려고 한다.
해보고자 하는 기능은 간단하다. 현재 보이고 있는 차트에서 특정 막대를 사용자가 클릭했을 때 해당 막대의 컬러를 변경하여 사용자가 클릭한 막대가 무엇인지 확인할 수 있게 하고자 하는 기능이다.
어쩌다 보니 위 예시 샘플에 Orange 컬러 막대는 클릭을 안 했는데, 해당 막대도 클릭하면 Blue 컬러로 변경되게 된다.
이걸 과연 어떻게 구현할 수 있을까? 먼저, Chart 내에서 사용자가 Mouse로 클릭했을 때 발생하는 동작이기 때문에 Mouse Click 이벤트를 사용하면 된다. 추가한 Chart Control에 Mouse Click 이벤트를 생성하여 준다.
Mouse Click 이벤트에는 사용자가 클릭한 마우스 좌표를 구할 수 있는 Parameter가 존재한다.
private void chartControl_MouseClick(object sender, MouseEventArgs e)
{
}
바로 MouseEventArgs라고 하는 이 Parameter를 사용하면 되는데, MouseEvent가 발생하여 생긴 Arguments들을 사용할 수 있는 변수라고 생각하면 된다. 그래서 사용자가 클릭한 마우스의 좌표를 사용해서 차트 내에서 어떤 개체를 선택한 것인지 알아내야 하는데, 아주 다행스럽게도 Winform Chart에서는 그 메서드를 제공해주고 있다. 바로 HitTest 메서드이다.
Chart.HitTest 메서드 (System.Windows.Forms.DataVisualization.Charting)
지정된 X 및 Y 좌표로 정의된 지점에 있는 차트 요소(있는 경우)를 결정합니다.
learn.microsoft.com
이 메서드의 정의로 보면 "지정된 X 및 Y 좌표로 정의된 지점에 있는 차트 요소(있는 경우)를 결정합니다."이라고 되어 있다. 우리가 딱 현재 원하는 상황에 알맞은 메서드임을 알 수 있다. 거두절미하고 이 HitTest 메서드를 사용해서 우리가 원하는 기능을 구현하기 위해서는 아래와 같이 코드를 작성하면 된다.
private void chartControl_MouseClick(object sender, MouseEventArgs e)
{
var target = this.chartControl.HitTest(e.X, e.Y);
if(target.ChartElementType == ChartElementType.DataPoint)
{
int pointIdx = target.PointIndex;
Series chartSr = this.chartControl.Series[0];
foreach(var point in chartSr.Points)
point.Color = Color.LightBlue;
chartSr.Points.FindMaxByValue().Color = Color.Orange;
chartSr.Points[pointIdx].Color = Color.Blue;
}
}
HitTest 메서드를 사용해서 사용자가 클릭한 곳이 DataPoint 객체인지를 먼저 판별하고, 클릭한 DataPoint 객체의 Index를 갖고 작업을 수행해 주면 된다.
Chart Series 객체 안에 있는 Points 컬렉션을 활용해서 똑같이 작업을 해주면 되는데, 선택한 DataPoint(차트 막대) 컬러를 변경하기 이전에 선택하지 않은 다른 막대들의 컬러를 원상 복구 시켜줘야 하기 때문에 초기화하는 코드가 조금 들어가 있다. 이점을 참고해 주고 봐주시면 될 것 같다. (만약, 초기화하지 않고 실행하게 되면 클릭한 곳마다 Blue로 변경되고 되돌아오지 않아 결국에는 모든 막대가 Blue 컬러가 될 것이다...)
최종 완성 코드
public partial class ChartExample_4_Form : TemplateForm
{
private DataTable dataSource;
public ChartExample_4_Form()
{
InitializeComponent();
this.SetData();
this.SetChart();
}
private void SetData()
{
this.dataSource = new DataTable("ChartData");
string[] columns = new string[] { "한국", "싱가포르", "유럽", "일본", "중국ㆍ홍콩", "미국"};
int[] values = new int[] { 3800, 3290, 3170, 2780, 2600, 2300 };
this.dataSource.Columns.Add("Country");
this.dataSource.Columns.Add("Price");
for (int i = 0; i < columns.Length; i++)
{
DataRow row = this.dataSource.NewRow();
row[0] = columns[i];
row[1] = values[i];
this.dataSource.Rows.Add(row);
}
}
private void SetChart()
{
this.chartControl.Titles.Add(
new Title("□□벅스 카페라떼 가격 국가별 비교", Docking.Top,
new Font("Malgun Gothic", 20, FontStyle.Bold), Color.Black)
);
this.chartControl.Legends.RemoveAt(0);
ChartArea chArea = this.chartControl.ChartAreas[0];
chArea.BackColor = Color.LightGray;
chArea.AxisX.MajorGrid.Enabled = false;
chArea.AxisX.MajorTickMark.TickMarkStyle = TickMarkStyle.InsideArea;
chArea.AxisX.Minimum = 0.5;
chArea.AxisX.Maximum = this.dataSource.Rows.Count + 0.5;
chArea.AxisX.LabelStyle.IntervalOffset = 0.5;
chArea.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dot;
chArea.AxisY.MajorTickMark.TickMarkStyle = TickMarkStyle.InsideArea;
chArea.AxisY.Interval = 500;
chArea.AxisY.LabelStyle.Format = "#,##0;#,##0;-";
Series chartSeries = this.chartControl.Series[0];
chartSeries.Color = Color.LightBlue;
chartSeries.BorderColor = Color.Black;
chartSeries.IsValueShownAsLabel = true;
chartSeries.SetCustomProperty("PointWidth", "0.5");
chartSeries.LabelFormat = "#,##0;#,##0;-";
for (int i = 0; i < this.dataSource.Rows.Count; i++)
{
string country = this.dataSource.Rows[i][0].ToString();
int price = int.Parse(this.dataSource.Rows[i][1].ToString());
chartSeries.Points.AddXY(country, price);
}
chartSeries.Points.FindMaxByValue().Color = Color.Orange;
}
private void chartControl_MouseClick(object sender, MouseEventArgs e)
{
var target = this.chartControl.HitTest(e.X, e.Y);
if(target.ChartElementType == ChartElementType.DataPoint)
{
int pointIdx = target.PointIndex;
Series chartSr = this.chartControl.Series[0];
foreach(var point in chartSr.Points)
point.Color = Color.LightBlue;
chartSr.Points.FindMaxByValue().Color = Color.Orange;
chartSr.Points[pointIdx].Color = Color.Blue;
}
}
}
'C# > Winform (.Net Framework)' 카테고리의 다른 글
C# - Winform Chart Control Example (6) (0) | 2025.01.23 |
---|---|
C# - Winform Chart Control Example (5) (0) | 2025.01.22 |
C# - Winform Chart Control Example (3) (0) | 2025.01.20 |
C# - Winform Chart Control Example (2) (1) | 2025.01.20 |
C# - Winform Chart Control Example (1) (1) | 2025.01.08 |