LiveCharts中的运行时多轴

时间:2019-04-08 18:05:14

标签: c# wpf livecharts multiple-axes

我正在尝试编写WPF应用程序以绘制具有一系列数字的折线图。这些数字列在.CSV文件中,我将在运行时阅读这些文件。因此,我不知道我将要拥有的序列数,也不知道每个序列的最大/最小值。

为演示起见,为了简洁起见,请看以下示例。将这些系列值视为我将从实际应用程序中的.CSV文件中读取的内容。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        SeriesCollection = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Series 1",
                Values = new ChartValues<double> { 4, 66, 5, 2, 4 },
            },
            new LineSeries
            {
                Title = "Series 2",
                Values = new ChartValues<double> { 6, 7, 3, 4, 6 },
            },
            new LineSeries
            {
                Title = "Series 3",
                Values = new ChartValues<double> { 4, 2, 7, 2, 7 },
            }
        };

        DataContext = this;
    }

    public SeriesCollection SeriesCollection { get; set; }
}

我的XAML看起来很简单,就像这样:

<Window x:Class="WPFCharts.MainWindow"
        ...
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <lvc:CartesianChart Series="{Binding SeriesCollection}"/>
    </Grid>
</Window>

如您所见,如果我使用默认的LiveCharts设置将其绘制在折线图中,则与其他序列相比,一个序列中的一个值不在图表上:

enter image description here

因此,我想让用户有机会在自己的轴上放置这些折线图。仔细阅读LiveCharts文档,我发现as shown here可以通过使用ScaleXAtScaleYAt属性在不同的轴上放置不同的线系列。

但是,该示例在XAML中设置了轴,而我想动态地进行设置。因此,我尝试在代码中设置上述属性,如下所示:

SeriesCollection = new SeriesCollection
{
    new LineSeries
    {
        Title = "Series 1",
        Values = new ChartValues<double> { 4, 66, 5, 2, 4 },
        ScalesYAt = 0
    },
    new LineSeries
    {
        Title = "Series 2",
        Values = new ChartValues<double> { 6, 7, 3, 4, 6 },
        ScalesYAt = 1
    },
    new LineSeries
    {
        Title = "Series 3",
        Values = new ChartValues<double> { 4, 2, 7, 2, 7 },
        ScalesYAt = 2
    }
};

但是,当我这样做并运行应用程序时,我得到一个异常提示:

  

System.ArgumentOutOfRangeException:'索引超出范围。必须为非负数并且小于集合的大小。'

我在这里做错了什么?我该如何使用代码而非XAML来设置?

2 个答案:

答案 0 :(得分:2)

如果要使用其他Y轴,则需要声明它们,也许您错过了。这样您的模型将变为:

const char *
errmsg(int e, int action)
{
    if (e != ENOENT && e != ENOTDIR)
        return strerror(e);

    if (action & E_OPEN)
        return "No such file";
    else if (action & E_CREAT)
        return "Directory nonexistent";
    else
        return "not found";
}

XAML将是:

public class ViewModel
{
    public ViewModel()
    {
        SeriesCollection = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Series 1",
                Values = new ChartValues<double> { 4, 66, 5, 2, 4 },
                ScalesYAt = 0
            },
            new LineSeries
            {
                Title = "Series 2",
                Values = new ChartValues<double> { 6, 7, 3, 4, 6 },
                ScalesYAt = 1
            },
            new LineSeries
            {
                Title = "Series 3",
                Values = new ChartValues<double> { 4, 2, 7, 2, 7 },
                ScalesYAt = 2
            }
        };

        AxisYCollection = new AxesCollection
        {
            new Axis { Title = "Y Axis 1", Foreground = Brushes.Gray },
            new Axis { Title = "Y Axis 2", Foreground = Brushes.Red },
            new Axis { Title = "Y Axis 3", Foreground = Brushes.Brown }
        };
    }

    public AxesCollection AxisYCollection { get; set; }

    public SeriesCollection SeriesCollection { get; set; }

}

当然,您需要将<Grid> <lvc:CartesianChart Series="{Binding SeriesCollection}" AxisY="{Binding AxisYCollection}" /> </Grid> 类的实例设置为Window的ViewModel

DataContext

如果未在绑定的public MainWindow() { vm = new ViewModel(); InitializeComponent(); DataContext = vm; } 中声明“足够”的Axis,则将找不到n索引处的元素,并且您将陷入 ArgumentOutOfRangeException 。希望它能对您有所帮助。

答案 1 :(得分:0)

我正在做类似的事情;现在一团糟,我将其用作学习经验。

using CsvHelper;
using LiveCharts.Geared;
using LiveCharts.Wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Media;
using System.ComponentModel;
using System.Data;

