WPF 2D Chart OxyPlot 사용법 – 꺾은선 그래프 (LineSeries)

이 Post 는 WPF, C# 에서 2D Chart 를 그릴 때 자주 사용되는 OxyPlot 의 기초적인 사용법을 설명 합니다.
랜덤 값으로 꺾은선 그래프를 그리고 Zoom 과 Pan 을 막거나 초기화 하는 방법 또한 설명합니다.

0. 실행 화면

OxyPlot 는 WPF 나 C# 에서 2D 차트를 그릴때 사용할 수 있는 유용한 차트 라이브러리 입니다.
무료에 오픈소스이며 다양한 차트를 지원합니다.
Nuget 에서 바로 설치 할 수 있으며 레퍼런스 또한 많은 편입니다.

다만 문서나 설명이 좀 부족한게 흠입니다.

사실 기본적인 사용법만 알아두면 나머지는 다 비슷합니다.
이번 Post 에서는 LineSeries 를 통해 기본적인 사용법을 알아봅니다.

1. 프로젝트에서 Nuget 패키지 관리자를 실행

프로젝트에서 Mouse 우클릭 하여 Nuget 패키지 관리 메뉴를 선택 합니다.

2. OxyPlot.Wpf 설치

Nuget 패키지 관리자에서 oxyplot 로 검색하고, 검색 결과에서 OxyPlot.Wpf 를 설치 합니다.

3. MainWindow.xaml

<Window x:Class="BlogDemo01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BlogDemo01"
		xmlns:oxy="http://oxyplot.org/wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
	<Grid>
		<Grid.ColumnDefinitions>
			<ColumnDefinition/>
			<ColumnDefinition Width="120"/>
		</Grid.ColumnDefinitions>
		<oxy:PlotView x:Name="plotView"/>
		<StackPanel Grid.Column="1">
			<Button x:Name="btnTest00" Content="Test 00" Margin="10" Height="24"/>
		</StackPanel>
	</Grid>
</Window>

xmlns:local 아래 oxy 네임스페이스를 추가 합니다.
화면에 oxy:PlotView 를 추가 합니다.

4. MainWindow.xaml.cs – 생성자

Random random = new Random(Guid.NewGuid().GetHashCode() + DateTime.Now.Millisecond);

public MainWindow()
{
	InitializeComponent();
	this.Loaded += MainWindow_Loaded;
}

랜덤 데이터를 만들기 위해 Random 클래스를 초기화 합니다.
Loaded 이벤트에서 그래프를 그려주기 때문에 MainWindow 가 표시되었을 때 그래프는 바로 보이게 됩니다.

그래프를 그리는 방법만 알고 싶으시다면 주석 처리된 부분은 무시하시면 됩니다.

5. 그래프 그리기

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
	plotView.Model = TwoLineSeries();
}

public PlotModel TwoLineSeries()
{
	PlotModel plotModel = new PlotModel();
	plotModel.Title = "Two LineSeries";

	var xAxis = new LinearAxis();
	xAxis.Position = AxisPosition.Bottom;
	xAxis.Title = "X Axis";
	// if you want to disable zooming and panning, uncomment the following lines
	//linearAxis1.IsZoomEnabled = false;
	//linearAxis1.IsPanEnabled = false;
	plotModel.Axes.Add(xAxis);

	var yAxis = new LinearAxis();
	//linearAxis2.Position = AxisPosition.Left;
	//linearAxis2.Position = AxisPosition.Right;
	yAxis.Title = "Y Axis";
	// if you want to disable zooming and panning, uncomment the following lines
	//linearAxis2.IsZoomEnabled = false;
	//linearAxis2.IsPanEnabled = false;
	plotModel.Axes.Add(yAxis);

	var lineSeries1 = new LineSeries();
	lineSeries1.Title = "LineSeries 1";
	lineSeries1.Color = OxyColors.Red;

	var lineSeries2 = new LineSeries();
	lineSeries2.Title = "LineSeries 2";
	lineSeries2.Color = OxyColors.Blue;

	// Generate random data points for the line series
	for (int i = 0; i <= 100; i += 10)
	{
		double y1 = random.Next(0, 30);
		double y2 = random.Next(-5, 20);
		lineSeries1.Points.Add(new DataPoint(i, y1));
		lineSeries2.Points.Add(new DataPoint(i, y2));
	}

	plotModel.Series.Add(lineSeries1);
	plotModel.Series.Add(lineSeries2);

	return plotModel;
}

선이 두 개 있는 꺾은 선 그래프를 그립니다.
값은 랜덤으로 생성 합니다.
첫 번째 Axis 에서 Position 을 정해주면 두 번째 Axis 는 나머지 가로나 세로로 들어가게 됩니다.
Zoom(확대, 축소) 이나 Pan(이동) 을 막고 싶다면 주석 된 부분을 해제하면 됩니다.
단순히 그래프 구현 부분은 여기까지 보시면 됩니다.
이 다음은 여담 입니다.

5. 그외 그래프 줌, 이동 초기화 관련 내용

