Ứng dụng vẽ đồ thị đơn giản.

Chúng ta sẽ thực hiện vẽ đồ thị như sau (gồm sin và cos):

Trước tiên các bạn tạo một project WPF, sau đó các bạn mở file XAML lên và gõ vào đoạn mã sau:


<Window x:Class="FirstWPFApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Simple line" Height="300" Width="300">
    <Viewbox Stretch="Fill">
        <Border BorderBrush="Black" BorderThickness="1" Margin="5">
            <Canvas Name="chartCanvas" Width="250" Height="200" ClipToBounds="True"/>
        </Border>
    </Viewbox>
</Window>


Mã XAML này dùng để tạo ra chart layout để bạn có thể tùy biến trong đó.
Xong, các bạn mở file .xaml.cs lên để thực hiện với các câu lệnh C# và gõ vào đoạn mã sau:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace FirstWPFApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private double xmin = 0;
        private double xmax = 6.5;
        private double ymin = -1.1;
        private double ymax = 1.1;
        private Polyline pl;

        public MainWindow()
        {
            InitializeComponent();
            AddChart();
        }

        private void AddChart()
        {
            pl = new Polyline();
            pl.Stroke = Brushes.Black;
            int i;
            for (i = 0; i < 70; i++)
            {
                double x = i / 5.0;
                double y = Math.Sin(x);
                pl.Points.Add(NormalizePoint(new Point (x, y)));
            }
            chartCanvas.Children.Add(pl);

            pl = new Polyline();
            pl.Stroke = Brushes.Black;
            pl.StrokeDashArray = new DoubleCollection(new double[] { 4, 3 });

            for (i = 0; i < 70; i++)
            {
                double x = i / 5.0;
                double y = Math.Cos(x);
                pl.Points.Add(NormalizePoint(new Point(x, y)));
            }
            chartCanvas.Children.Add(pl);
        }

        private Point NormalizePoint(Point pt)
        {
            Point result = new Point();
            result.X = (pt.X – xmin) * chartCanvas.Width / (xmax – xmin);
            result.Y = chartCanvas.Height – (pt.Y – ymin) * chartCanvas.Height / (ymax – ymin);
            return result;
        }
    }
}


Xong, các bạn bấm F5 để xem kết quả. Các bạn có thể vẽ thêm các hàm khác như tan,… và tùy biến màu sắc cho nó sinh động hơn.
Để chương trình mang tính hướng đối tượng hơn (object oriented) và dễ dàng mở rộng các tính năng mới, ta cần định nghĩa thêm 3 lớp mới: ChartStyle, DataColletion và DataSeries. Lớp ChartStyle xác định tất cả các chart layout thông tin liên quan. Lớp DataCollection giữ các đối tượng DataSeries với mỗi đối tượng DataSeries đại diện một đường cong trên chart. Lớp DataSeries giữ dữ liệu chart và các kiểu đường bao gồm màu của đường, độ dày,…

1. ChartStyle:


public class ChartStyle
    {
        private double xmin = 0;
        private double xmax = 1;
        private double ymin = 0;
        private double ymax = 1;

        private Canvas chartCanvas;

        public ChartStyle()
        {
        }

        public ChartStyle(Canvas chartCanvas)
        {
            // TODO: Complete member initialization
            this.chartCanvas = chartCanvas;
        }

        public Canvas ChartCanvas
        {
            get { return chartCanvas; }
            set { chartCanvas = value; }
        }

        public double Xmin
        {
            get { return xmin; }
            set { xmin = value; }
        }

        public double Xmax
        {
            get { return xmax; }
            set { xmax = value; }
        }

        public double Ymin
        {
            get { return ymin; }
            set { ymin = value; }
        }

        public double Ymax
        {
            get { return ymax; }
            set { ymax = value; }
        }

        public void ResizeCanvas(double width, double height)
        {
            ChartCanvas.Width = width;
            ChartCanvas.Height = height;
        }

        public Point NormalizePoint(Point pt)
        {
            if (ChartCanvas.Width.ToString() == "NaN")
                ChartCanvas.Width = 270;
            if (ChartCanvas.Height.ToString() == "NaN")
                ChartCanvas.Height = 250;
            Point result = new Point();
            result.X = (pt.X – Xmin) * ChartCanvas.Width / (Xmax – Xmin);
            result.Y = ChartCanvas.Height – (pt.Y – Ymin) * ChartCanvas.Height / (Ymax – Ymin);
            return result;
        }
    }


Trong lớp này, ta dùng điều khiển Canvas vì nó cần được xác định khu vực chart và lưu giữ tọa độ điểm trong thế giới thực. Phương thức ResizeCanvas cho phép điều chỉnh kích cỡ của canvas. Phương thức NormalizePoint làm cho đoạn mã rõ ràng và dễ đọc hơn.

2. DataCollection:


