4개의 축

축은 차트의 가장자리에 표시되는 눈금이다. ChartArea에 소속되며 Axes 속성의 컬렉션 편집기를 열어 보면 축의 목록이 나타난다.

가로쪽이 X축이고 세로쪽이 Y축이며 양쪽으로 각각 붙일 수 있어 에리어당 총 4개의 축이 있다. 편집기에서 보다시피 4개의 축이 미리 등록되어 있으며 더 만들거나 삭제할 수는 없고 속성만 조정할 수 있다.

축의 여러 가지 속성을 테스트해 보기 위해 가로축을 시간으로 하는 차트를 만들어 보자. 

 

private void Form1_Load(object sender, EventArgs e)

{

       chart1.Legends[0].Enabled = false;

       Random R = new Random(100);

       DateTime dt = new DateTime(2020, 10, 1, 9, 0, 0);

       for (int i = 0;i<20;i++)

       {

                  chart1.Series[0].Points.AddXY(dt, R.Next(10, 100));

                  dt = dt.AddMinutes(1);

       }

}

 

오전 9시부터 1분 간격으로 10~100 사이의 난수로 차트를 그렸다. 세로축은 점수라고 생각하면 된다. 이 코드 아래쪽에 속성을 조정하는 코드를 작성하여 실습을 진행한다.

아래쪽이 X축이며 시간을 보여 준다. 왼쪽이 Y축이며 0 ~ 100까지 20 간격으로 값을 표시한다. 축에는 다음 요소가 배치된다.

 

틱마크 : 정확한 위치를 표시하는 선이다. 위 그림의 Y축을 보면 20, 40, 60 자리에 왼쪽으로 약간 삐져 나온 직선이 틱마크이다.

레이블 : 위치나 값을 설명하는 문자열이다. Y축의 20, 40이나 X축의 날짜가 레이블이다. 주기, 포맷, 속성 등에 따라 모양이 천차 만별로 달라진다.

그리드 : 차트의 플롯 영역에 표시되는 바둑판 모양의 격자이다. 틱마크가 플롯 영역으로 확장된 것이다.

 

디폴트로 X축은 아래쪽, Y축은 왼쪽만 표시되어 있어 2개의 주축만 보인다. 오른쪽과 위쪽에도 보조축이 있지만 숨겨져 있다. 이 축도 보이도록 해 보자. 아래쪽에 다음 코드를 작성한다.

 

chart1.ChartAreas[0].AxisX2.Enabled = AxisEnabled.True;

chart1.ChartAreas[0].AxisY2.Enabled = AxisEnabled.True;

 

AxisX2AxisY2 축의 Enabled 속성을 True로 변경하면 축이 보인다. 이 속성을 Auto로 지정하면 차트의 타입이나 시리즈의 특성에 따라 자동으로 보임 여부를 결정한다. 축 객체의 이름을 지정하는 대신 에리어의 Axes 속성의 첨자를 통해 Axes[2], Axes[3]로 칭해도 효과는 같다.

오른쪽과 위쪽에도 축이 나타난다. 차트가 아주 크다면 축이 양쪽에 다 있는 것이 보기는 편리하지만 번잡스러워 보통은 주축만 표시한다.

축은 차트 플롯을 가리지 않도록 가장자리에 놓는 것이 무난하지만 필요하다면 플롯 중간에 놓을 수도 있다. 축의 Crossing 속성은 상대축과 교차할 지점을 지정하는데 디폴트값은 NaN이며 이 경우 자동으로 적당한 곳에 배치된다.

double.MinValue로 지정하면 가장 작은값 쪽에 배치되고 double.MaxValue로 지정하면 가장 큰값쪽에 배치된다. 중간의 적당한 값으로 지정하면 해당 위치에 상대축이 배치된다. AxisYCrossing 속성으로 50 정도로 지정해 보자.

 

chart1.ChartAreas[0].AxisY.Crossing = 50;

 

Y축의 범위는 0 ~ 100까지인데 50으로 지정하면 상대축인 X축이 딱 중간 위치에 배치된다.

이렇게 되면 50보다 더 작은 값은 막대가 아래쪽으로 그려진다. Crossing 속성을 double.MaxValue로 지정하면 X축이 최대값인 100 자리로 올라가고 모든 막대는 아래쪽으로 뻗는다.

레이블의 포맷은 시리즈의 타입에 따라 자동으로 결정되는데 시간인 경우는 날짜가 디폴트로 되어 있다. 그래서 X축의 날짜값이 모두 같아 구분이 잘 되지 않는다. 예제의 차트는 분 단위이므로 날짜가 아닌 시간 포맷으로 표시하는 것이 더 좋다. 레이블의 포맷은 축의 LabelStyle.Format 속성으로 지정한다. 다음 코드를 작성해 보자.

 

Axis ax = chart1.ChartAreas[0].AxisX;

Axis ay = chart1.ChartAreas[0].AxisY;

ax.LabelStyle.Format = "HH:mm";

 

