I have been reading documentation for several days now but I can't get it working, no matter what I try. I have Basic Row chart and want to display as a graph time spent. My bar title and value are changing constantly (more items getting added). I am able to add bars with my current code, but I am not able to add title for each added bar. Only first title / first bar title is visible, all the others / coming are not visible.
How to add title and value in a proper way? (I am already familiar with documentation https://lvcharts.net/App/examples/v1/wf/Basic%20Row)
Here is my code (you can see from commented out sections what has been tried yet):
public static SeriesCollection SeriesCollection { get; set; }
public static string[] Labels { get; set; }
public static List<string> LabelsList { get; set; }
public static Func<double, string> Formatter { get; set; }
public AppUsageBarGraph()
{
InitializeComponent();
LabelsList = new List<string>();
SeriesCollection = new SeriesCollection
{
new RowSeries
{
Values = new ChartValues<double> { },
DataLabels = true
}
};
DataContext = this;
}
public static void UpdateChart()
{
SeriesCollection[0].Values.Clear();
LabelsList.Clear();
//Labels = MainProcess.ActivityLogGrouped.Rows.Cast<DataRow>().Select(row => row["Window Title"].ToString()).ToArray();
foreach (DataRow row in MainProcess.ActivityLogGrouped.Rows)
{
SeriesCollection[0].Values.Add(Convert.ToDouble(row["Time Spent"]));
//SeriesCollection[0]. = row["Time Spent"].ToString());
LabelsList.Add(row["Window Title"].ToString());
}
//MessageBox.Show(Labels[0].ToString());
Labels = LabelsList.ToArray();
//foreach (var item in Labels)
//{
// MessageBox.Show(item);
//}
//Labels = new[]
// {
// "Shea Ferriera",
// "Maurita Powel",
// "Scottie Brogdon",
// "Teresa Kerman",
// "Nell Venuti",
// "Anibal Brothers",
// "Anderson Dillman"
// };
//Formatter = value => value.ToString("N");
}
The key is to use a ObservableCollection<string>
instead of a string[]
.
I also recommend to use a model to encapsulate the actual chart data points. I introduced the class DataModel
for this reason.
The following example shows how to dynamically bind values and labels to the chart. I should say that making everything public static
is a very bad smelling code design.
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<wpf:CartesianChart Height="500">
<CartesianChart.Series>
<RowSeries Values="{Binding ChartModel.RowSeries}"
Configuration="{Binding ChartModel.RowSeriesConfiguration}"/>
</CartesianChart.Series>
<CartesianChart.AxisY>
<Axis Labels="{Binding ChartModel.RowSeriesLabels}" />
</CartesianChart.AxisY>
</CartesianChart>
</Window>
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.ChartModel = new ChartModel();
}
public void UpdateChart()
{
foreach (DataRow row in MainProcess.ActivityLogGrouped.Rows)
{
if (double.TryParse(row["Time Spent"], out double value)
{
string label = row["Window Title"];
var newDataModel = new DataModel(value, label);
this.ChartModel.RowSeries.Add(newDataModel);
this.ChartModel.RowSeriesLabels.Add(newDataModel.Label);
}
}
}
public ChartModel ChartModel { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ChartModel.cs
public class ChartModel : INotifyPropertyChanged
{
public ChartModel()
{
// Initialize chart
this.RowSeries = new ChartValues<DataModel>()
{
new DataModel(20, "Shea Ferriera"),
new DataModel(100, "Maurita Powel"),
new DataModel(60, "Scottie Brogdon"),
};
// Create labels
this.RowSeriesLabels = new ObservableCollection<string>();
foreach (DataModel dataModel in this.RowSeries)
{
this.RowSeriesLabels.Add("dataModel.Label");
}
// DatModel to value mapping
this.RowSeriesConfiguration = new CartesianMapper<DataModel>()
.X(dataModel => dataModel.Value);
}
public CartesianMapper<DataModel> RowSeriesConfiguration { get; set; }
public ChartValues<DataModel> RowSeries { get; set; }
public ObservableCollection<string> RowSeriesLabels { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
DataModel.cs
public class DataModel
{
public DataModel(double value, string label)
{
this.Value = value;
this.Label = label;
}
public double Value { get; set; }
public string Label { get; set; }
}