Home > Net >  Draw Graph Diagram with WPF in C# Properly
Draw Graph Diagram with WPF in C# Properly

Time:12-18

How can this code produce a similar Graph Diagram as the other one? I have posted my entire code, if someone is an expert on this, share your input. I don't understand drawing fully in WPF! I am still reading on the topic.

My Code:

private void GenerateGraphBtn_Click(object sender, RoutedEventArgs e)
{
    // Setting up the bounds of the graph
    const double margin = 30;
    double xmin = margin;
    double xmax = (DrawGraphArea.Width / 2);
    double ymin = margin;
    double ymax = (DrawGraphArea.Height / 2);
    const double step = 12;

    // ##########################################################
    // Make the X axis.
    GeometryGroup xaxis_geom = new GeometryGroup();

    // Creates the long Horisontal Line
    xaxis_geom.Children.Add(new LineGeometry(
        new Point(0, ymax), new Point(DrawGraphArea.Width / 2, ymax)));

    // Adds all the mini lines on the horisontal axis (bottom)
    for (double x = xmin   step;
        x <= (DrawGraphArea.Width / 2) - step; x  = step)
    {
        xaxis_geom.Children.Add(new LineGeometry(
            new Point(x, ymax - margin / 2),
            new Point(x, ymax   margin / 2)));
    }

    /* Adds all the lines that were created in the above code to the graph.
     * Stroke thickness = line thickness
     * Stroke = line colour
     * Data = the geometry you are adding (the points that you have created in the above code)
     */
    Path xaxis_path = new Path();
    xaxis_path.StrokeThickness = 2;
    xaxis_path.Stroke = Brushes.DarkRed;
    xaxis_path.Data = xaxis_geom;
    DrawGraphArea.Children.Add(xaxis_path);


    // ##########################################################
    // Creates the Y axis.
    GeometryGroup yaxis_geom = new GeometryGroup();

    // Adds the long Vertical Line
    yaxis_geom.Children.Add(new LineGeometry(
        new Point(xmin, 0), new Point(xmin, DrawGraphArea.Height / 2)));

    // Adds the mini lines on the vertical axis (Left)
    for (double y = step; y <= (DrawGraphArea.Height / 2) - step; y  = step)
    {
        yaxis_geom.Children.Add(new LineGeometry(
            new Point(xmin - margin / 2, y),
            new Point(xmin   margin / 2, y)));
    }

    // Adds them all to the graph.
    Path yaxis_path = new Path();
    yaxis_path.StrokeThickness = 1;
    yaxis_path.Stroke = Brushes.Black;
    yaxis_path.Data = yaxis_geom;
    DrawGraphArea.Children.Add(yaxis_path);


    // ##########################################################
    // This creates the brushes colours
    Random rand = new Random();

    // This will then go along and create all the colour
    for (int data_set = 0; data_set < 1; data_set  )
    {
        int last_y = rand.Next((int)ymin, (int)ymax);

        // This is where you add the points to the graph
        // Little bit confusing as it is adding 3 lines at once
        PointCollection points = new PointCollection();
        for (double x = xmin; x <= xmax; x  = step)
        {
            last_y = rand.Next(last_y - 10, last_y   10);
            if (last_y < ymin) last_y = (int)ymin;
            if (last_y > ymax) last_y = (int)ymax;
            points.Add(new Point(x, last_y));
        }

        // Adds the lines that connect the points
        Polyline polyline = new Polyline();
        polyline.StrokeThickness = 2;
        polyline.Stroke = Brushes.Red;
        polyline.Points = points;

        // Add the line / points
        DrawGraphArea.Children.Add(polyline);
    }
}

Current result:

enter image description here

Desired result:

enter image description here

CodePudding user response:

You were on the right path to resolve it, I used your code as guideline.

enter image description here

So, 1st here is the code that provide your desired result,

This goes to the MainWindow xaml:

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="20" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="Button" HorizontalAlignment="Left" Click="GenerateGraphBtn_Click" />
        <Canvas Grid.Row="2" x:Name="GrpahArea" Margin="40" />
    </Grid>
</Window>