X, Y 양쪽으로 두 축의 속성을 계속 바꿔 볼 것이므로 주축에 대한 참조를 ax, ay로 미리 구해 두었다. 컬렉션에 의한 중첩이 워낙 깊다 보니 자주 사용하는 객체는 참조를 따로 구한 후 사용하는 것이 편리하다. ax의 레이블 포맷을 HH:mm으로 표시하면 시:분 만 레이블에 표시된다.

이제 X축의 시간이 서로 구분된다. "HH:mm:ss"로 붙이면 초단위도 나타나지만 이 차트의 데이터가 분단위여서 별 의미가 없다.

차트는 5분 간격으로 틱마크와 레이블을 하나씩 표시한다. 모든 X값에 레이블을 일일이 배치할 공간이 충분하지 않아 적당히 건너 뛴다. 심지어 차트를 확대하거나 축소해도 이 간격을 유지한다. 차트의 크기와 레이블의 간격은 IntervalAutoMode 속성으로 지정하는데 이 속성의 디폴트가 FixedCount이기 때문이다. 이 속성을 VariableCount로 변경해 보자.

 

ax.IntervalAutoMode = IntervalAutoMode.VariableCount;

ay.IntervalAutoMode = IntervalAutoMode.VariableCount;

 

X, Y 양축 모두 가변 개수로 변경하였다. 이 상태에서 차트의 크기를 조정해 보면 크기에 따라 레이블의 간격과 개수가 달라진다.

 

자리가 좁으면 레이블을 조금만 보여 주고 넉넉하게 넓으면 가급적 많은 레이블을 보여준다. 데이터 개수가 가변적이고 차트 크기도 자주 바뀐다면 이 방식이 유리하지만 일관성이 없는 것은 단점이다.

레이블의 주기를 강제로 지정하려면 IntervalType, Interval 속성을 사용한다. Interval은 주기를 지정하며 이 값마다 레이블이 하나씩 표시된다. 단 값이 시간이면 Interval이 어떤 단위인지를 IntervalType으로 밝힌다. IntervalAutoMode는 해제하고 다음 코드를 작성해 보자.

 

ax.IntervalType = DateTimeIntervalType.Minutes;

ax.Interval = 2;

ay.Interval = 10;

 

X축은 매 2분마다 레이블을 표시하고 Y축은 매 10마다 레이블을 표시하라고 지시했다.

지시한대로 정확하게 레이블이 표시된다. 심지어 크기를 줄여도 레이블을 지그재그로 겹치거나 세로로 세워서라도 이 간격을 유지한다.

폼을 더 작게 줄이면 레이블끼리 겹치기도 한다. 웬만하면 자동으로 결정하도록 내버려 두는 것이 편리하지만 강제로 지정하면 이 지시를 가급적 지키기 위해 애쓴다.

축의 모양을 지정하는 속성은 직관적이고 쉽다. 대부분 색상이나 선모양, 타이틀 등의 속성은 단순한 장식이어서 코드를 작성해 보면 쉽게 이해할 수 있다.

 

속성

설명

Title

축의 제목이다.

TitleAlignment

제목의 정렬 방향이다. 디폴트는 Center이며 Near는 가까운 쪽, Far는 먼쪽이다. 좌우, 상하가 아닌 이유는 가로, 세로에 따라 방향이 다르고 우에서 좌로 읽는 레이아웃도 있기 때문이다.

TitleFont

제목의 폰트

TitleForeColor

제목의 색상

TextOrientation

제목의 방향. 디폴트는 차트 타입에 따라 자동으로 결정하는 Auto이되 수평, 90도 회전, 270도 회전, 쌓기 등이 있다.

ArrowStyle

축 끝의 화살표 모양이다. 디폴트는 None이며 Line은 화살표 모양, Triangle은 삼각형, ShartTriangle은 길쭉한 삼각형이다.

Enabled

사용 여부이다. 이 값이 false이면 축의 요소는 모두 표시되지 않는다. 주축도 없애 버릴 수 있다.

LineColor

선색상

LineWidth

선 굵기

LineDashStyle

선 모양

IsReserved

축의 방향을 반대로 뒤집는다.

IsMarginVisible

축 끝에 약간의 마진을 줄 것인가를 지정한다.

 

선모양을 바꾸고 축에 제목을 붙여 보자.

 

ax.LineColor = Color.Green;

ax.LineWidth = 3;

ax.ArrowStyle = AxisArrowStyle.Triangle;

ay.ArrowStyle = AxisArrowStyle.Lines;

 

ax.Title = "날짜";

ay.Title = "점수";

ay.TextOrientation = TextOrientation.Horizontal;

IsReversed 속성은 축의 방향을 반대로 뒤집는다. ax.IsReversed = true; 대입문을 추가하면 X축의 시간이 좌에서 우로가 아닌 우에서 좌로 증가한다.

IsMarginVisible 속성은 끝 부분에 약간씩 여백을 주는데 디폴트는 true이다.

 

ax.IsMarginVisible = false;

ay.IsMarginVisible = false;

 

이 값을 false로 변경하면 여백이 사라지고 너무 꼭 맞게 그려져 답답해 보인다. 대개의 경우는 마진이 있는 것이 무난하다. 데이터 수가 대단히 많고 가장자리의 값이 그다지 중요치 않다면 마진을 없애는 것이 보기 좋다.