namespace CSVtoCHART
{
    public partial class FrmCSVtoCHART : Form
    {
        public FrmCSVtoCHART()
        {
            InitializeComponent();
            LoadSettings();
            InitializeDataGridSettings();
            cartesianChart1.AxisX.Add(new Axis
            {
                Title = "Time",
                Labels = new string[] { "0", "1", "2", "3", "4" },
                MinValue = 0,
                MaxValue = 4,
                LabelsRotation = -45,
                Foreground = Brushes.Black
            });
            cartesianChart1.AxisY.Add(new Axis
            {
                Foreground = Brushes.Black,
                Title = "Chart",
                MinValue = 0,
                MaxValue = 100
            });
            cartesianChart1.Zoom = LiveCharts.ZoomingOptions.X;
        }

        public DataTable dt = new DataTable();

        private void BtnOpenCSV_Click(object sender, EventArgs e)
        {
            string FilePath;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                cartesianChart1.AxisX.Clear();
                cartesianChart1.AxisY.Clear();
                cartesianChart1.Series.Clear();

                FilePath = openFileDialog1.FileName;
                using (var streamReader = new StreamReader(@FilePath))
                using (var csv = new CsvReader(streamReader, CultureInfo.InvariantCulture))
                {
                    csv.Read();
                    csv.ReadHeader();
                    csv.Read();

                    int num = 0;
                    foreach (var header in csv.HeaderRecord)
                    {
                        if (double.TryParse(csv.GetField(num), out double value))
                        {
                            dt.Columns.Add(header, typeof(double));
                        }
                        else if (TryConvertStringToDateTime(csv.GetField(num), out DateTime ts))
                        {
                            dt.Columns.Add(header, typeof(string));
                        }

                        num++;
                    }

                    while (csv.Read())
                    {
                        num = 0;
                        var row = dt.NewRow();
                        foreach (DataColumn column in dt.Columns)
                        {
                            if (double.TryParse(csv.GetField(num), out double value))
                            {
                                row[column.ColumnName] = value;
                            }
                            else if (TryConvertStringToDateTime(csv.GetField(num), out DateTime ts))
                            {
                                row[column.ColumnName] = csv.GetField(num);
                            }
                            else if (column.DataType == typeof(double))
                            {
                                row[column.ColumnName] = 0; //double.NaN;
                            }

                            num++;
                        }
                        dt.Rows.Add(row);
                    }

                    var dgRow = new List<RowSettings>();
                    num = 0;
                    foreach (DataColumn column in dt.Columns)
                    {
                        if (column.DataType == typeof(double))
                        {
                            var brushes = typeof(Brushes).GetProperties()
                                .Where(pi => pi.PropertyType == typeof(SolidColorBrush))
                                .Select(pi => pi.Name)
                                .ToList();
                            var random = new Random();
                            int index = random.Next(brushes.Count);
                            var brush = typeof(Brushes).GetProperty(brushes[index])
                                .GetValue(null)
                                as SolidColorBrush;

                            List<double> vals = dt.AsEnumerable().Select(v => v.Field<double>(column.ColumnName)).Distinct().ToList();
                            double minVal = vals.Min();
                            double maxVal = vals.Max();
                            dgRow.Add(new RowSettings
                            (
                                column.ColumnName,
                                brushes[index],
                                true,
                                minVal,
                                maxVal
                            ));

                            AddYSeries(num, brushes[index], column, minVal, maxVal);

                            num++;
                        }
                        if (column.DataType == typeof(string))
                        {
                            var col = dt.AsEnumerable().Select(c => c.Field<string>(column.ColumnName)).ToList();
                            cartesianChart1.AxisX.Add(new Axis
                            {
                                Title = "Time",
                                Labels = col,
                                LabelsRotation = -45,
                                Foreground = Brushes.Black,
                                MinValue = 0,
                                MaxValue = 10000
                            });
                        }
                    }

                    dataGridView1.DataSource = dgRow;
                }
            }
        }

        private void AddYSeries(int num, string lineColor, DataColumn column, double minVal, double maxVal)
        {
            var brush = typeof(Brushes).GetProperty(lineColor)
                            .GetValue(null)
                            as SolidColorBrush;

            var col = dt.AsEnumerable().Select(c => c.Field<double>(column.ColumnName)).ToList();
            cartesianChart1.Series.Add(new LineSeries
            {
                Title = column.ColumnName,
                Values = col.AsGearedValues().WithQuality(Quality.Low),
                ScalesYAt = num,
                LineSmoothness = 0,
                StrokeThickness = 1,
                Fill = Brushes.Transparent,
                PointGeometry = null,
                Stroke = brush
                
            });

            cartesianChart1.AxisY.Add(new Axis
            {
                Foreground = brush,
                Title = column.ColumnName,
                MinValue = minVal,
                MaxValue = maxVal,
                DisableAnimations = true
            });
        }

        private void InitializeDataGridSettings()
        {
            var brushes = typeof(Brushes).GetProperties()
                             .Where(pi => pi.PropertyType == typeof(SolidColorBrush))
                             .Select(pi => pi.Name)
                             .ToList();
            DataGridViewColumn[] dgCol =
            {
                new DataGridViewTextBoxColumn()
                {
                    HeaderText = "Plot Name",
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    DataPropertyName = "PlotName",
                    ValueType = typeof(string),
                    ReadOnly = true
                },
                new DataGridViewCheckBoxColumn()
                {
                    HeaderText = "Display on chart?",
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    DataPropertyName = "Display",
                    ValueType = typeof(bool),
                    ReadOnly = false
                },
                new DataGridViewComboBoxColumn()
                {
                    HeaderText = "Line Color",
                    DataSource = brushes,
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    DataPropertyName = "LineColor",
                    ValueType = typeof(string),
                    ReadOnly = false
                },
                new DataGridViewComboBoxColumn()
                {
                    HeaderText = "Line Style",
                    DataSource = new List<string>
                    {
                        "Solid",
                        "Dash Dash",
                        "Dash Dot",
                        "Dot Dot",
                    },
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    DataPropertyName = "LineStyle",
                    MaxDropDownItems = 4,
                    ValueType = typeof(List<string>),
                    ReadOnly = false
                },
                new DataGridViewTextBoxColumn()
                {
                    HeaderText = "Chart Min",
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    DataPropertyName = "ChartMin",
                    ValueType = typeof(double),
                    ReadOnly = false
                },
                new DataGridViewTextBoxColumn()
                {
                    HeaderText = "Chart Max",
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    DataPropertyName = "ChartMax",
                    ValueType = typeof(double),
                    ReadOnly = false
                }
            };
            dataGridView1.Columns.AddRange(dgCol);
            dataGridView1.RowHeadersVisible = false;
            dataGridView1.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing;
        }

        public bool TryConvertStringToDateTime(string s, out DateTime result)  // function for converting string to datetime
        {
            return ((DateTime.TryParse(s, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy-MM-dd H:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy-MM-dd h:mm:ss tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy-MM-dd hh:mm:ss tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy/MM/dd H:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy/MM/dd HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy/MM/dd h:mm:ss tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy/MM/dd hh:mm:ss tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "yyyy/MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) ||
                    (DateTime.TryParseExact(s, "MM.dd.yy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
                   );
        }

        private void LoadSettings()
        {
            this.Height = Properties.Settings.Default.FormHeight;
            this.Width = Properties.Settings.Default.FormWidth;
            this.Location = Properties.Settings.Default.FormLocation;
        }

        private void FrmCSVtoCHART_FormClosing(object sender, FormClosingEventArgs e)
        {
            Properties.Settings.Default.FormHeight = this.Height;
            Properties.Settings.Default.FormWidth = this.Width;
            Properties.Settings.Default.FormLocation = this.Location;
            Properties.Settings.Default.Save();
        }

        private void btnApply_Click(object sender, EventArgs e)
        {
            cartesianChart1.AxisY.Clear();
            cartesianChart1.Series.Clear();
            int num = 0;
            List<string> showSeries = new List<string>();
            List<string> seriesColor = new List<string>();
            List<double> seriesMin = new List<double>();
            List<double> seriesMax = new List<double>();
            foreach (DataGridViewRow row in dataGridView1.Rows)
            {
                if (Convert.ToBoolean(row.Cells[1].Value))
                {
                    showSeries.Add(row.Cells[0].Value.ToString());
                    seriesColor.Add(row.Cells[2].Value.ToString());
                    seriesMin.Add(Convert.ToDouble(row.Cells[4].Value));
                    seriesMax.Add(Convert.ToDouble(row.Cells[5].Value));
                }
            }
            foreach (DataColumn column in dt.Columns)
            {
                if (showSeries.Contains(column.ColumnName))
                {
                    AddYSeries(num, seriesColor[num], column, seriesMin[num], seriesMax[num]);
                    num++;
                }
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSVtoCHART
{
    class RowSettings
    {
        private string _PlotName, _LineColor, _LineStyle;
        private bool _Display;
        private double _ChartMin, _ChartMax;
        public RowSettings(string _PlotName, string _LineColor, bool _Display, double _ChartMin, double _ChartMax)
        {
            this._PlotName = _PlotName;
            this._LineColor = _LineColor;
            this._Display = _Display;
            this._ChartMin = _ChartMin;
            this._ChartMax = _ChartMax;
        }
        public string PlotName 
        { 
            get 
            {
                return _PlotName; 
            } 
            set 
            { 
                _PlotName = value; 
            }
        }
        public bool Display
        {
            get
            {
                return _Display;
            }
            set
            {
                _Display = value;
            }
        }
        public string LineColor
        {
            get
            {
                return _LineColor;
            }
            set
            {
                _LineColor = value;
            }
        }
        public string LineStyle
        {
            get
            {
                return _LineStyle;
            }
            set
            {
                _LineStyle = value;
            }
        }
        public double ChartMin
        {
            get
            {
                return _ChartMin;
            }
            set
            {
                _ChartMin = value;
            }
        }
        public double ChartMax
        {
            get
            {
                return _ChartMax;
            }
            set
            {
                _ChartMax = value;
            }
        }
    }
}