And this goes into the code-behind of the MainWindow

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private Point[] GenerateGraphPoints(int minimumX, int maximumX, int minimumY, int maximumY)
        {
            var result = new List<Point>();
            var rnd = new Random();
            for (var x = minimumX; x < maximumX; x  )
                result.Add(new Point(x, rnd.Next(minimumY, maximumY)));
            return result.ToArray();
        }

        private void PlotXAxes(Panel drawArea, int minimumX, int maximumX, int step, int miniLineExtent)
        {
            var geometryGroup = new GeometryGroup();
            var axePositionY = drawArea.ActualHeight;
            geometryGroup.Children.Add(new LineGeometry(new Point(0, axePositionY), new Point(drawArea.ActualWidth, axePositionY)));
            var factorX = drawArea.ActualWidth / (maximumX - minimumX);
            for (var i = minimumX; i < maximumX; i  )
                if (i % step == 0)
                {
                    geometryGroup.Children.Add(new LineGeometry(
                        new Point(i * factorX, axePositionY - miniLineExtent),
                        new Point(i * factorX, axePositionY   miniLineExtent)));
                    drawArea.Children.Add(new Label
                    {
                        Margin = new Thickness(i*factorX, axePositionY   miniLineExtent, 0, 0),
                        Content = i,
                        Foreground = Brushes.Black
                    });
                }
            var path = new Path
            {
                StrokeThickness = 2,
                Stroke = Brushes.DarkRed,
                Data = geometryGroup
            };
            drawArea.Children.Add(path);
        }

        private void PlotYAxes(Panel drawArea, int minimumY, int maximumY, int step, int miniLineExtent)
        {
            var labelSize = new Size(30, 26);
            var geometryGroup = new GeometryGroup();
            var axePositionX = 0;
            geometryGroup.Children.Add(new LineGeometry(new Point(axePositionX, 0), new Point(axePositionX, drawArea.ActualHeight)));
            var factorY = drawArea.ActualHeight / (maximumY - minimumY);
            for (var i = minimumY; i < maximumY; i  )
                if (i % step == 0)
                {
                    geometryGroup.Children.Add(new LineGeometry(
                        new Point(axePositionX - miniLineExtent, (maximumY-i) * factorY),
                        new Point(axePositionX   miniLineExtent, (maximumY-i) * factorY)));
                    drawArea.Children.Add(new Label
                    {
                        Width = labelSize.Width,
                        Height = labelSize.Height,
                        Margin = new Thickness(
                            axePositionX - miniLineExtent - labelSize.Width,
                            (maximumY - i) * factorY - (labelSize.Height / 2), 0, 0),
                        Content = i,
                        Foreground = Brushes.Black
                    });
                }
            var path = new Path
            {
                StrokeThickness = 2,
                Stroke = Brushes.DarkRed,
                Data = geometryGroup
            };
            drawArea.Children.Add(path);
        }

        private void PlotGraph(Point[] points, Panel drawArea, int minimumX, int maximumX, int minimumY, int maximumY)
        {
            var factorX = drawArea.ActualWidth / (maximumX-minimumX);
            var factorY = drawArea.ActualHeight / (maximumY-minimumY);
            for (var i = 0; i < points.Length; i  )
            {
                points[i].X *= factorX;
                //points[i].Y *= factorY;
                points[i].Y = (maximumY - points[i].Y) * factorY; //since zero of Y should be in bottom we "swap" the Y value.
            }
            Polyline polyline = new Polyline();
            polyline.StrokeThickness = 2;
            polyline.Stroke = Brushes.Blue;
            polyline.Points = new PointCollection(points);
            drawArea.Children.Add(polyline);
        }

        private void GenerateGraphBtn_Click(object sender, RoutedEventArgs e)
        {
            //Step 1: define the data limits and generate the data, this has nothing to do with the view.
            const int xmin = 0, xmax = 12;
            const int ymin = 0, ymax = 800;
            var graphPoints = GenerateGraphPoints(xmin, xmax, ymin, ymax);

            //Step 2: Draw yAxes
            PlotYAxes(GrpahArea, ymin, ymax, 100, 2);

            //Step 3: Draw xAxes
            PlotXAxes(GrpahArea, xmin, xmax, 1, 2);

            //Step 4: Plot Grpah
            var plotPoints = new Point[graphPoints.Length]; //graphPoints is our model, we usually wish to keep the original value of model.
            graphPoints.CopyTo(plotPoints,0); //plotPoints is what we going to scale and adjust to the view.
            PlotGraph(plotPoints, GrpahArea, xmin, xmax, ymin, ymax);
        }
    }
}

Now to the 2nd part of my answer, Building a graph component is not that simple task, mostly since graphs are usually developed as generic to many applications, so the amount of allowed customization need to be enormous, this is why the others rightfully directed you to a ready library.

Also, the code I provided you in the answer is not MVVM style, it is very near to the code you provided. This is since writing that in MVVM-style will require creating a set of view model classes and bind them to the view, considering the customizations, it is quite an effort compared to what you asked to resolve.

Nevertheless I hope the code provided can help you do whatever you wanted.