X축의 첫 항목은 막대가 반으로 잘려 보이고 Y축은 100점이 없다. 대부분의 속성은 이런 식으로 값을 바꿔가며 차이점을 관찰해 보면 쉽게 이해할 수 있다. 글만 읽어서는 따분하지만 속성을 바꿔 가며 실습해 보면 나름 재미도 있고 속성의 의미를 분명히 알 수 있다.

레이블

레이블의 속성은 축의 LabelStyle 속성으로 조정하며 이는 또 다른 객체이다. LabelStyle의 멤버는 다음과 같다.

 

속성

설명

Format

레이블의 포맷이다. 시간인 경우 yyyy-MM-dd HH:mm:ss 식으로 시간 요소를 포맷팅한다. 실수는 0#.## 식으로 선행 제로와 소수점 이하의 정밀도를 지정한다.

Font

레이블의 폰트를 지정한다.

Fore​Color

레이블의 색상을 지정한다. 디폴트는 검정색이다.

Enabled

레이블 표시 여부를 지정한다. false이면 레이블을 숨긴다.

Angle

레이블의 각도를 지정한다.

Interval

레이블의 간격

Interval​Type

간격의 타입

Interval​Offset

레이블의 시작점

Interval​Offset​Type

레이블 시작점의 타입

Is​End​Label​Visible

마지막 레이블 표시 여부

Is​Staggered

레이블을 지그재그로 계단 형태로 표시할 것인지를 지정한다.

Truncated​Labels

레이블의 일부가 잘려도 상관 없는지 지정한다.

 

레이블이 문자열이기 때문에 폰트와 관련된 속성이 많다. 다음 코드를 작성해 보자.

 

ax.LabelStyle.Format = "HH mm";

ax.LabelStyle.Font = new Font("궁서", 15);

ax.LabelStyle.ForeColor = Color.Red;

ax.LabelStyle.Angle = -45;

 

레이블에 한글이 궁서체 빨간색으로 표시되며 각도를 45도로 기울여 표시한다.

-90으로 각도를 지정하면 수직으로 레이블을 표시한다. IsStaggered 속성을 true로 지정하면 각도 속성은 무시되며 레이블이 2층으로 계단을 이루어 표시된다. 이처름 속성끼리 상호 배타적인 경우도 있다.

 

ax.LabelStyle.IsStaggered = true;

IntervalIntervalType 속성은 축 자체에도 있고 레이블에도 있는데 이 두 속성은 적용되는 곳이 다르다. 축의 속성은 틱 마크와 그리드, 레이블 등에 모두 적용되는데 비해 레이블의 속성은 레이블에만 지역적으로 적용된다. 다음 코드로 확인해 보자.

 

ax.IntervalType = DateTimeIntervalType.Minutes;

ax.Interval = 2;

ax.LabelStyle.IntervalType = DateTimeIntervalType.Minutes;

ax.LabelStyle.Interval = 5;

 

축의 간격은 2분으로 지정하고 레이블의 간격은 5분으로 지정했다. 출력 형태를 잘 관찰해 보자.

틱마크와 그리드는 2분에 하나씩 표시되지만 레이블은 5분에 하나씩 표시된다. 틱마크는 3분에 하나씩 표시하고 레이블은 1분에 하나씩 표시할 수도 있다.

IntervalOffset은 레이블의 시작점을 지정한다. 이 속성의 의미를 알아 보려면 간격이 좀 있어야 한다. 레이블의 간격을 3분으로 지정하면 다음과 같이 표시된다.

정각, 3, 6, 9분 식으로 레이블이 표시되는데 이 경우는 오프셋이 0이다. 오프셋을 지정하면 그만큼 건너뛴 후 레이블을 표시한다.

 

ax.IntervalOffset = 1;

ax.IntervalOffsetType = DateTimeIntervalType.Minutes;

 

IntervalOffset1로 지정하면 제일 처음부터 1분 건너 뛰고 3분 간격으로 레이블을 표시한다.

1, 4, 7분 식으로 레이블이 표시된다. 2로 지정하면 2, 5, 8 식으로 증가하며 3으로 지정하면 0으로 지정한 것과 같다. IntervalOffset 속성은 축 자체에도 있어 틱 마크와 그리드의 시작점도 지정할 수 있다.

그리드와 틱마크

축의 MajorGrid, MinorGird 속성은 주 그리드와 보조 그리드의 속성을 지정한다. 두 속성 모두 Grid 객체이며 다음 속성을 가진다.

 

Enabled, Interval, IntervalType, IntervalOffset, IntervalOffsetType, LineColor, LineDashStyle, LineColor

 

더 설명이 필요 없을 정도로 직관적이며 앞에서 다 실습했던 것이다. 디폴트로 MajorGrid만 검정색 실선으로 표시하며 간격은 축의 설정을 따른다. 그래서 차트 영역에 바둑판 모양의 그리드가 표시된다. 그리드의 속성을 변경하면 이 모양을 얼마든지 원하는대로 바꿀 수 있다.

 

ax.MajorGrid.Enabled = false;

