Несколько осей во время выполнения в LiveCharts

Я пытаюсь написать приложение 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:

Несколько осей во время выполнения в LiveCharts

Поэтому я хочу дать пользователю возможность размещать такие линейные графики на своей оси. Прочитав документацию LiveCharts, я обнаружил, как показано здесь, что вы можете размещать разные серии линий на разных осях, используя свойства ScaleXAt и ScaleYAt.

Однако этот пример устанавливает ось в 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: 'Index was out of range. Must be non-negative and less than the size of the collection.'

Что я здесь делаю неправильно? Как я могу установить это с помощью кода, а не XAML?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
3 237
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы хотите использовать другую ось Y, вам нужно объявить их, возможно, вы пропустили это. Таким образом, ваша модель станет примерно такой:

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; }

}

в то время как XAML будет:

<Grid>
    <lvc:CartesianChart Series = "{Binding SeriesCollection}" AxisY = "{Binding AxisYCollection}" />
</Grid>

Конечно, вам нужно установить экземпляр класса ViewModel в качестве DataContext вашего окна:

public MainWindow()
{
    vm = new ViewModel();

    InitializeComponent();
    DataContext = vm;
}

Если вы не объявите «достаточно» Axis в привязке AxesCollection, ее элемент с индексом n не будет найден, и вы попадете в Аргументаутофранжексцептион. Я надеюсь, что это может помочь вам.

Вот и все! Да, я не знал, что мне нужно добавить ось отдельно, я думаю, я предположил, что настройка ScalesYAt сделала это автоматически. Спасибо!

Sach 09.04.2019 16:50

Я работаю над чем-то подобным; это беспорядок прямо сейчас, я использую его как опыт обучения.

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;
            }
        }
    }
}

пожалуйста, объясните логику вашего кода

Anand Vidvat 16.04.2021 19:40

Другие вопросы по теме