public class DataCollection
    {
        private List<DataSeries> dataList;

        public DataCollection()
        {
            dataList = new List<DataSeries>();
        }

        public List<DataSeries> DataList
        {
            get { return dataList; }
            set { dataList = value; }
        }

        public void AddLines(Canvas canvas, ChartStyle cs)
        {
            int j = 0;
            foreach (DataSeries ds in DataList)
            {
                if (ds.SeriesName == "Default Name")
                {
                    ds.SeriesName = "DataSeries" + j.ToString();
                }
                ds.AddLinePattern();
                int i;
                for (i = 0; i < ds.LineSeries.Points.Count; i++)
                {
                    ds.LineSeries.Points[i] = cs.NormalizePoint(ds.LineSeries.Points[i]);
                }
                canvas.Children.Add(ds.LineSeries);
                j++;
            }
        }
    }


Lớp này giữ các đối tượng DataSeries. Thuộc tính DataList giữ DataSeries, sau đó thực thi phương thức AddLines vẽ các đường sử dụng các đối tượng DataSeries trong lớp DataCollection. Với mỗi DataSeries, ta thêm một đường vào chart canvas bằng cách dùng line style đã được định nghĩa cho DataSeries đó.

3. DataSeries:


public class DataSeries
    {
        private Polyline lineSeries = new Polyline();
        private Brush lineColor;
        private double lineThickness = 1;
        private LinePatternEnum linePattern;
        private string seriesName = "Default Name";

        public DataSeries()
        {
            LineColor = Brushes.Black;
        }

        public Brush LineColor
        {
            get { return lineColor; }
            set { lineColor = value; }
        }

        public Polyline LineSeries
        {
            get { return lineSeries; }
            set { lineSeries = value; }
        }

        public double LineThickness
        {
            get { return lineThickness; }
            set { lineThickness = value; }
        }

        public LinePatternEnum LinePattern
        {
            get { return linePattern; }
            set { linePattern = value; }
        }

        public string SeriesName
        {
            get { return seriesName; }
            set { seriesName = value; }
        }

        public void AddLinePattern()
        {
            LineSeries.Stroke = LineColor;
            LineSeries.StrokeThickness = LineThickness;

            switch (LinePattern)
            {
                case LinePatternEnum.Dash:
                    LineSeries.StrokeDashArray = new DoubleCollection(new double[2] { 4, 3 });
                    break;
                case LinePatternEnum.Dot:
                    LineSeries.StrokeDashArray = new DoubleCollection(new double[2] { 1, 2 });
                    break;
                case LinePatternEnum.DashDot:
                    LineSeries.StrokeDashArray = new DoubleCollection(new double[4] { 4, 2, 1, 2 });
                    break;
                case LinePatternEnum.None:
                    LineSeries.Stroke = Brushes.Transparent;
                    break;
            }
        }

        public enum LinePatternEnum
        {
            Solid = 1,
            Dash = 2,
            Dot = 3,
            DashDot = 4,
            None = 5
        }
    }


Lớp này định nghĩa đối tượng Polyline cho một DataSeries được cho.

Trong file .xaml.cs, ta chỉnh lại như sau:


public partial class MainWindow : Window
    {
        private ChartStyle cs;
        private DataCollection dc = new DataCollection();
        private DataSeries ds = new DataSeries();

        public MainWindow()
        {
            InitializeComponent();
            AddChart();
        }

        private void AddChart()
        {
            cs = new ChartStyle(chartCanvas);
            cs.Xmin = 0;
            cs.Xmax = 7;
            cs.Ymin = -1.1;
            cs.Ymax = 1.1;

            ds = new DataSeries();
            ds.LineColor = Brushes.Blue;
            ds.LineThickness = 3;
            int i;
            for (i = 0; i < 70; i++)
            {
                double x = i / 5.0;
                double y = Math.Sin(x);
                ds.LineSeries.Points.Add(new Point (x, y));
            }
            dc.DataList.Add(ds);

            ds = new DataSeries();
            ds.LineColor = Brushes.Red;
            ds.LinePattern = DataSeries.LinePatternEnum.DashDot;
            ds.LineThickness = 3;

            for (i = 0; i < 70; i++)
            {
                double x = i / 5.0;
                double y = Math.Cos(x);
                ds.LineSeries.Points.Add(new Point(x, y));
            }
            dc.DataList.Add(ds);
            dc.AddLines(chartCanvas, cs);
        }
    }


Xong, bạn nhấm phím F5 để build và xem kết quả:

Các bạn dễ dàng nhận ra những điểm mới so với ví dụ ban đầu. Các bạn có thể điều tinh chỉnh hoặc thêm các thuộc tính mới, vẽ thêm hàm tan,… Bài kế tiếp tôi sẽ hướng dẫn các bạn vẽ đồ thị đa đường và hiệu chỉnh cho nó được đẹp hơn. Chúc các bạn thành công.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s