ay.MajorGrid.LineWidth = 2;

ay.MajorGrid.Interval = 40;

 

ay.MinorGrid.Enabled = true;

ay.MinorGrid.Interval = 10;

ay.MinorGrid.LineDashStyle = ChartDashStyle.Dot;

ay.MinorGrid.LineColor = Color.Gray;

X축의 그리드는 아예 없애 버렸다. Y축은 40 간격으로 2 픽셀 두께로 주 그리드를 긋고 보조 그리드는 회색의 얇은 점선으로 10 간격으로 그렸다. 그리드는 어디까지나 차트를 보기 편하게 해 주는 보조선이므로 너무 요란하지 않는 것이 바람직하다.

틱마크의 구조도 그리드와 거의 유사하다. 축의 MajorTickMark, MinotTickMark 속성으로 편집하며 둘 다 TickMark 객체이다. 이 클래스의 속성도 그리드와 유사하되 다음 속성이 더 있다.

 

TickMarkStyle : 틱 마크를 배치할 방향을 지정한다. 디폴트는 OutsideArea여서 에리어 바깥으로 삐져 나오지만 InsideArea로 바꾸면 에리어 안쪽에 표시되며 AcrossAxis는 양쪽에 표시된다. None은 틱마크를 표시하지 않는다.

Size : 틱마크의 크기를 지정한다. 차트 이미지에 대한 백분율로 지정한다. 디폴트는 1.0이며 폭과 높이의 1% 비율로 틱마크를 그린다.

 

다음 코드로 틱마크를 프로그래밍해 보자.

 

ax.MajorTickMark.Enabled = false;

ay.MajorTickMark.Interval = 50;

ay.MajorTickMark.LineWidth = 3;

ay.MajorTickMark.LineColor = Color.Red;

ay.MajorTickMark.Size = 3;

ay.MinorTickMark.Enabled = true;

ay.MinorTickMark.Interval = 10;

ay.MinorTickMark.TickMarkStyle = TickMarkStyle.InsideArea;

X축 틱마크는 아예 없애 버렸다. Y축은 50 간격으로 빨간색의 굵기 3, 차트 너비의 3% 길이로 주틱마크를 배치하고 10 간격으로 보조 틱마크를 에리어 안쪽에 배치했다.

축의 범위

축이 표현하는 범위는 들어오는 데이터에 따라 자동으로 결정하며 대개의 경우 차트가 알아서 설정하도록 내버려 두는 것이 무난하다. 우리의 예에서는 X축이 9시 정각 ~ 919분까지이고 Y축은 10 ~ 100까지의 점수값이다. 그래서 이 범위에 맞게 양축의 범위를 알아서 지정한다.

값의 범위에 너무 꼭 맞게 축범위를 설정하면 여유가 없어 약간의 여분을 더하는데 IsMarginVisible 속성으로 여분을 줄 것인가를 지정한다. 이 외에도 축의 범위를 설정하는 여러 가지 속성이 있다. Y값의 범위를 다음과 같이 바꾸어 보자.

 

for (int i = 0;i<20;i++)

{

       chart1.Series[0].Points.AddXY(dt, R.Next(10, 100) + 100);

       dt = dt.AddMinutes(1);

}

 

원래는 10~100까지였지만 여기에 100을 더해 110 ~ 200까지로 범위를 수평 이동시켰다.

값이 모두 위로 100만큼 수평 이동하여 아래쪽은 다 가득차 있는 모양이다. 100 이하의 값이 없는데 Y축의 범위를 0 ~ 200으로 잡는 것은 공간을 낭비한다. 만약 값이 1000 ~ 1100 사이에 분포한다면 낭비는 더 심해지고 시간별 값의 차이도 분명히 보이지 않을 것이다.

차트는 모든 값이 양수이면 범위의 시작을 항상 0부터 시작하도록 되어 있다. 값 중에 음수가 있다면 가장 작은 값을 시작값으로 잡는다. 이런 동작은 Is​Started​From​Zero 속성으로 제어하며 이 값의 디폴트가 true이다. 이 속성을 false로 변경하면 0이 아닌 적당한 시작점을 선정한다.

 

ay.IsStartedFromZero = false;

Y축의 시작점이 100으로 설정된다. 0을 시작점으로 하지 않고 실제 데이터가 존재하는 구간을 분석하여 적당한 범위를 잡는다. 110~200 사이이면 100 ~ 200 정도의 범위가 합당하다. IsMarginVisible 속성까지 false로 지정하면 정말 데이터가 실존하는 부분만 범위로 설정한다.

최소값인 113 ~ 최대값인 199까지 범위로 설정된다. 공간을 가득 채워서 알뜰하게 쓰기는 하지만 Y축의 레이블이 딱 떨어지지 않아 미관상 좋지 않고 X축의 제일 마지막인 113값은 아예 막대가 그려지지 않아 없는 것처럼 보인다.

축의 범위는 Axis 객체의 Minimum, Maximum 속성으로 조정한다. 두 속성 모두 디폴트는 NaN이며 차트가 알아서 결정하도록 되어 있다. 범위를 직접 지정하려면 두 속성에 원하는 최소값과 최대값을 지정한다. 값 생성문의 +100을 제거하여 10 ~ 100 사이의 값을 생성하고 Y축의 범위를 다음과 같이 직접 지정해 보자.

 

