Windows 10 IoT Core에서 WinRT C#용 Chart


1. WinRTXamlToolkit.Controls.DataVisualization.UWP를 이용한 Chart.


차트를 드로잉하기 위해서 nuget에 있는 https://www.nuget.org/packages/WinRTXamlToolkit.Controls.DataVisualization.UWP/

Toolkit을 이용하는 방법이 있습니다.  

Window 10 iot core 용으로 다운 받을 경우 UWP 버젼으로 다운 받아야 한다. 정상적으로 받았을 경우 xaml 파일과 cs 소스 코드를 이용하여 개발을 수행합니다. 

        <WinRT_Charting:Chart x:Name="LineChart1" Title="My Chart">

            <WinRT_Charting:LineSeries Title="T1"

                        ItemsSource="{Binding Items}"

                        IndependentValueBinding="{Binding Name}"

                        DependentValueBinding="{Binding Value}"

                        IsSelectionEnabled="True"/>

            <WinRT_Charting:LineSeries Title="T2"

                        ItemsSource="{Binding Items}"

                        IndependentValueBinding="{Binding Name}"

                        DependentValueBinding="{Binding Value}"

                        IsSelectionEnabled="True"/>

        </WinRT_Charting:Chart>


private void updateChart_Click(object sender, RoutedEventArgs e)

        {

            try

            {

                List<NameValueItem> newitems1 = new List<NameValueItem>();

                List<NameValueItem> newitems2 = new List<NameValueItem>();

 

                int num = items1.Count / 250;

                int con = 0;

                int value1 = 0;

                int value2 = 0;

                string name;

                for (var i = 0; i < items1.Count; i++)

                {

                     name = Convert.ToString(newitems1.Count);

                     newitems1.Add(new NameValueItem { Name = name, Value = items1[i].Value });

                     newitems2.Add(new NameValueItem { Name = name, Value = items2[i].Value });


                }

                ((LineSeries)this.LineChart1.Series[0]).ItemsSource = newitems1;

                ((LineSeries)this.LineChart1.Series[1]).ItemsSource = newitems2;

 

                newitems1 = null;

                newitems2 = null;

            }

            catch (Exception ex)

            {

                status.Text = ex.Message;

            }

        } 

Window 10 IoT Core에서 센서값을 차트로 표현결과, 라즈베리 파이에서 그래픽 리소스를 많이 잡아먹는 WinRTXamlToolkit.Controls.DataVisualization를 이용할 경우에는 많은 양의 데이터를 표현은 어렵다는것을 확인하였습니다. 약 1000개의 데이터를 현시할 경우, 약 30초정도 소요됩니다. 아래 그림은 1000개의 데이터를 차트에 표현하여 보았습니다. 

실시간으로 데이터 표현이 어렵기 때문에, 데이터를 취득하는 버튼과 데이터 취득 종료버튼을 별도로 이용하여 데이터를 분석하는 모듈로 구성하였습니다. 

만약 실시간으로 차트를 표현하고자 한다면, Canvas를 이용하여 직접 Drawing을 수행하면 빠른 현시가 가능합니다. 


2. Canvas객체를 이용하 WinRT C# Chart 드로잉

