Search code examples
c#wpfbar-chartoxyplot

Remove item from ColumnSeries


Using Oxyplot, how would I remove an item from a given ColumnSeries?

Given the code which is the example provided in the library itself (with a small modification), as well as a Delete event of some sort (which I could figure out myself) how would I remove an item (column) from the graph?

If I simply remove the item from the bar.Items list, the Label won't dissaper. Removing it from the tmp.Axes[0].ActualLabels (which is the CategoryAxis) won't "refresh" the view, and the Label remains there. Is there any solution for this situation? I've managed to do it with Line and Pie Graphs, but I'm struggling with the Column one.

Code-behind for building the Graph:

namespace ColumnSeriesDemo
{
    using System.Collections.ObjectModel;
    using System.Windows;

    using OxyPlot;
    using OxyPlot.Axes;
    using OxyPlot.Series;

    using WpfExamples;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    [Example("Shows column series.")]
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();

            // Create some data
            this.Items = new Collection<Item>
                            {
                                new Item {Label = "Apples", Value1 = 37, Value2 = 12, Value3 = 19},
                                new Item {Label = "Pears", Value1 = 7, Value2 = 21, Value3 = 9},
                                new Item {Label = "Bananas", Value1 = 23, Value2 = 2, Value3 = 29}
                            };

            // Create the plot model
            var tmp = new PlotModel { Title = "Column series", LegendPlacement = LegendPlacement.Outside, LegendPosition = LegendPosition.RightTop, LegendOrientation = LegendOrientation.Vertical };

            // Add the axes, note that MinimumPadding and AbsoluteMinimum should be set on the value axis.
            tmp.Axes.Add(new CategoryAxis { ItemsSource = this.Items, LabelField = "Label" });
            tmp.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MinimumPadding = 0, AbsoluteMinimum = 0 });

            // Add the series, note that the BarSeries are using the same ItemsSource as the CategoryAxis.
            ColumnSeries bar = new ColumnSeries();
            tmp.Series.Add(bar);
            bar.Items.Add(new ColumnItem { Color = OxyPlot.OxyColors.Yellow, Value = this.Items[0].Value3, CategoryIndex = 0 });
            bar.Items.Add(new ColumnItem { Color = OxyPlot.OxyColors.Green, Value = this.Items[0].Value2, CategoryIndex = 2 });
            bar.Items.Add(new ColumnItem { Color = OxyPlot.OxyColors.Red, Value = this.Items[0].Value1, CategoryIndex = 3 });


            this.Model1 = tmp;

            this.DataContext = this;
        }

        public Collection<Item> Items { get; set; }

        public PlotModel Model1 { get; set; }
    }

    public class Item
    {
        public string Label { get; set; }
        public double Value1 { get; set; }
        public double Value2 { get; set; }
        public double Value3 { get; set; }
    }
}

Solution

  • I'm not really sure I know what you are trying to do, but I hope this helps you:

    I took the Sample and played a little bit around your code and the sample. I think the problem with your code was that you have a Bind in your CategoryAxis, but the data is not added with a Bind but directly in your ColumnSeries. Using your code, I left the first part as it is, and instead of ColumnSeries bar = new ColumnSeries() I did:

            ColumnSeries bar = new ColumnSeries
            {
                FillColor = OxyPlot.OxyColors.Yellow,
                ValueField = "Value1",
                Title = "Value1",
                ItemsSource = Items
            };
            tmp.Series.Add(bar);
    

    This way the data in Items is binded both in your CategoryAxis and in your ColumnSeries (of course if you need more columns representing the Value2 and Value3 values of your Items class you can add new ColumnSeries to the series of your PlotModel)

    Then I added a Button to the Window, and in the Code-Behind added:

            Items.RemoveAt(0);
            Model1.InvalidatePlot(true);
    

    And this updates the Plot removing (each time) the first ColumnSeries including the Label in the CategoryAxis.

    Window Code-Behind:

    using System.Collections.ObjectModel;
    using System.Windows;
    using OxyPlot;
    using OxyPlot.Axes;
    using OxyPlot.Series;
    
    namespace OxyPlot_TEST
    {
        /// <summary>
        /// Interaction logic for Window2.xaml
        /// </summary>
        public partial class Window2 : Window
        {
            public Window2()
            {
                InitializeComponent();
    
                // Create some data
                this.Items = new Collection<Item>
                                {
                                    new Item {Label = "Apples", Value1 = 37, Value2 = 12, Value3 = 19},
                                    new Item {Label = "Pears", Value1 = 7, Value2 = 21, Value3 = 9},
                                    new Item {Label = "Bananas", Value1 = 23, Value2 = 2, Value3 = 29}
                                };
    
                // Create the plot model
                var tmp = new PlotModel { Title = "Column series", LegendPlacement = LegendPlacement.Outside, LegendPosition = LegendPosition.RightTop, LegendOrientation = LegendOrientation.Vertical };
    
                // Add the axes, note that MinimumPadding and AbsoluteMinimum should be set on the value axis.
                tmp.Axes.Add(new CategoryAxis { ItemsSource = this.Items, LabelField = "Label" });
                tmp.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MinimumPadding = 0, AbsoluteMinimum = 0 });
    
                ColumnSeries bar = new ColumnSeries
                {
                    FillColor = OxyPlot.OxyColors.Yellow,
                    ValueField = "Value1",
                    Title = "Value1",
                    ItemsSource = Items
                };
                ColumnSeries bar1 = new ColumnSeries
                {
                    FillColor = OxyPlot.OxyColors.Green,
                    ValueField = "Value1",
                    Title = "Value1",
                    ItemsSource = Items
                };
                ColumnSeries bar2 = new ColumnSeries
                {
                    FillColor = OxyPlot.OxyColors.Red,
                    ValueField = "Value1",
                    Title = "Value1",
                    ItemsSource = Items
                };
                tmp.Series.Add(bar);
                tmp.Series.Add(bar1);
                tmp.Series.Add(bar2);
    
                this.Model1 = tmp;
                this.DataContext = this;
            }
    
            public Collection<Item> Items { get; set; }
    
            public PlotModel Model1 { get; set; }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                Items.RemoveAt(0);
                Model1.InvalidatePlot(true);
            }
        }
    
        public class Item
        {
            public string Label { get; set; }
            public double Value1 { get; set; }
            public double Value2 { get; set; }
            public double Value3 { get; set; }
        }
    }
    

    <<--------------------------------- EDIT --------------------------------->>

    If you just need one bar per category, you need only one Value per Item. Then you can (as in the previous example) Remove or even Add Items from/to the Collection (updating the Plot using InvalidatePlot. Code-Behind:

    using System.Collections.ObjectModel;
    using System.Windows;
    using OxyPlot;
    using OxyPlot.Axes;
    using OxyPlot.Series;
    
    namespace OxyPlot_TEST
    {
        /// <summary>
        /// Interaction logic for Window2.xaml
        /// </summary>
        public partial class Window2 : Window
        {
            public Window2()
            {
                InitializeComponent();
    
                // Create some data
                this.Items = new Collection<Item>
                                {
                                    new Item {Label = "Apples", Value1 = 37},
                                    new Item {Label = "Pears", Value1 = 7},
                                    new Item {Label = "Bananas", Value1 = 23}
                                };
    
                // Create the plot model
                var tmp = new PlotModel { Title = "Column series", LegendPlacement = LegendPlacement.Outside, LegendPosition = LegendPosition.RightTop, LegendOrientation = LegendOrientation.Vertical };
    
                // Add the axes, note that MinimumPadding and AbsoluteMinimum should be set on the value axis.
                tmp.Axes.Add(new CategoryAxis { ItemsSource = this.Items, LabelField = "Label" });
                tmp.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MinimumPadding = 0, AbsoluteMinimum = 0 });
    
                ColumnSeries bar = new ColumnSeries
                {
                    FillColor = OxyPlot.OxyColors.Black,
                    ValueField = "Value1",
                    Title = "Value1",
                    ItemsSource = Items
                };
                tmp.Series.Add(bar);
    
                this.Model1 = tmp;
                this.DataContext = this;
            }
    
            public Collection<Item> Items { get; set; }
    
            public PlotModel Model1 { get; set; }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                Items.RemoveAt(0);
                Model1.InvalidatePlot(true);
            }
    
            private void button2_Click(object sender, RoutedEventArgs e)
            {
                Items.Add(new Item()
                {
                    Label = "Strawberrys", Value1 = 55
                });
                Model1.InvalidatePlot(true);
            }
        }
    
        public class Item
        {
            public string Label { get; set; }
            public double Value1 { get; set; }
        }
    }