ay.Minimum = -20;

ay.Maximum = 200;

범위를 직접 지정하면 0부터 시작, 여백 등의 다른 모든 속성은 무시하고 지정한 최소값과 최대값의 범위를 강제로 적용한다. 최소값이 -20이다 보니 막대의 아래쪽은 비어 있고 최대값이 200이어서 100 이상의 공간도 비어 있어 시원스럽기는 하다. 값의 범위보다 더 좁게 설정할 수도 있다.

 

ay.Minimum = 30;

ay.Maximum = 50;

실제값보다 범위를 더 좁게 잡으면 아래 위가 잘린다. 30보다 작은 값은 모두 막대가 생략되어 실제값을 알 수 없으며 50보다 더 큰 값은 모두 같아 보인다. 문제가 있지만 어쨌거나 차트는 명령한 대로 범위를 표현할 뿐이다.

여러 개의 시리즈를 같은 에리어에 표시할 때 차트는 모든 시리즈의 범위를 포괄하는 공통의 범위를 잡는다. 공통 범위가 너무 넓어 특정 시리즈가 잘 보이지 않거나 특이값이 있는 시리즈로 인해 공간이 낭비된다면 적당한 수준의 범위를 강제 지정하는 편이 더 나을 수도 있다.

다음은 X축의 범위를 지정해 보자. Y축은 숫자여서 최소, 최대값을 지정하기 편리하지만 X축은 시간값이어서 단순한 숫자가 아니다. 원본 데이터는 9:00 ~ 9:19까지 있지만 이중 앞뒤는 잘라먹고 9:05~9:15까지만 범위를 설정해 보자.

 

ax.Minimum = new DateTime(2020, 10, 1, 9, 5, 0);

ax.Maximum = new DateTime(2020, 10, 1, 9, 15, 0);

 

이렇게 되면 쉽겠지만 이 코드는 에러이다. 왜냐하면 Minimum, Maximum 속성의 타입은 DateTime이 아니라 double이기 때문이다. X축이 시간으로 고정되어 있지 않고 순서값인 경우가 더 일반적이어서 double 타입으로 정의되어 있다.

X축의 한 지점도 double 타입으로 표현해야 하는데 이 때 사용하는 타입이 OADate이다. OADateOLE Automation에서 사용하던 타입이며 날짜를 실수 포맷으로 표현한다. 18991230일 자정을 0으로 잡고 경과한 날짜를 정수부에, 경과한 시간을 실수부에 저장한다.

시간이 날짜의 소수부인 것은 논리적으로 합당하다. OADate 타입에서 1은 하루이며 0.5는 반나절인 12시간이다. 하루가 86400초이며 고작 십진수 다섯자리에 불과해 double의 정밀도이면 충분히 긴 날짜와 충분히 정밀한 시간을 표현할 수 있다. DateTime 클래스는 OADate 타입으로 변환하는 메서드를 제공한다.

 

public static DateTime FromOADate (double d);

public double ToOADate ();

 

FromOADate는 실수값으로부터 DateTime 객체를 만든다. 새로 객체를 만들어야 하므로 static이다. ToOADate는 반대로 DateTime 객체를 double 타입으로 변환한다. 이 메서드를 사용하면 원하는 시간을 정확히 지정할 수 있다.

 

ax.Minimum = (new DateTime(2020, 10, 1, 9, 5, 0)).ToOADate();

ax.Maximum = (new DateTime(2020, 10, 1, 9, 15, 0)).ToOADate();

 

20201019:5double 타입으로 44105.3784이다. 1899년으로부터 44105일이 지났으며 하루 24시간의 대략 37%쯤의 시간이라는 뜻이다. 차트는 X축의 시간을 이 타입으로 저장한다. 따라서 범위를 지정할 때도 double 타입으로 바꿔서 지정해야 한다. 결과는 다음과 같다.

X축의 9:05~9:15까지만 표시되며 나머지는 잘려 보이지 않는다. 물론 더 넓게 잡을 수도 있다. Minimum은 디폴트로 두고 Maximum10시 정각으로 조정해 보자.

 

ax.Maximum = (new DateTime(2020, 10, 1, 10, 0, 0)).ToOADate();

뒤쪽은 데이터가 없어 비어 있지만 어쨌거나 지정한 범위만큼 정확히 표시된다. 시간을 double 타입으로 변환하는 방법만 알면 Y축의 범위를 지정하는 방법과 같다.

앞 항에서 그리드와 틱마크를 실습할 때 주로 Y축만을 대상으로 했는데 X축의 한 지점을 기술하는 방법을 몰랐기 때문이다. X축도 시간값을 실수로 변환해서 지정하면 Y축과 방법은 같다.

, 스크롤

실제 통계자료는 데이터가 너무 방대해 한 화면에 다 보여줄 수 없다. 많은 데이터를 차트에 표시해 보자.

 

private void Form1_Load(object sender, EventArgs e)