private void DrawChart(Canvas canGraph, List<NameValueItem>[] pList, double axisx_min, double axisx_max, double data_min, double data_max)

        {

            canGraph.Children.Clear();


            double[] RectWall;

            RectWall = new double[4];

            RectWall[0] = 0;                // left 

            RectWall[1] = 0;                // top

            RectWall[2] = canGraph.ActualWidth;   // right

            RectWall[3] = canGraph.ActualHeight;  // bottom


            double[] RectGap;

            RectGap = new double[4];

            RectGap[0] = 30;    // left 

            RectGap[1] = 20;    // top

            RectGap[2] = 20;    // right

            RectGap[3] = 20;    // bottom


            double[] RectChart;

            RectChart = new double[4];

            RectChart[0] = RectWall[0] + RectGap[0]; // left 

            RectChart[1] = RectWall[1] + RectGap[1]; // top

            RectChart[2] = RectWall[2] - RectGap[2]; // right

            RectChart[3] = RectWall[3] - RectGap[3]; // bottom


            double fChartWidth = RectChart[2] - RectChart[0];

            double fChartHeight = RectChart[3] - RectChart[1];


            int xGridNum = 5;

            int yGridNum = 10;

            double fGridGapX = fChartWidth / xGridNum;

            double fGridGapY = fChartHeight / yGridNum;


            // 차트 그리기.

            Rectangle pChartRect = new Rectangle();

            pChartRect.StrokeThickness = 1;

            pChartRect.Fill = new SolidColorBrush(Colors.Black);

            pChartRect.Stroke = new SolidColorBrush(Colors.Yellow);

            pChartRect.Width = fChartWidth;

            pChartRect.Height = fChartHeight;

            Canvas.SetLeft(pChartRect, RectChart[0]);

            Canvas.SetTop(pChartRect, RectChart[1]);

            canGraph.Children.Add(pChartRect);


            // x축 그리기.

            int iCount = 0;

            GeometryGroup xaxis_geom = new GeometryGroup();

            for (double x = 0; x <= fChartWidth; x += fGridGapX)

            {

                LineGeometry xtick = new LineGeometry();

                xtick.StartPoint = new Point(x + RectChart[0], RectChart[3] + 5);

                xtick.EndPoint = new Point(x + RectChart[0], RectChart[1]);

                xaxis_geom.Children.Add(xtick);


                TextBlock xlabel = new TextBlock();

                int ivalue = (int)axisx_min + (int)(axisx_max - axisx_min) / xGridNum * iCount;

                xlabel.Text = ivalue.ToString();

                xlabel.FontSize = 10;

                Canvas.SetLeft(xlabel, x + RectChart[0]);

                Canvas.SetTop(xlabel, RectChart[3] + 5);

                canGraph.Children.Add(xlabel);

                iCount = iCount + 1;

            }


            Path xaxis_path = new Path();

            xaxis_path.StrokeThickness = 1;

            xaxis_path.Stroke = new SolidColorBrush(Colors.Green);

            xaxis_path.Data = xaxis_geom;

            canGraph.Children.Add(xaxis_path);


            // y축 그리기.

            iCount = 0;

            GeometryGroup yxaxis_geom = new GeometryGroup();

            for (double y = 0; y <= fChartHeight; y += fGridGapY)

            {

                LineGeometry ytick = new LineGeometry();

                ytick.StartPoint = new Point(RectChart[0] - 5, RectChart[3] - y);

                ytick.EndPoint = new Point(RectChart[2], RectChart[3] - y);

                yxaxis_geom.Children.Add(ytick);


                TextBlock ylabel = new TextBlock();

                int ivalue = (int)data_min + (int)(data_max - data_min) / yGridNum * iCount;

                ylabel.Text = ivalue.ToString();

                ylabel.FontSize = 10;

                Canvas.SetLeft(ylabel, RectChart[0] - 20);

                Canvas.SetTop(ylabel, RectChart[3] - y - ylabel.FontSize);

                canGraph.Children.Add(ylabel);

                iCount = iCount + 1;

            }


            Path yaxis_path = new Path();

            yaxis_path.StrokeThickness = 1;

            yaxis_path.Stroke = new SolidColorBrush(Colors.Green);

            yaxis_path.Data = yxaxis_geom;

            canGraph.Children.Add(yaxis_path);


            // data 그리기.

            double x1 = 0;

            double y1 = 0;

            double x2 = 0;

            double y2 = 0;

            

            int idim = 0;

            idim = pList.Length;

            if (idim > 0)

            {

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

                {

                    GeometryGroup data_geom = new GeometryGroup();


                    List<NameValueItem> newitems = pList[i];

                    double xstep = fChartWidth / newitems.Count;


                    for (int j = 0; j < newitems.Count-1; j++)

                    {

                        LineGeometry vline = new LineGeometry();


                        x1 = RectChart[0] + xstep * j;

                        y1 = RectChart[3] - fChartHeight * ((newitems[j].Value- data_min) / (data_max - data_min));

                        if (y1 < RectChart[1])

                            y1 = RectChart[1];

                        if (y1 > RectChart[3])

                            y1 = RectChart[3];

                        x2 = RectChart[0] + xstep * (j+1);

                        y2 = RectChart[3] - fChartHeight * ((newitems[j+1].Value - data_min) / (data_max - data_min));

                        if (y2 < RectChart[1])

                            y2 = RectChart[1];

                        if (y2 > RectChart[3])

                            y2 = RectChart[3];

                        vline.StartPoint = new Point(x1, y1);

                        vline.EndPoint = new Point(x2, y2);

                        data_geom.Children.Add(vline);

                    }


                    Path value_path = new Path();

                    value_path.StrokeThickness = 1;

                    if (i == 0)  

                        value_path.Stroke = new SolidColorBrush(Colors.Red);

                    else if (i == 1)

                        value_path.Stroke = new SolidColorBrush(Colors.Green);

                    else if (i == 2)

                        value_path.Stroke = new SolidColorBrush(Colors.Blue);

                    else

                        value_path.Stroke = new SolidColorBrush(Colors.Yellow);

                    value_path.Data = data_geom;

                    canGraph.Children.Add(value_path);

                }

            }

        }

위와 같이 직접 Canvas에 직접 차트를 그릴경우에는 약 3000개의 데이터를 현시하는데 있어 2~3초정도 소요됨을 확인하였습니다. 

차트를 이용하여 대용량을 현시하고자 할 경우에는 Canvas에 직접 그리는 것을 권고합니다.

Canvas를 이용하여 WinRT용 C# 차트 응용프로그램 실행 화면.



+ Recent posts