사실 하다 보니 확대 축소 혹은 이동 후 초기화 기능이 없어 몇 가지 더 구현한 내용이 있습니다.
전체 소스를 보겠습니다.

public partial class MainWindow : Window
{
	Random random = new Random(Guid.NewGuid().GetHashCode() + DateTime.Now.Millisecond);

	double xMin, xMax, yMin, yMax = 0;

	public MainWindow()
	{
		InitializeComponent();

		plotView.MouseMove += PlotView_MouseMove;
		plotView.KeyDown += PlotView_KeyDown;

		btnTest00.Click += BtnTest00_Click;

		this.Loaded += MainWindow_Loaded;
	}

	private void PlotView_MouseMove(object sender, MouseEventArgs e)
	{
		if (!plotView.IsFocused)
		{
			plotView.Focus();
		}
	}

	private void PlotView_KeyDown(object sender, KeyEventArgs e)
	{
		if (e.Key == Key.Escape)
		{
			foreach(Axis axis in plotView.Model.Axes)
			{
				axis.Reset();
			}
			
			plotView.Model.InvalidatePlot(true);
		}
	}

	private void CheckFirstPosition()
	{
		if(plotView.Model == null || plotView.Model.Axes.Count == 0)
		{
			return;
		}

		foreach(Axis axis in plotView.Model.Axes)
		{
			if(axis.Position == AxisPosition.Bottom)
			{
				xMin = axis.ActualMinimum;
				xMax = axis.ActualMaximum;
			}
			else
			{
				yMin = axis.ActualMinimum;
				yMax = axis.ActualMaximum;
			}
		}
	}

	private void MainWindow_Loaded(object sender, RoutedEventArgs e)
	{
		BtnTest00_Click(sender, e);
	}

	private void BtnTest00_Click(object sender, RoutedEventArgs e)
	{
		plotView.Model = TwoLineSeries();
		CheckFirstPosition();
	}

	public PlotModel TwoLineSeries()
	{
		PlotModel plotModel = new PlotModel();
		plotModel.Title = "Two LineSeries";

		var xAxis = new LinearAxis();
		xAxis.Position = AxisPosition.Bottom;
		xAxis.Title = "X Axis";
		// if you want to disable zooming and panning, uncomment the following lines
		//linearAxis1.IsZoomEnabled = false;
		//linearAxis1.IsPanEnabled = false;
		plotModel.Axes.Add(xAxis);

		var yAxis = new LinearAxis();
		//linearAxis2.Position = AxisPosition.Left;
		//linearAxis2.Position = AxisPosition.Right;
		yAxis.Title = "Y Axis";
		// if you want to disable zooming and panning, uncomment the following lines
		//linearAxis2.IsZoomEnabled = false;
		//linearAxis2.IsPanEnabled = false;
		plotModel.Axes.Add(yAxis);

		var lineSeries1 = new LineSeries();
		lineSeries1.Title = "LineSeries 1";
		lineSeries1.Color = OxyColors.Red;

		var lineSeries2 = new LineSeries();
		lineSeries2.Title = "LineSeries 2";
		lineSeries2.Color = OxyColors.Blue;

		// Generate random data points for the line series
		for (int i = 0; i <= 100; i += 10)
		{
			double y1 = random.Next(0, 30);
			double y2 = random.Next(-5, 20);
			lineSeries1.Points.Add(new DataPoint(i, y1));
			lineSeries2.Points.Add(new DataPoint(i, y2));
		}

		plotModel.Series.Add(lineSeries1);
		plotModel.Series.Add(lineSeries2);

		return plotModel;
	}
}

xMin, xMax, yMin, yMax 는 그래프를 Zoom(확대, 축소), 혹은 Pan(이동) 했을 때 초기 상태로 되돌리기 위한 값을 저장합니다.
MainWindow 가 표시되면 그래프가 표시 됩니다.
이때 마우스 휠을 사용하여 확대 축소도 되고 마우스 우클릭 버튼을 눌러 이동도 됩니다.
다만, 마우스 휠을 사용해 확대 축소를 한 후에 Esc 버튼을 눌러 줌 상태를 초기화 하려면 동작하지 않을 수 있습니다.
왜냐면, PlotView 가 선택되지 않은 상태이기 때문에 Focus 가 없어 KeyDown 이벤트를 받을 수 없기 때문입니다.
이 때문에 MouseMove 이벤트에서는 그래프에서 Mouse 가 움직였을 경우 그래프에 Focus 를 줍니다.
또한 KeyDown 이벤트 에서는 Esc 키를 감지하여 Zoom 과 Pan 을 초기화 합니다.

6. OxyPlot 예제 프로젝트

여기까지 PxyPlot 를 사용하여 기본적인 그래프를 구현하는 방법을 확인 했습니다.
OxyPlot 에는 여러가지 그래프가 있지만 사용 방법은 다 비슷합니다.
OxyPlot 의 예제 프로젝트를 실행해 보시면 쉽게 확인 하실 수 있을 것 같습니다.
다음은 예제 프로젝트 실행 화면 입니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