{

       chart1.Legends[0].Enabled = false;

       Random R = new Random(100);

       double value = 10;

       for (int i = 0;i<200;i++)

       {

                  value += R.Next(-4, 5);

                  chart1.Series[0].Points.AddY(value);

       }

       Axis ax = chart1.ChartAreas[0].AxisX;

       Axis ay = chart1.ChartAreas[0].AxisY;

       chart1.Series[0].ChartType = SeriesChartType.Area;

}

 

10에서 시작하여 +-4만큼 오르락내리락거리는 200개의 무작위 데이터를 생성했으며 차트 타입은 Area로 지정하였다. X축은 0 ~ 199까지의 순서값이다.

대충의 증감 정도는 파악할 수 있지만 특정 시점의 값이 얼마인지 한눈에 가늠하기 어렵다. 데이터가 많을수록 자세히 보기 어렵다. 이럴 때는 줌, 스크롤 등의 기법을 사용하여 원하는 부분만 확대하고 스크롤해서 봐야 한다. 다음 코드 한줄을 추가해 보자.

 

ax.ScaleView.Zoom(50, 70);

 

ScaleViewZoom 메서드로 범위를 지정하면 이 범위만 확대해서 보여준다. X축을 50 ~ 70까지의 범위만 보인다.

범위 바깥은 잘려서 보이지 않지만 아래쪽에 스크롤 바가 생겨 이동할 수 있다. Y축도 마찬가지로 일정 범위로 제한하면 전체 차트의 한 부분만 보게 된다.

메서드가 아닌 실행중에 사용자가 선택한 부분만 확대해서 보려면 Cursor, ScaleView, ScrollBar 세 개의 객체를 다 알아야 한다. 하나씩 연구해 보자.

Cursor는 축의 현재 위치를 알려 주는 선이다. X축의 커서는 수직선이고 Y축의 커서는 수평선이며 두 선이 만나는 곳이 사용자가 관심을 가지는 부분이다. 커서는 ChartAreaCursorX, CursorY 속성으로 제어하며 둘 다 Cursor 타입이다. 주요 속성은 다음과 같다.

 

속성

설명

Is​User​Enabled

사용자가 커서를 제어할 수 있는지를 지정한다. 디폴트는 false여서 사용자가 클릭해서 커서를 표시할 수 없다. 이 속성과 상관없이 코드에서 Position 속성을 통해 커스를 표시할 수는 있다.

Position

커서의 현재 위치이다. 코드에서 이 속성을 변경할 수 있으며 커서를 숨길 때는 이 속성을 NaN으로 지정한다.

Is​User​Selection​Enabled

사용자가 영역을 드래그하여 일정 범위를 선택할 수 있는지를 지정한다. 디폴트는 false이다.

Auto​Scroll

영역 선택중에 경계 밖으로 커서가 이동하면 자동으로 스크롤할 것인지를 지정한다. 자동 스크롤이 가능하려면 스크롤하는 방향으로 데이터가 더 있어야 한다. 디폴트는 false이다.

Selection​Color

선택 영역의 색상이다. 디폴트는 알파값이 120LightGray이며 반투명하다. 양축을 다 지정할 경우 Y축 색상을 사용한다.

Selection​Start

Selection​End

선택영역의 시작과 끝, 즉 선택 범위이다. 사용자가 드래그하여 범위를 설정하면 이 값이 변경되며 코드에서 Set​Selection​Position 메서드로 선택 영역을 지정한다. 선택을 해제하려면 둘 다 NaN으로 지정한다.

LineColor

LineDashStyle

LineWidth

선의 색상, 모양, 굵기이다. 디폴트는 1픽셀 굵기의 빨간 실선이다.

Interval

IntervalType

IntervalOffset

IntervalOffsetType

커서가 나타날 수 있는 간격이다. 디폴트는 0이며 차트의 어느 위치에나 커서를 배치할 수 있다. 간격을 5로 지정하면 5의 배수 위치에만 커서가 표시되며 영역을 선택할 때도 같은 방식으로 적용된다. Position 속성을 직접 지정할 때는 이 간격을 무시한다.

Axis​Type

커서가 부착된 축이 주축인지, 부축인지를 지정한다.

 

Zoom 메서드 호출문은 제거하고 다음 코드를 작성한다. 사용자가 커서를 다룰 수 있도록 했으며 선은 3픽셀 두께로 하여 잘 보이도록 했다. 간격은 X축은 20, Y축은 10으로 지정했다.

 

chart1.ChartAreas[0].CursorX.IsUserEnabled = Enabled;

chart1.ChartAreas[0].CursorY.IsUserEnabled = Enabled;

chart1.ChartAreas[0].CursorX.LineWidth = 3;

chart1.ChartAreas[0].CursorY.LineWidth = 3;

chart1.ChartAreas[0].CursorX.Interval = 20;

chart1.ChartAreas[0].CursorY.Interval = 10;

 

실행 후 차트의 한 지점을 클릭하면 빨간색 수평, 수직선이 나타나며 두 점이 만나는 곳이 사용자가 클릭한 곳이다.

, 간격을 지정해 놓았기 때문에 정확히 클릭한 곳이 아닌 간격의 배수 위치에만 커서가 나타난다. Interval0으로 지정하면 임의 위치에 커서를 놓을 수 있다. 다음 코드는 사용자가 드래그하여 영역을 선택한다.

 

chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = Enabled;

chart1.ChartAreas[0].CursorX.AutoScroll = true;

chart1.ChartAreas[0].CursorY.IsUserSelectionEnabled = Enabled;

 

X축에 대해서만 자동 스크롤 기능을 켰다. 차트의 임의 영역을 드래그하면 반투명한 회색 영역이 나타난다.

이 상태에서 마우스를 놓으면 선택한 영역이 확대되며 스크롤 바가 나타난다. 스크롤바를 드래그하거나 양쪽의 버튼 또는 몸통을 클릭하여 임의의 위치로 이동할 수 있다.

확대한 상태에서 수평으로 영역을 확장할 때는 자동으로 스크롤된다. 수직쪽으로는 AutoScroll 속성을 주지 않아 자동 스크롤되지 않는다. 확대한 상태에서 영역을 다시 선택하여 더 좁은 범위를 확대할 수 있으며 제한은 없다. 스크롤 바 왼쪽과 위의 리셋 버튼을 누르면 직전 줌 상태로 복귀한다.

차트를 확장하면 전체 영역중 일부만 보이는데 이 일부만 표시하는 주체가 스케일뷰이다. 축의 ScaleView 속성으로 되어 있으며 AxisScaleView 클래스 타입의 객체이다. 이 객체의 속성을 통해 확대, 스크롤의 여러 정보를 조사하거나 설정한다.

 

속성

설명

Zoomable

사용자에 의해 줌 가능한 상태인지를 정의한다. 디폴트는 true이다.

Is​Zoomed

현재 줌 상태인가 아닌가를 조사하는 읽기 전용 속성이다. 최초 false이다가 사용자가 영역을 선택해 확대하면 true가 된다.

Min​Size

Min​Size​Type

스케일뷰의 최소 크기를 지정한다. 이 크기 이상으로는 더 확대되지 않는다.

Position

스케일뷰의 현재 위치이다.

Size

Size​Type

스케일뷰의 현재 크기이다.

View​Minimum

View​Maximum

축의 최소값과 최대값이다. 현재 표시하는 범위를 보여 주는 읽기 전용 속성이다.

Small​Scroll​Size

Small​Scroll​Size​Type

스크롤 바의 화살표 버튼을 클릭할 때 스크롤될 양인 작은 스크롤값이다. 디폴트는 NaN이며 자동으로 결정한다.

Small​Scroll​Min​Size

Small​Scroll​Min​Size​Type

작은 스크롤의 최소값이다. 작은 스크롤 크기를 지정하지 않았을 때 이 값을 사용한다.

 

스케일뷰를 디폴트 설정대로 두면 얼마든지 확대 가능하다. 그러나 MinSize를 지정하면 이 이상은 확대되지 않는다. 다음과 같이 설정하면 X, Y 양쪽으로 최소한 50 이상의 뷰를 확보한다. 값의 변화를 충분히 확인할 수 있다면 굳이 더 확대할 필요는 없다.

 

ax.ScaleView.MinSize = 50;

ay.ScaleView.MinSize = 50;

 

나머지 정보는 스케일뷰의 현재 상태를 조사 또는 설정하는 값이다. 줌 상태 변화에 의해 이 값이 어떻게 바뀌는지 찍어 보자. 차트의 Click 이벤트 핸들러를 작성하고 속성값을 조사한 후 타이틀바에 찍어 보면 된다.

 

private void chart1_Click(object sender, EventArgs e)

{

       Text = String.Format("Pos = {0}, Size = {1}, Min = {2}, Max = {3}",

                  chart1.ChartAreas[0].AxisX.ScaleView.Position,

                  chart1.ChartAreas[0].AxisX.ScaleView.Size,

                  chart1.ChartAreas[0].AxisX.ScaleView.ViewMinimum,

                  chart1.ChartAreas[0].AxisX.ScaleView.ViewMaximum);

}

 

편의상 X축만 찍어 봤는데 Y축도 마찬가지이다. 최초 실행시, 즉 확대하지 않았을 때는 PosSizeNaN이고 Min = 0, Max = 201로 조사된다. 드래그하여 영역을 적당히 확대해 보자.

X축의 현재 위치가 37이고 폭은 83이며 36 ~ 121까지의 범위를 보여 주고 있다는 뜻이다. 위치는 Zero Base여서 36번째 X좌표가 37번째 값인데 비해 Min, Max는 축의 X값을 그대로 보여준다. PositionSize는 쓰기도 가능해서 이 값을 변경하면 줌 위치와 크기가 즉시 바뀐다. 클릭 이벤트 끝에 다음 코드를 작성해 보자.

 

chart1.ChartAreas[0].AxisX.ScaleView.Position = 50;

chart1.ChartAreas[0].AxisX.ScaleView.Size = 80;

 

폭이 80이 되도록 즉시 확대되며 왼쪽 위치는 50에 맞추어진다. 코드에서 확대나 스크롤을 할 때는 속성을 직접 변경하는 것보다는 메서드를 사용하는 것이 더 편리하다.

 

void Zoom (double viewStart, double viewEnd);

void Zoom (double viewPosition, double viewSize, DateTimeIntervalType viewSizeType, bool saveState);

void Scroll (double newPosition);

void Scroll (DateTime newPosition);

void ZoomReset ();

void ZoomReset (int numberOfViews);

 

시작과 끝 범위를 지정하거나 위치와 크기를 지정하는 방식으로 확대한다. saveState 인수를 true로 지정하면 각 확대 상태를 내부적으로 저장해 둔다. Scroll 메서드는 이동할 지점을 지정하되 double 타입과 DateTime을 둘 다 지원한다. Click 메서드의 앞쪽에 다음 코드를 작성해 보자.

 

chart1.ChartAreas[0].AxisX.ScaleView.Zoom(50, 100);

chart1.ChartAreas[0].AxisX.ScaleView.Scroll(70);

 

50 ~ 100까지를 표시할 크기로 확대하고 스크롤 위치는 70에 맞춘다. 차트를 클릭하면 즉시 확대하고 위치 이동까지 완료한다.

ZoomReset 메서드는 한 단계만 줌을 리셋한다. 여러 단계로 확대했다면 바로 직전 단계로 돌아가는 식이어서 확대한 횟수만큼 ZoomReset 메서드를 호출해야 한다. ZoomReset(0)는 모든 줌을 즉시 취소한다. 폼에 버튼을 하나 배치해 놓고 클릭 이벤트에서 Zoom 메서드를 호출해 보면 어떤 식으로 동작하는지 금방 알 수 있다.

차트를 확대하면 스크롤 바가 나타나며 사용자는 스크롤 바를 통해 원하는 곳으로 즉시 이동할 수 있다. 이 스크롤 바는 축의 ScrollBar 속성으로 관리하며 AxisScrollBar 클래스의 객체이다. 스크롤 바는 상태를 보여 주는 것이 주 임무여서 속성은 주로 스크롤 바의 모양을 정의할 뿐 동작을 제어하는 속성은 별로 없다.

 

속성

설명

Enabled

스크롤 바의 표시 여부를 지정한다. 디폴트는 true이며 확대하면 자동으로 나타난다.

Is​Visible

스크롤 바가 현재 보이는 상태인가를 조사하는 읽기 전용 속성이다.

Back​Color

스크롤 바의 배경 색상이다.

Button​Color

버튼의 색상이다.

Button​Style

어떤 버튼을 표시할 것인가를 지정한다. None은 버튼 없음, SmallScroll은 양쪽 버튼, ResetZoom은 리셋 줌 버튼이며 All은 모든 버튼을 다 표시한다.

Is​Positioned​Inside

스크롤 바를 에리어의 안에 둘 것인지 밖에 둘 것인지를 지정한다. 디폴트는 true여서 에리어 안쪽에 표시된다.

Line​Color

선의 색상이다.

Size

스크롤 바의 크기이다. 5~20까지 지정할 수 있다.

 

디폴트가 무난하게 되어 있지만 필요하다면 속성을 자유롭게 조정할 수 있다. 다음 코드를 작성하여 처음부터 확대해 놓고 Y축은 디폴트 속성대로, X축은 디폴트와 다른 속성을 주어 보자.

 

ax.ScaleView.Zoom(50, 100);

ay.ScaleView.Zoom(10, 50);

 

ax.ScrollBar.BackColor = Color.Red;

ax.ScrollBar.ButtonColor = Color.Green;

ax.ScrollBar.LineColor = Color.Blue;

ax.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;

ax.ScrollBar.IsPositionedInside = false;

ax.ScrollBar.Size = 20;

X축 스크롤바를 보면 어떤 색상이 어디에 적용되었는지 알 수 있다. 속성을 연구할 때는 이런 식으로 값을 지정해 보고 어디가 어떻게 바뀌는지 관찰해 보면 된다.

스트립 라인

스트립 라인은 배경에 줄무늬를 깔아 차트를 분명히 보이도록 한다. 축의 IsInterlaced 속성을 true로 설정하고 InterlacedColor에 줄무늬의 색상을 지정한다.

 

ax.IsInterlaced = true;

ax.InterlacedColor = Color.Yellow;

X축으로 노란색 줄무늬를 깔았다. 주그리드에 대해 한칸씩 건너 띄며 배경색을 칠해 줄무늬를 만든다. X축과 Y축으로 모두 스트립라인을 배치하면 세로줄이 더 위쪽에 그려진다.

IsInterlaced 속성은 사용하기 쉽지만 주기가 그리드에 의해 결정되기 때문에 자유도가 떨어진다. 또한 색상만 지정할 수 있을 뿐 무늬를 배치하거나 바깥에 경계선을 다른 색으로 그릴 수 없다.

더 다양한 스트립라인을 그리려면 축의 StripLines 속성으로 컬렉션을 만들어야 한다. 이 컬렉션은 StripLine의 집합이며 주기, 색상, 문자열까지 훨씬 더 다양한 모양의 스트립라인을 그릴 수 있다.