Update, adding labels:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private Point[] GenerateGraphPoints(int minimumX, int maximumX, int minimumY, int maximumY)
        {
            var result = new List<Point>();
            var rnd = new Random();
            for (var x = minimumX; x < maximumX; x  )
                result.Add(new Point(x, rnd.Next(minimumY, maximumY)));
            return result.ToArray();
        }

        private void PlotXAxes(Panel drawArea, int minimumX, int maximumX, int step, int miniLineExtent, string title)
        {
            var geometryGroup = new GeometryGroup();
            var axePositionY = drawArea.ActualHeight;
            geometryGroup.Children.Add(new LineGeometry(new Point(0, axePositionY), new Point(drawArea.ActualWidth, axePositionY)));
            var factorX = drawArea.ActualWidth / (maximumX - minimumX);
            for (var i = minimumX; i < maximumX; i  )
                if (i % step == 0)
                {
                    geometryGroup.Children.Add(new LineGeometry(
                        new Point(i * factorX, axePositionY - miniLineExtent),
                        new Point(i * factorX, axePositionY   miniLineExtent)));
                    drawArea.Children.Add(new Label
                    {
                        Margin = new Thickness(i*factorX, axePositionY   miniLineExtent, 0, 0),
                        Content = i,
                        Foreground = Brushes.Black
                    });
                }
            var path = new Path
            {
                StrokeThickness = 2,
                Stroke = Brushes.DarkRed,
                Data = geometryGroup
            };
            drawArea.Children.Add(path);
            var titleLabel = new Label
            {
                Margin = new Thickness(drawArea.ActualWidth - 50, drawArea.ActualHeight - 20, 0, 0),
                Content = title
            };
            drawArea.Children.Add(titleLabel);
        }

        private void PlotYAxes(Panel drawArea, int minimumY, int maximumY, int step, int miniLineExtent, string title)
        {
            var labelSize = new Size(30, 26);
            var geometryGroup = new GeometryGroup();
            var axePositionX = 0;
            geometryGroup.Children.Add(new LineGeometry(new Point(axePositionX, 0), new Point(axePositionX, drawArea.ActualHeight)));
            var factorY = drawArea.ActualHeight / (maximumY - minimumY);
            for (var i = minimumY; i < maximumY; i  )
                if (i % step == 0)
                {
                    geometryGroup.Children.Add(new LineGeometry(
                        new Point(axePositionX - miniLineExtent, (maximumY-i) * factorY),
                        new Point(axePositionX   miniLineExtent, (maximumY-i) * factorY)));
                    drawArea.Children.Add(new Label
                    {
                        Width = labelSize.Width,
                        Height = labelSize.Height,
                        Margin = new Thickness(
                            axePositionX - miniLineExtent - labelSize.Width,
                            (maximumY - i) * factorY - (labelSize.Height / 2), 0, 0),
                        Content = i,
                        Foreground = Brushes.Black
                    });
                }
            var path = new Path
            {
                StrokeThickness = 2,
                Stroke = Brushes.DarkRed,
                Data = geometryGroup
            };
            drawArea.Children.Add(path);
            var titleLabel = new Label
            {
                Margin = new Thickness(0, 0, 0, 0),
                Content = title
            };
            drawArea.Children.Add(titleLabel);
        }

        private void PlotGraph(Point[] points, Panel drawArea, int minimumX, int maximumX, int minimumY, int maximumY)
        {
            var factorX = drawArea.ActualWidth / (maximumX-minimumX);
            var factorY = drawArea.ActualHeight / (maximumY-minimumY);
            for (var i = 0; i < points.Length; i  )
            {
                points[i].X *= factorX;
                //points[i].Y *= factorY;
                points[i].Y = (maximumY - points[i].Y) * factorY; //since zero of Y should be in bottom we "swap" the Y value.
            }
            Polyline polyline = new Polyline();
            polyline.StrokeThickness = 2;
            polyline.Stroke = Brushes.Blue;
            polyline.Points = new PointCollection(points);
            drawArea.Children.Add(polyline);
        }

        private void GenerateGraphBtn_Click(object sender, RoutedEventArgs e)
        {
            //Step 1: define the data limits and generate the data, this has nothing to do with the view.
            const int xmin = 0, xmax = 12;
            const int ymin = 0, ymax = 800;
            var graphPoints = GenerateGraphPoints(xmin, xmax, ymin, ymax);

            GrpahArea.Children.Add(new Label
            {
                Content = "Profits over months",
                Margin = new Thickness(GrpahArea.ActualWidth / 2 - 100, -50, 0, 0)
            });

            //Step 2: Draw yAxes
            PlotYAxes(GrpahArea, ymin, ymax, 100, 2, "Profits");

            //Step 3: Draw xAxes
            PlotXAxes(GrpahArea, xmin, xmax, 1, 2, "Months");

            //Step 4: Plot Grpah
            var plotPoints = new Point[graphPoints.Length]; //graphPoints is our model, we usually wish to keep the original value of model.
            graphPoints.CopyTo(plotPoints,0); //plotPoints is what we going to scale and adjust to the view.
            PlotGraph(plotPoints, GrpahArea, xmin, xmax, ymin, ymax);
        }
    }
}
  • Related