Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BoxPlotSeries to OxyPlot.WPF #425

Closed
stefan-schweiger opened this issue Mar 27, 2015 · 3 comments
Closed

Add BoxPlotSeries to OxyPlot.WPF #425

stefan-schweiger opened this issue Mar 27, 2015 · 3 comments

Comments

@stefan-schweiger
Copy link
Contributor

I'm trying to add a BoxPlotSeries for WPF (and want to create a pull request for it later), but for some reason I can't get it to work.

Does anyone see what's wrong with my code?

View:

<oxy:Plot x:Name="ChartPlot">
    <oxy:Plot.Series>
        <classes:BoxPlotSeries ItemsSource="{Binding BoxPlotItems}" />
    </oxy:Plot.Series>
</oxy:Plot>

ViewModel:

BoxPlotItems = new List<BoxPlotItem>
{
    new BoxPlotItem(1, 10, 15, 20, 25, 30, new List<double>()),
    new BoxPlotItem(2, 10, 15, 20, 25, 30, new List<double>()),
    new BoxPlotItem(3, 10, 15, 20, 25, 30, new List<double>())
};

Implementation:

public class BoxPlotSeries : XYAxisSeries
{
    /// <summary>
    /// Identifies this <see cref="BoxWidthProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty BoxWidthProperty = DependencyProperty.Register(
        "BoxWidth",
        typeof(double),
        typeof(BoxPlotSeries),
        new PropertyMetadata(0.3, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="FillProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
        "Fill",
        typeof(Color),
        typeof(BoxPlotSeries),
        new PropertyMetadata(MoreColors.Automatic, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="LineStyleProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty LineStyleProperty = DependencyProperty.Register(
        "LineStyle",
        typeof(LineStyle),
        typeof(BoxPlotSeries),
        new PropertyMetadata(LineStyle.Solid, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="MedianPointSizeProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty MedianPointSizeProperty = DependencyProperty.Register(
        "MedianPointSize",
        typeof(double),
        typeof(BoxPlotSeries),
        new PropertyMetadata(2.0, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="MedianThicknessProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty MedianThicknessProperty = DependencyProperty.Register(
        "MedianThickness",
        typeof(double),
        typeof(BoxPlotSeries),
        new PropertyMetadata(2.0, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="OutlierSizeProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty OutlierSizeProperty = DependencyProperty.Register(
        "OutlierSize",
        typeof(double),
        typeof(BoxPlotSeries), 
        new PropertyMetadata(2.0, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="OutlierTypeProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty OutlierTypeProperty = DependencyProperty.Register(
        "OutlierType",
        typeof(MarkerType),
        typeof(BoxPlotSeries),
        new PropertyMetadata(MarkerType.Circle, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="OutlierOutlineProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty OutlierOutlineProperty = DependencyProperty.Register(
        "OutlierOutline",
        typeof(Point[]),
        typeof(BoxPlotSeries),
        new PropertyMetadata(null, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="ShowBoxProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty ShowBoxProperty = DependencyProperty.Register(
        "ShowBox",
        typeof(bool),
        typeof(BoxPlotSeries),
        new PropertyMetadata(true, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="ShowMedianAsDotProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty ShowMedianAsDotProperty = DependencyProperty.Register(
        "ShowMedianAsDot",
        typeof(bool),
        typeof(BoxPlotSeries),
        new PropertyMetadata(false, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="StrokeProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
        "Stroke",
        typeof(Color),
        typeof(BoxPlotSeries),
        new PropertyMetadata(Colors.Black, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="StrokeThicknessProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
        "StrokeThickness",
        typeof(double),
        typeof(BoxPlotSeries),
        new PropertyMetadata(1.0, AppearanceChanged));

    /// <summary>
    /// Identifies this <see cref="WhiskerWidthProperty"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty WhiskerWidthProperty = DependencyProperty.Register(
        "WhiskerWidth",
        typeof(double),
        typeof(BoxPlotSeries),
        new PropertyMetadata(0.5, AppearanceChanged));

    /// <summary>
    /// Gets or sets the width of the boxes (specified in x-axis units).
    /// </summary>
    /// <value>The width of the boxes.</value>
    public double BoxWidth
    {
        get { return (double)GetValue(BoxWidthProperty); }
        set { SetValue(BoxWidthProperty, value); }
    }

    /// <summary>
    /// Gets or sets the fill color. If <c>null</c>, this color will be automatically set.
    /// </summary>
    /// <value>The fill color.</value>
    public Color Fill
    {
        get { return (Color)GetValue(FillProperty); }
        set { SetValue(FillProperty, value); }
    }

    /// <summary>
    /// Gets or sets the line style.
    /// </summary>
    /// <value>The line style.</value>
    public LineStyle LineStyle
    {
        get { return (LineStyle)GetValue(LineStyleProperty); }
        set { SetValue(LineStyleProperty, value); }
    }

    /// <summary>
    /// Gets or sets the size of the median point.
    /// </summary>
    /// <remarks>This property is only used when MedianStyle = Dot.</remarks>
    public double MedianPointSize
    {
        get { return (double)GetValue(MedianPointSizeProperty); }
        set { SetValue(MedianPointSizeProperty, value); }
    }

    /// <summary>
    /// Gets or sets the median thickness, relative to the StrokeThickness.
    /// </summary>
    /// <value>The median thickness.</value>
    public double MedianThickness
    {
        get { return (double)GetValue(MedianThicknessProperty); }
        set { SetValue(MedianThicknessProperty, value); }
    }

    /// <summary>
    /// Gets or sets the diameter of the outlier circles (specified in points).
    /// </summary>
    /// <value>The size of the outlier.</value>
    public double OutlierSize
    {
        get { return (double)GetValue(OutlierSizeProperty); }
        set { SetValue(OutlierSizeProperty, value); }
    }

    /// <summary>
    /// Gets or sets the type of the outliers.
    /// </summary>
    /// <value>The type of the outliers.</value>
    /// <remarks>MarkerType.Custom is currently not supported.</remarks>
    public MarkerType OutlierType
    {
        get { return (MarkerType)GetValue(OutlierTypeProperty); }
        set { SetValue(OutlierTypeProperty, value); }
    }

    /// <summary>
    /// Gets or sets the a custom polygon outline for the outlier markers. Set <see cref="OutlierType" /> to <see cref="OxyPlot.MarkerType.Custom" /> to use this property.
    /// </summary>
    /// <value>A polyline. The default is <c>null</c>.</value>
    public Point[] OutlierOutline
    {
        get { return (Point[])GetValue(OutlierOutlineProperty); }
        set { SetValue(OutlierOutlineProperty, value); }
    }

    /// <summary>
    /// Gets or sets a value indicating whether to show the boxes.
    /// </summary>
    public bool ShowBox
    {
        get { return (bool)GetValue(ShowBoxProperty); }
        set { SetValue(ShowBoxProperty, value); }
    }

    /// <summary>
    /// Gets or sets a value indicating whether to show the median as a dot.
    /// </summary>
    public bool ShowMedianAsDot
    {
        get { return (bool)GetValue(ShowMedianAsDotProperty); }
        set { SetValue(ShowMedianAsDotProperty, value); }
    }

    /// <summary>
    /// Gets or sets the stroke color.
    /// </summary>
    /// <value>The stroke color.</value>
    public Color Stroke
    {
        get { return (Color)GetValue(StrokeProperty); }
        set { SetValue(StrokeProperty, value); }
    }

    /// <summary>
    /// Gets or sets the stroke thickness.
    /// </summary>
    /// <value>The stroke thickness.</value>
    public double StrokeThickness
    {
        get { return (double)GetValue(StrokeThicknessProperty); }
        set { SetValue(StrokeThicknessProperty, value); }
    }

    /// <summary>
    /// Gets or sets the width of the whiskers (relative to the BoxWidth).
    /// </summary>
    /// <value>The width of the whiskers.</value>
    public double WhiskerWidth
    {
        get { return (double)GetValue(WhiskerWidthProperty); }
        set { SetValue(WhiskerWidthProperty, value); }
    }

    public BoxPlotSeries()
    {
        this.InternalSeries = new OxyPlot.Series.BoxPlotSeries();
    }

    /// <summary>
    /// Initializes static members of the <see cref="BoxPlotSeries"/> class.
    /// </summary>
    static BoxPlotSeries()
    {
        TrackerFormatStringProperty.OverrideMetadata(typeof(BoxPlotSeries), new PropertyMetadata(OxyPlot.Series.BoxPlotSeries.DefaultTrackerFormatString, AppearanceChanged));
    }

    public override OxyPlot.Series.Series CreateModel()
    {
        this.SynchronizeProperties(this.InternalSeries);
        return this.InternalSeries;
    }

    protected override void SynchronizeProperties(OxyPlot.Series.Series series)
    {
        base.SynchronizeProperties(series);

        var s = (OxyPlot.Series.BoxPlotSeries)series;
        s.Fill = this.Fill.ToOxyColor();
        s.LineStyle = this.LineStyle;
        s.MedianPointSize = this.MedianPointSize;
        s.OutlierSize = this.OutlierSize;
        s.OutlierType = this.OutlierType;
        s.OutlierOutline = this.OutlierOutline.ToScreenPointArray();
        s.ShowBox = this.ShowBox;
        s.ShowMedianAsDot = this.ShowMedianAsDot;
        s.Stroke = this.Stroke.ToOxyColor();
        s.StrokeThickness = this.StrokeThickness;
        s.WhiskerWidth = this.WhiskerWidth;

        if (this.ItemsSource == null)
        {
            s.Items.Clear();
            foreach (var item in this.Items)
            {
                s.Items.Add((OxyPlot.Series.BoxPlotItem)item);
            }
        }
    }
}
@stefan-schweiger
Copy link
Contributor Author

I've now tried to set the items directly instead of using ItemsSource and suddenly it magically works.

So the problem seems to be somewhere with the ItemsSource Binding stuff. Please anyone got an idea why this doesn't work?

<classes:BoxPlotSeries Fill="Orange">
    <classes:BoxPlotSeries.Items>
        <series:BoxPlotItem BoxBottom="15"
                            BoxTop="25"
                            LowerWhisker="10"
                            Median="20"
                            UpperWhisker="30"
                            X="1" />
    </classes:BoxPlotSeries.Items>
</classes:BoxPlotSeries>

2015-03-30 09_51_17-glowwindow

PS: The BoxPlotItem implementation should probably check if Outliers are null before trying to merge them as it results in an error.

I currently fixed it by this:

if (this.ItemsSource == null)
{
    s.Items.Clear();
    foreach (var item in this.Items)
    {
        var boxPlotItem = (OxyPlot.Series.BoxPlotItem)item;

        if (boxPlotItem.Outliers == null)
            boxPlotItem.Outliers = new List<double>();

        s.Items.Add(boxPlotItem);
    }
}

@stefan-schweiger
Copy link
Contributor Author

I borrowed some code from BarBaseSeries and got it to work, but I had to do some changes on OxyPlot.Series.BoxPlot.

I will try to make a pullrequest later.

@YahorSubach
Copy link

It seems that here must be a string with BoxWidth:

 protected override void SynchronizeProperties(OxyPlot.Series.Series series)
    {
        base.SynchronizeProperties(series);

        var s = (OxyPlot.Series.BoxPlotSeries)series;
        s.Fill = this.Fill.ToOxyColor();
        s.LineStyle = this.LineStyle;
        s.MedianPointSize = this.MedianPointSize;
        s.OutlierSize = this.OutlierSize;
        s.OutlierType = this.OutlierType;
        s.OutlierOutline = this.OutlierOutline.ToScreenPointArray();
        s.ShowBox = this.ShowBox;
        s.ShowMedianAsDot = this.ShowMedianAsDot;
        s.Stroke = this.Stroke.ToOxyColor();
        s.StrokeThickness = this.StrokeThickness;
        s.WhiskerWidth = this.WhiskerWidth;
        //HERE s.BoxWidth = this.BoxWidth;     <----------- SOMETHING LIKE THIS 

        if (this.ItemsSource == null)
        {
            s.Items.Clear();
            foreach (var item in this.Items)
            {
                s.Items.Add((OxyPlot.Series.BoxPlotItem)item);
            }
        }
    }

without it BoxWidth in xaml does not change anything in my case:

            <oxy:Plot.Series>
                <oxy:BoxPlotSeries Title="Values"
                                       Fill="LightBlue"
                                       BoxWidth="0.0005"
                                       WhiskerWidth="0.0005"
                                       ItemsSource="{Binding Items}" />
            </oxy:Plot.Series>

image

When I have added this string to your code it seems that it all became as it should:
image

Whiskers are not shown because theirs width are relative to the BoxWidth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants