我正在尝试编写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
文档,我发现as shown here可以通过使用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:'索引超出范围。必须为非负数并且小于集合的大小。'
我在这里做错了什么?我该如何使用代码而非XAML
来设置?
答案 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;
}
}
}
}