드디어 범례를 살펴볼 시간이 왔습니다. 사실 지금까지 글을 살펴보시면서 뭔가 빠졌는데? 뭔가 허전 한거 아냐? 하고 의구심을 가지신 분이 계시다면 당연한 궁금증이라고 생각됩니다. 지금까지 다루어본 차트의 코드에서는 차트의 핵심적인 기능이라고 할 수 있는 범례(Legend)가 빠져 있었기 때문이죠. 사실 범례는 여러 가지 이유로 차트에서 매우 필수적인 요소입니다. 차트의 특성상 값을 정확히 이해하기 힘드므로 이를 알려주는 역할을 하기도 하고 차트에 바로 띄워주는 Label이 없을 때에는 차트의 어느 영역이 어느 부분이다라는 걸 알려주는 역할도 하지요.
대부분의 경우에 차트에 범례가 없다면 그야말로 앙꼬 없는 찐빵이나 진배 없어지기도 합니다. 그러한 중요성이 있는 부분이기는 하지만 범례를 매 차트마다 넣어주게 되면 코드의 길이가 너무 길어지기도 하고 예제가 복잡해질 우려가 있었기 때문에 별도의 글로 옮겨 왔습니다. 사실 범례는 ChartArea와 Series처럼 다른 요소에 대해서 어느 정도 독립적인 구성을 가지고 있기 때문에 차트의 형태에 관계없는 코딩이 가능하다는 부분도 독립된 글로 범례를 다루게 된 한 이유입니다. 그럼 범례를 다루는 법을 살펴보면서 차트의 마지막 조각을 맞춰 봅시다.
첫 번째 예제의 HTML 코드는 다음과 같습니다.
<asp:chart id="Chart1" runat="server" BackColor="White" Height="400px"
Width="500px" AntiAliasing="All" ImageType="Png"
PaletteCustomColors="200, 222, 52, 8; 200, 8, 89, 140; 200, 189, 186, 189;
200, 49, 130, 239; 200, 255, 174, 57; 200, 0, 150, 0;" Palette="None">
<titles>
<asp:Title Font="맑은 고딕, 18pt, style=Bold" Text="음료 판매량"
ForeColor="0, 0, 0"></asp:Title>
</titles>
<legends>
<asp:Legend BackColor="White" BorderColor="Gray" Alignment="Near"
Docking="Right" Font="맑은 고딕, 9pt" IsTextAutoFit="False"
Name="Default" LegendStyle="Column">
</asp:Legend>
</legends>
<borderskin SkinStyle="None"></borderskin>
<series>
<asp:Series Name="Default" ChartType="Pie"></asp:Series>
</series>
<chartareas>
<asp:ChartArea Name="ChartArea1" BorderColor="64, 64, 64, 64"
BackSecondaryColor="Transparent" BackColor="Transparent"
ShadowColor="Transparent" BorderWidth="0"></asp:ChartArea>
</chartareas>
</asp:chart>
그리고 .cs 코드는 다음과 같습니다.
protected void Page_Load(object sender, System.EventArgs e)
{
double[] yValues = { 3000, 2200, 1800, 600, 300, 100 };
string[] xValues = { "콜라", "사이다", "오렌지주스", "홍차", "커피", "보리차" };
Chart1.Series["Default"].Points.DataBindXY(xValues, yValues);
Chart1.Series["Default"]["PieLabelStyle"] = "Inside";
Chart1.Series["Default"].Points[0]["Exploded"] = "true";
Chart1.ChartAreas["ChartArea1"].Area3DStyle.Enable3D = true;
Chart1.ChartAreas["ChartArea1"].Area3DStyle.Inclination = 55;
Chart1.ChartAreas["ChartArea1"].Area3DStyle.Rotation = 0;
}
이에 의한 결과는 다음과 같습니다.
사실 저 많은 코드 중에 이번 예제에서 살펴 볼 내용은
<asp:Legend BackColor="White" BorderColor="Gray" Alignment="Near" Docking="Right" Font="맑은 고딕, 9pt" IsTextAutoFit="False" Name="Default" LegendStyle="Column">
이 태그 하나로 다 설명이 됩니다. 애트리뷰트 중 Alignment 특성을 통해서 상하 어느 쪽에 위치할지를 결정합니다.(Near(위쪽에 붙음), Center(중간), Far(아래에 붙음)) 사실 Alignment라는 용어는 HTML태그의 align 특성을 연상시키기 때문에 이와 같은 위아래 방향이 아닌 좌우 방향으로 오해하기 쉽습니다.(글쓴이도 처음에는 그렇게 이해했으니까요.) 그리고 Docking 특성을 이용해 어떤 방향에 범례가 위치할지(Top, Bottom, Left, Right) 결정합니다. 사실 Alignment이 VerticalAlign이나 HorizontalAlign처럼 좌우상하 방향이 없이 단순히 Alignment인 이유는 여기에 있습니다. 앞에서 Alignment가 Near일 때 위로 붙고 Far일 때 아래로 붙는다는 설명을 했는데 Docking값이 Top이나 Bottom이라면 당연히 Alignment값에 따라서 범례가 움직이는 위치는 좌우로 변하게 됩니다. Alignment의 값에 의한 범례의 정렬은 Docking에 연관되어 있으니 Alignment와 Docking의 값을 여러가지로 조합해 결과를 확인해 보시기 바랍니다.
그리고 범례에 쓰일 문자의 형태를 결정하고 LegendStyle(Column, Row, Table)의 값을 Column으로 주어 아래 방향으로 반복되게 정의했습니다. LegendStyle을 Row로 정의하면 내용의 반복을 횡으로 반복합니다. 그리고 범례의 문자 크기를 전체 차트의 크기에 따라서 자동 조절되지 않도록 설정하였습니다.
이러한 식으로 범례에 대한 꾸미기를 복잡하게 하지 않고
<asp:Legend></asp:Legend>
만 적어주는 것 만으로도 다음과 같이 기본적인 범례가 꾸며집니다.
때문에 필요한 만큼 꾸며주면 되고 어떤 경우에는 단지 태그만 정의해주는 것 만으로도 작업이 끝나기도 합니다. 하지만 범례에서는 때때로 복잡한 내용을 표현해주기 바라는 경우가 많습니다. 때로는 표시된 값의 합이나 평균 등을 표시 해야 하고 표시된 값의 정확한 값을 표시해 주어야 하기도 합니다. 우선 앞에서 살펴본 차트에서 값을 어떻게 표시하는지 살펴보기로 하겠습니다.
다음처럼 HTML 코드를 변경합니다.
<asp:chart id="Chart1" runat="server" BackColor="White"
Height="400px" Width="500px" AntiAliasing="All" ImageType="Png"
PaletteCustomColors="200, 222, 52, 8; 200, 8, 89, 140; 200, 189, 186, 189;
200, 49, 130, 239; 200, 255, 174, 57; 200, 0, 150, 0;"
Palette="None" oncustomizelegend="Chart1_CustomizeLegend">
<titles>
<asp:Title Font="맑은 고딕, 18pt, style=Bold" Text="음료 판매량"
ForeColor="0, 0, 0"></asp:Title>
</titles>
<legends>
<asp:Legend BackColor="White" BorderColor="Black" Alignment="Near"
Docking="Right" Font="맑은 고딕, 9pt" IsTextAutoFit="False"
Name="Default" LegendStyle="Column">
</asp:Legend>
</legends>
<borderskin SkinStyle="None"></borderskin>
<series>
<asp:Series Name="Default" ChartType="Pie"></asp:Series>
</series>
<chartareas>
<asp:ChartArea Name="ChartArea1" BorderColor="64, 64, 64, 64"
BackSecondaryColor="Transparent" BackColor="Transparent"
ShadowColor="Transparent" BorderWidth="0">
</asp:ChartArea>
</chartareas>
</asp:chart>
거의 바뀐 부분이 없지만 Chart태그의 마지막 부분에 OnCustomizelegend="Chart1_CustomizeLegend"로 핸들러를 지정해 주고 있음을 확인할 수 있습니다. 그리고 다음처럼 .cs 코드를 변경합니다
protected void Page_Load(object sender, System.EventArgs e)
{
double[] yValues = { 3000, 2200, 1800, 600, 300, 100 };
string[] xValues = { "콜라", "사이다", "오렌지주스", "홍차", "커피", "보리차" };
Chart1.Series["Default"].Points.DataBindXY(xValues, yValues);
Chart1.Series["Default"]["PieLabelStyle"] = "Inside";
Chart1.Series["Default"].Points[0]["Exploded"] = "true";
Chart1.ChartAreas["ChartArea1"].Area3DStyle.Enable3D = true;
Chart1.ChartAreas["ChartArea1"].Area3DStyle.Inclination = 55;
Chart1.ChartAreas["ChartArea1"].Area3DStyle.Rotation = 0;
LegendCellColumn cell1 = new LegendCellColumn();
cell1.ColumnType = LegendCellColumnType.SeriesSymbol;
cell1.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell1);
LegendCellColumn cell2 = new LegendCellColumn();
cell2.ColumnType = LegendCellColumnType.Text;
cell2.HeaderText = "종류";
cell2.Text = "#LEGENDTEXT";
cell2.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell2);
LegendCellColumn cell3 = new LegendCellColumn();
cell3.ColumnType = LegendCellColumnType.Text;
cell3.HeaderText = "판매량";
cell3.Text = "#VAL";
cell3.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell3);
}
protected void Chart1_CustomizeLegend(object sender, CustomizeLegendEventArgs e)
{
foreach (LegendItem item in e.LegendItems)
{
item.ImageStyle = LegendImageStyle.Rectangle;
item.BorderColor = Color.Gray;
}
}
범례의 내용을 꾸미기 위해서 약간 긴 코드가 나왔습니다. 위의 코드에 의한 결과는 다음과 같습니다.
범례의 내용은 LegendCellColumn 객체를 생성해 그 형태를 설정하고 Legend 객체의 CellColumns에 추가해 주는 방식으로 진행됩니다.
cell1.ColumnType = LegendCellColumnType.SeriesSymbol;
이렇게 컬럼의 형태를 지정해 주면 그림 2에서 가장 첫 열처럼 범례의 이 부분이 어떤 부분을 설명하는지 알려주는 심볼(색상이 있는 네모로 되어 있는 부분)로 지정하게 됩니다. 심볼은 여러 가지 형태로 꾸밈이 가능하며 이 부분에 대해서는 다음 예제에서 자세하게 살펴보겠습니다.
cell2.ColumnType = LegendCellColumnType.Text;
두 번째 열과 세 번째 열은 위와 같은 코드를 통해서 텍스트 형태의 값이 바인딩 되도록 알려주고 있습니다. Text로 지정된 부분에는 단순한 정적 텍스트가 위치할 수도 있지만 범례의 특성상 #(Sharp)으로 시작되는 바인딩 구문이 사용됩니다. 여기에서는 #LEGENDTEXT로 해당 요소의 이름과 #VAL로 해당 요소의 값을 출력하도록 설정하였습니다. 그 외에 각각의 열의 헤더 부분의 스타일을 지정해 주었습니다.
마지막으로 살펴 볼 부분은 Chart1_CustomizeLegend() 메서드 입니다. 이 메서드는 GridView의 GridView_ItemBound() 처럼 런타임에 범례의 내용을 변경하는 데 사용됩니다. 범례에 있는 심볼의 모양을 네모 혹은 선 형태로 지정해 주기 위해서는 이 메서드를 사용해야 합니다. 이번 예제에서는 사용되는 모든 심볼을 사각형 형태로 하고 외곽에 회색 선으로 테두리를 두르도록 지정하고 있습니다.
이것으로 간단하게 나마 범례를 꾸미는 법을 살펴보았습니다. 하지만 이것 만으로는 여러 가지로 부족함이 느껴집니다. 위에서 집계 함수 등에 대한 뉘앙스를 풍기는 이야기를 하기도 했는데 단지 요소의 이름과 값만을 범례에 표시해 줄 수 있다면 꽤나 힘 빠지는 일이 되겠죠? 구조상 원형 차트는 여러 가지 범례의 요소를 담기에 부족하기 때문에 차트의 형태를 막대로 바꾸어 여러 가지 범례의 요소를 다루어 보겠습니다.
다음처럼 HTML 코드를 작성합니다.
<asp:chart id="Chart1" runat="server" BackColor="White" Height="400px"
Width="900px" AntiAliasing="All" ImageType="Png"
PaletteCustomColors="200, 222, 52, 8; 200, 8, 89, 140; 200, 189, 186, 189;
200, 49, 130, 239; 200, 255, 174, 57; 200, 0, 150, 0;"
Palette="None" oncustomizelegend="Chart1_CustomizeLegend">
<titles>
<asp:Title Font="맑은 고딕, 18pt, style=Bold" Text="음료 판매량"
ForeColor="0, 0, 0"></asp:Title>
</titles>
<legends>
<asp:Legend BackColor="White" BorderColor="Black" Alignment="Near"
Docking="Right" Font="맑은 고딕, 9pt" IsTextAutoFit="False"
Name="Default" LegendStyle="Column"></asp:Legend>
</legends>
<borderskin SkinStyle="None"></borderskin>
<series>
<asp:Series ChartType="Column" BorderColor="Gray" Name="Series1" />
<asp:Series ChartType="Column" BorderColor="Gray" Name="Series2" />
<asp:Series ChartType="Column" BorderColor="Gray" Name="Series3" />
<asp:Series ChartType="Column" BorderColor="Gray" Name="Series4" />
<asp:Series ChartType="Column" BorderColor="Gray" Name="Series5" />
</series>
<chartareas>
<asp:ChartArea Name="ChartArea1" BorderColor="64, 64, 64, 64"
BackSecondaryColor="Transparent" BackColor="Transparent"
ShadowColor="Transparent" BorderWidth="0">
<area3dstyle Rotation="0" />
<axisy LineColor="64, 64, 64, 64">
<LabelStyle Font="맑은 고딕, 10pt, style=Bold" />
<MajorGrid LineColor="64, 64, 64, 64" />
</axisy>
<axisx LineColor="64, 64, 64, 64">
<LabelStyle Font="맑은 고딕, 10pt, style=Bold" />
<MajorGrid LineColor="64, 64, 64, 64" />
</axisx>
</asp:ChartArea>
</chartareas>
</asp:chart>
그리고 다음처럼 .cs 코드를 작성합니다.
protected void Page_Load(object sender, System.EventArgs e)
{
string[] xValues = { "콜라", "사이다", "오렌지주스", "홍차", "커피" };
Random rand = new Random();
for (int i = 0; i < 5; i++)
{
Chart1.Series[i].Name = xValues[i];
int[] values = new int[10];
for (int j = 0; j < 5; j++)
{
Chart1.Series[i].Points.AddY(rand.Next(1000));
}
}
LegendCellColumn cell1 = new LegendCellColumn();
cell1.ColumnType = LegendCellColumnType.SeriesSymbol;
cell1.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell1);
LegendCellColumn cell2 = new LegendCellColumn();
cell2.ColumnType = LegendCellColumnType.Text;
cell2.HeaderText = "종류";
cell2.Text = "#LEGENDTEXT";
cell2.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell2);
LegendCellColumn cell3 = new LegendCellColumn();
cell3.ColumnType = LegendCellColumnType.Text;
cell3.HeaderText = "평균";
cell3.Text = "#AVG";
cell3.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell3);
LegendCellColumn cell4 = new LegendCellColumn();
cell4.ColumnType = LegendCellColumnType.Text;
cell4.HeaderText = "합계";
cell4.Text = "#TOTAL";
cell4.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell4);
LegendCellColumn cell5 = new LegendCellColumn();
cell5.ColumnType = LegendCellColumnType.Text;
cell5.HeaderText = "최대판매량";
cell5.Text = "#MAX";
cell5.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell5);
LegendCellColumn cell6 = new LegendCellColumn();
cell6.ColumnType = LegendCellColumnType.Text;
cell6.HeaderText = "최소판매량";
cell6.Text = "#MIN";
cell6.HeaderBackColor = Color.WhiteSmoke;
Chart1.Legends["Default"].CellColumns.Add(cell6);
Chart1.ChartAreas[0].AxisX.Title = "월";
Chart1.ChartAreas[0].AxisX.TitleAlignment = StringAlignment.Far;
Chart1.ChartAreas[0].AxisX.TitleFont = new Font("맑은 고딕", 9, FontStyle.Bold);
Chart1.ChartAreas[0].AxisY.Title = "판매량(개)";
Chart1.ChartAreas[0].AxisY.TextOrientation = TextOrientation.Horizontal;
Chart1.ChartAreas[0].AxisY.TitleAlignment = StringAlignment.Far;
Chart1.ChartAreas[0].AxisY.TitleFont = new Font("맑은 고딕", 9, FontStyle.Bold);
}
protected void Chart1_CustomizeLegend(object sender, CustomizeLegendEventArgs e)
{
Random rand = new Random();
for(int i = 0; i < e.LegendItems.Count; i++)
{
LegendItem item = (LegendItem)e.LegendItems[i];
switch (i % 3)
{
case 0:
item.ImageStyle = LegendImageStyle.Rectangle;
item.BorderColor = Color.Gray;
break;
case 1:
item.ImageStyle = LegendImageStyle.Line;
break;
case 2:
item.ImageStyle = LegendImageStyle.Marker;
item.MarkerStyle = MarkerStyle.Diamond;
break;
}
}
}
이 코드에 의한 결과는 다음과 같습니다.
꽤나 긴 코드이지만 지금까지 작성하였던 많은 코드들 중에서 가장 '통계'라는 개념을 잘 보여주는 결과물이 아닐까 합니다. 코드는 이런저런 꾸미는 부분이 들어가 있기 때문에 꽤 길지만 두 가지 포인트만 짚어 보도록 하겠습니다. 우선 앞에서 '#'으로 시작하는 바인딩 구문을 이용해서 여러 가지 값을 표현한다는 말이 있었습니다. 이번 예제에서는 여러 가지 바인딩 구문을 사용해서 표의 내용을 표현해 봤습니다. #LEGENDTEXT는 앞에서 살펴본 것 처럼 요소의 이름입니다. #AVG는 이름 그대로 평균 값 입니다. #TOTAL는 총합을 나타냅니다. #MAX는 최대값이 되겠구요. #MIN는 최소값이 되겠군요. 이와 같이 다양한 바인딩 구문을 이용하면 차트를 통계적 관점에서도 유용하게 사용할 수 있습니다. 바인딩 구문에 대해서는 다음의 링크를 참고하시기 바랍니다.
http://blogs.msdn.com/alexgor/archive/2008/11/11/microsoft-chart-control-how-to-using-keywords.aspx
Microsoft Chart Control How-to: Add Rich Content Using Keywords(Alex Gorev)
다음에 살펴볼 부분은 심볼을 꾸미는 부분입니다. 앞에서 _CustomizeLegend() 메서드를 통해서 범례의 내용을 꾸민다고 했는데요 실제로 어떻게 꾸미는 코드가 사용되는지 살펴보겠습니다.
메서드에는 CustomizeLegendEventArgs e 라는 범례의 요소에 접근할 수 있는 인자를 제공합니다. 예제 코드에서는 요소의 수만큼 반복을 하고 순서대로 네모모양, 선모양, 마커로 심볼의 형태를 지정해 주고 있습니다. 실제로 차트를 꾸미는 경우에는 요소의 값을 조사해 비교하는 형식으로 심볼의 모양을 지정하게 되겠지만 예제는 예제인 만큼 간단한 반복문으로 구성해 봤습니다.
지금까지 살펴본 내용과 알려드린 링크를 참조하시면 완전하지는 않더라도 불편함 없이 여러 차트의 범례를 조립하실 수 있으리라 생각합니다. 이제 차트에 대한 글에도 거의 끝이 보이네요. 사실 범례를 살펴 봄으로서 ASP.NET에서 차트를 사용하는데 필요한 필요 충분한 부분은 모두 살펴본 것이라고 할 수 있습니다. 마지막 남은 두 글에서는 공백 값의 처리와 윈도우즈 응용프로그램에서의 차트의 사용에 대해서 살펴보도록 하겠습니다. 즐거운 프로그래밍 되세요!