Created
January 29, 2018 11:46
-
-
Save dodikk/02e85b3f38dc4bd87cf96a489a0e2983 to your computer and use it in GitHub Desktop.
[solved] OxyPlot columns and line series together Raw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace PracticeDashboard.PlotBuilders.SalesPipeline | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Helpers; | |
using global::OxyPlot; | |
using global::OxyPlot.Axes; | |
using global::OxyPlot.Series; | |
using PracticeDashboard.DAL.Models.SalesPipeline; | |
using Money = System.Decimal; | |
using System.Diagnostics; | |
using PracticeDashboard.Models; | |
public class SalesPipelineNormalizedPlotBuilder | |
{ | |
// http://docs.oxyplot.org/en/latest/guidelines/design.html | |
// oxyplot guidelines | |
// | |
private readonly SalesPipelinePlotTheme currentTheme = SalesPipelinePlotTheme.DefaultTheme(); | |
private SalesPipelineChartModel pipelineData; | |
private SalesPipelineModelNormalizationHelper normalizedData; | |
public SalesPipelineNormalizedPlotBuilder() | |
{ | |
} | |
#region ISalesPipelinePlotBuilder | |
public PlotModel ResultModel { get; private set; } | |
public SalesPipelineNormalizedPlotBuilder WithReport(SalesPipelineChartModel pipelineData) | |
{ | |
Debug.Assert(null == this.pipelineData); | |
this.pipelineData = pipelineData; | |
this.GenerateNormalizedData(); | |
return this; | |
} | |
public SalesPipelineNormalizedPlotBuilder Build() | |
{ | |
Debug.Assert(null != this.pipelineData); | |
this.ImplBuildPlot(); | |
Debug.Assert(null != this.ResultModel); | |
return this; | |
} | |
#endregion ISalesPipelinePlotBuilder | |
private void ImplBuildPlot() | |
{ | |
PlotModel result = new PlotModel(); | |
this.AddAxisForBarsToPlot(result); | |
this.AddVerticalAxisToPlot(result); | |
this.AddCountLineToPlot(result); | |
this.AddAmountLineSeriesToPlot(result); | |
this.AddCountBarsToPlot(result); | |
this.ResultModel = result; | |
} | |
#region logic | |
private void GenerateNormalizedData() | |
{ | |
Debug.Assert(null != this.pipelineData); | |
this.normalizedData = new SalesPipelineModelNormalizationHelper(); | |
this.normalizedData.Normalize( | |
salesPipelineDataset: this.pipelineData, | |
majorTickCount: this.currentTheme.numberOfSquaresInGrid); | |
} | |
#endregion logic | |
#region series | |
/** | |
* | |
* Adds "total count" data as it is. | |
* | |
*/ | |
private void AddCountLineToPlot(PlotModel result) | |
{ | |
var dataset = this.pipelineData.ValuesByMonth; | |
// Using point index as X value because of CategoryAxis | |
// https://github.com/oxyplot/oxyplot/issues/1187 | |
// | |
Func<SalesPipelineDataPoint, DataPoint> dataPointBuilder = | |
point => | |
new DataPoint( | |
//x: DateTimeAxis.ToDouble(point.yearAndMonth), | |
x: dataset.IndexOf(point), | |
y: Convert.ToDouble(point.pendingOpportunitiesTotalCount) | |
); | |
List<DataPoint> points = | |
dataset.Select(dataPointBuilder) | |
.ToList(); | |
LineSeries lineSeries = PlotHelpers.GetLineSeries( | |
data: points, | |
color: this.currentTheme.salesCountLineColor, | |
markerSize: PlotHelpers.DefaultMarkerSize, | |
thickness: PlotHelpers.DefaultThickness, | |
filled: false); | |
result.Series.Add(lineSeries); | |
} | |
/** | |
* | |
* Adds data of "amounts" normalized to "count space" | |
* | |
* | |
*/ | |
private void AddAmountLineSeriesToPlot(PlotModel result) | |
{ | |
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth; | |
// Using point index as X value because of CategoryAxis | |
// https://github.com/oxyplot/oxyplot/issues/1187 | |
// | |
Func<SalesPipelineDataPointNormalized, DataPoint> dataPointBuilder = | |
point => | |
new DataPoint( | |
// x: DateTimeAxis.ToDouble(point.yearAndMonth), | |
x: dataset.IndexOf(point), | |
y: point.totalBudgetAmountForPendingOpportunities | |
); | |
List<DataPoint> points = dataset.Select(dataPointBuilder) | |
.ToList(); | |
LineSeries lineSeries = PlotHelpers.GetLineSeries( | |
data: points, | |
color: this.currentTheme.amountsLineColor, | |
markerSize: PlotHelpers.DefaultMarkerSize, | |
thickness: PlotHelpers.DefaultThickness, | |
filled: false); | |
result.Series.Add(lineSeries); | |
} | |
#endregion series | |
#region Bars | |
/** | |
* | |
* Adds tripple bar "count" data as it is. | |
* | |
*/ | |
private void AddCountBarsToPlot(PlotModel result) | |
{ | |
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth; | |
//#if DEBUG | |
// var dates = dataset.Select(dp => dp.yearAndMonth).ToArray(); | |
// var started = dataset.Select(dp => dp.numberOfOpportunitiesStartedThisMonth).ToArray(); | |
// var won = dataset.Select(dp => dp.numberOfOpportunitiesWonThisMonth).ToArray(); | |
// var lost = dataset.Select(dp => dp.numberOfOpportunitiesLostThisMonth).ToArray(); | |
//#endif | |
// == | |
// | |
var startedBarPlotData = | |
dataset.Select( | |
salesPipelineDataPoint => | |
new OxyColumnViewModel | |
{ | |
Date = salesPipelineDataPoint.yearAndMonth, | |
Value = salesPipelineDataPoint.numberOfOpportunitiesStartedThisMonth, | |
Color = this.currentTheme.newCountBarColor | |
}); | |
var startedBarSeries = | |
new ColumnSeries | |
{ | |
StrokeColor = OxyColors.Transparent, | |
StrokeThickness = 0, | |
ColumnWidth = 4, | |
ItemsSource = startedBarPlotData, | |
ValueField = "Value", | |
ColorField = "Color" | |
}; | |
// == | |
// | |
var wonBarPlotData = | |
dataset.Select( | |
salesPipelineDataPoint => | |
new OxyColumnViewModel | |
{ | |
Date = salesPipelineDataPoint.yearAndMonth, | |
Value = salesPipelineDataPoint.numberOfOpportunitiesWonThisMonth, | |
Color = this.currentTheme.wonCountBarColor | |
}); | |
var wonBarSeries = | |
new ColumnSeries | |
{ | |
StrokeColor = OxyColors.Transparent, | |
StrokeThickness = 0, | |
ColumnWidth = 4, | |
ItemsSource = wonBarPlotData, | |
ValueField = "Value", | |
ColorField = "Color" | |
}; | |
// == | |
// | |
var lostBarPlotData = | |
dataset.Select( | |
salesPipelineDataPoint => | |
new OxyColumnViewModel | |
{ | |
Date = salesPipelineDataPoint.yearAndMonth, | |
Value = salesPipelineDataPoint.numberOfOpportunitiesLostThisMonth, | |
Color = this.currentTheme.lostCountBarColor | |
}); | |
var lostBarSeries = | |
new ColumnSeries | |
{ | |
StrokeColor = OxyColors.Transparent, | |
StrokeThickness = 0, | |
ColumnWidth = 4, | |
ItemsSource = lostBarPlotData, | |
ValueField = "Value", | |
ColorField = "Color" | |
}; | |
result.Series.Add(startedBarSeries); | |
result.Series.Add(wonBarSeries); | |
result.Series.Add(lostBarSeries); | |
} | |
#endregion Bars | |
#region Axes | |
/** | |
* | |
* Expands range to | |
* | |
* ``` | |
* max(totalCount) + normalized( max(amount) ) | |
* ``` | |
* | |
*/ | |
private void AddVerticalAxisToPlot(PlotModel result) | |
{ | |
int countMaxValue = this.normalizedData.countMax; | |
double fCountMaxValue = this.normalizedData.fCountMax; | |
double emulatedAmountMaxValue = this.normalizedData.emulatedAmountMax; | |
// TODO: maybe we should round it to some "beautiful" value | |
// like "5 / 10 / ..." | |
double fNumberOfSquaresInGrid = Convert.ToDouble(this.currentTheme.numberOfSquaresInGrid); | |
double step = this.normalizedData.countStep; | |
double maxValue = Math.Max(fCountMaxValue, emulatedAmountMaxValue) + step; | |
var verticalAxis = | |
PlotHelpers.GetLinearAxis( | |
minimum: 0, | |
maximum: maxValue, | |
textColor: this.currentTheme.axisColor, | |
gridStep: step, | |
position: AxisPosition.Right, | |
moneyValues: false); | |
var previousFormatter = verticalAxis.LabelFormatter; | |
// TODO: maybe move lambda to PCL | |
// so that it would be testable | |
// | |
// ** but not to SalesPipelineModelNormalizationHelper class | |
// | |
verticalAxis.LabelFormatter = | |
(double arg) => | |
{ | |
string currentLabel = previousFormatter(arg); | |
double precision = 0.001; | |
bool isZeroAmount = | |
Math.Abs(arg - this.normalizedData.emulatedAmountZeroOffset) <= precision; | |
bool isCountLabel = | |
(arg < this.normalizedData.emulatedAmountZeroOffset); | |
if (isZeroAmount) | |
{ | |
return "$0"; | |
} | |
else if (isCountLabel) | |
{ | |
string lambdaResult = currentLabel; | |
return lambdaResult; | |
} | |
else | |
{ | |
double recoveredAmount = | |
this.normalizedData.ConvertEmulatedAmountToOriginalScale(arg); | |
double millionsCount = | |
recoveredAmount / 1000000; | |
string strMillions = millionsCount.ToString("N1"); | |
string lambdaResult = $"${strMillions}M"; | |
return lambdaResult; | |
} | |
}; | |
this.ConfigureGridForVerticalAxis(verticalAxis); | |
result.Axes.Add(verticalAxis); | |
} | |
private void AddAxisForBarsToPlot(PlotModel result) | |
{ | |
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth; | |
// CategoryAxis is forced by OxyPlot if ColumnSeries are used | |
// otherwise an exception is thrown | |
// | |
var dates = | |
dataset.Select( | |
salesPipelineDataPoint => salesPipelineDataPoint.yearAndMonth | |
); | |
var columnAxisX = new CategoryAxis | |
{ | |
Position = AxisPosition.Bottom, | |
ItemsSource = dates, | |
StringFormat = "MMM yy", | |
TicklineColor = OxyColors.Transparent, | |
TextColor = this.currentTheme.axisColor, | |
IsZoomEnabled = false, | |
IsPanEnabled = true, | |
// To avoid render collapse | |
// https://github.com/oxyplot/oxyplot/issues/1187 | |
// | |
AbsoluteMinimum = -1, | |
AbsoluteMaximum = dates.Count() | |
}; | |
this.ConfigureGridForTimeAxis(columnAxisX); | |
result.Axes.Add(columnAxisX); | |
} | |
#endregion Axes | |
#region Grid | |
private void ConfigureGridForVerticalAxis(Axis verticalAxis) | |
{ | |
verticalAxis.MajorGridlineStyle = LineStyle.Solid; | |
verticalAxis.MajorGridlineThickness = 1; | |
var blackColor = OxyColor.FromRgb(r: 0, g: 0, b: 0); | |
verticalAxis.MajorGridlineColor = blackColor; | |
} | |
private void ConfigureGridForTimeAxis(Axis timeAxis) | |
{ | |
timeAxis.MajorGridlineStyle = LineStyle.Dash; | |
timeAxis.MajorGridlineThickness = 1; | |
var blackColor = OxyColor.FromRgb(r: 0, g: 0, b: 0); | |
timeAxis.MajorGridlineColor = blackColor; | |
} | |
#endregion Grid | |
} | |
} |
Implementation with Column Selection
namespace PracticeDashboard.PlotBuilders.SalesPipeline
{
using System;
using System.Collections.Generic;
using System.Linq;
using Helpers;
using global::OxyPlot;
using global::OxyPlot.Axes;
using global::OxyPlot.Series;
using PracticeDashboard.DAL.Models.SalesPipeline;
using Money = System.Decimal;
using System.Diagnostics;
using PracticeDashboard.Models;
using global::OxyPlot.Annotations;
using Xamarin.Forms;
public class SalesPipelineNormalizedPlotBuilder
{
// http://docs.oxyplot.org/en/latest/guidelines/design.html
// oxyplot guidelines
//
private readonly SalesPipelinePlotTheme currentTheme = SalesPipelinePlotTheme.DefaultTheme();
private SalesPipelineChartModel pipelineData;
private SalesPipelineModelNormalizationHelper normalizedData;
private RectangleAnnotation selectedArea;
private CategoryAxis categoryAxisX;
public SalesPipelineNormalizedPlotBuilder()
{
}
#region ISalesPipelinePlotBuilder
public PlotModel ResultModel { get; private set; }
public SalesPipelineNormalizedPlotBuilder WithReport(SalesPipelineChartModel pipelineData)
{
Debug.Assert(null == this.pipelineData);
this.pipelineData = pipelineData;
this.GenerateNormalizedData();
return this;
}
public SalesPipelineNormalizedPlotBuilder Build()
{
Debug.Assert(null != this.pipelineData);
this.ImplBuildPlot();
Debug.Assert(null != this.ResultModel);
return this;
}
#endregion ISalesPipelinePlotBuilder
#region Logic
private void ImplBuildPlot()
{
PlotModel result = new PlotModel();
this.AddAxisForBarsToPlot(result);
this.AddVerticalAxisToPlot(result);
this.AddCountLineToPlot(result);
this.AddAmountLineSeriesToPlot(result);
this.AddCountBarsToPlot(result);
this.AddSelectionEventHandlersToPlot(result);
this.ResultModel = result;
}
private void GenerateNormalizedData()
{
Debug.Assert(null != this.pipelineData);
this.normalizedData = new SalesPipelineModelNormalizationHelper();
this.normalizedData.Normalize(
salesPipelineDataset: this.pipelineData,
majorTickCount: this.currentTheme.numberOfSquaresInGrid);
}
#endregion logic
#region series
/**
*
* Adds "total count" data as it is.
*
*/
private void AddCountLineToPlot(PlotModel result)
{
var dataset = this.pipelineData.ValuesByMonth;
// Using point index as X value because of CategoryAxis
// https://github.com/oxyplot/oxyplot/issues/1187
//
Func<SalesPipelineDataPoint, DataPoint> dataPointBuilder =
point =>
new DataPoint(
//x: DateTimeAxis.ToDouble(point.yearAndMonth),
x: dataset.IndexOf(point),
y: Convert.ToDouble(point.pendingOpportunitiesTotalCount)
);
List<DataPoint> points =
dataset.Select(dataPointBuilder)
.ToList();
LineSeries lineSeries = PlotHelpers.GetLineSeries(
data: points,
color: this.currentTheme.salesCountLineColor,
markerSize: PlotHelpers.DefaultMarkerSize,
thickness: PlotHelpers.DefaultThickness,
filled: false);
result.Series.Add(lineSeries);
}
/**
*
* Adds data of "amounts" normalized to "count space"
*
*
*/
private void AddAmountLineSeriesToPlot(PlotModel result)
{
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth;
// Using point index as X value because of CategoryAxis
// https://github.com/oxyplot/oxyplot/issues/1187
//
Func<SalesPipelineDataPointNormalized, DataPoint> dataPointBuilder =
point =>
new DataPoint(
// x: DateTimeAxis.ToDouble(point.yearAndMonth),
x: dataset.IndexOf(point),
y: point.totalBudgetAmountForPendingOpportunities
);
List<DataPoint> points = dataset.Select(dataPointBuilder)
.ToList();
LineSeries lineSeries = PlotHelpers.GetLineSeries(
data: points,
color: this.currentTheme.amountsLineColor,
markerSize: PlotHelpers.DefaultMarkerSize,
thickness: PlotHelpers.DefaultThickness,
filled: false);
result.Series.Add(lineSeries);
}
#endregion series
#region Bars
/**
*
* Adds tripple bar "count" data as it is.
*
*/
private void AddCountBarsToPlot(PlotModel result)
{
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth;
// ==
//
var startedBarPlotData =
dataset.Select(
salesPipelineDataPoint =>
new OxyColumnViewModel
{
Date = salesPipelineDataPoint.yearAndMonth,
Value = salesPipelineDataPoint.numberOfOpportunitiesStartedThisMonth,
Color = this.currentTheme.newCountBarColor
});
var startedBarSeries =
new ColumnSeries
{
StrokeColor = OxyColors.Transparent,
StrokeThickness = 0,
ColumnWidth = 20, // screen points - ?
ItemsSource = startedBarPlotData,
ValueField = "Value",
ColorField = "Color"
};
// ==
//
var wonBarPlotData =
dataset.Select(
salesPipelineDataPoint =>
new OxyColumnViewModel
{
Date = salesPipelineDataPoint.yearAndMonth,
Value = salesPipelineDataPoint.numberOfOpportunitiesWonThisMonth,
Color = this.currentTheme.wonCountBarColor
});
var wonBarSeries =
new ColumnSeries
{
StrokeColor = OxyColors.Transparent,
StrokeThickness = 0,
ColumnWidth = 20, // screen points - ?
ItemsSource = wonBarPlotData,
ValueField = "Value",
ColorField = "Color"
};
// ==
//
var lostBarPlotData =
dataset.Select(
salesPipelineDataPoint =>
new OxyColumnViewModel
{
Date = salesPipelineDataPoint.yearAndMonth,
Value = salesPipelineDataPoint.numberOfOpportunitiesLostThisMonth,
Color = this.currentTheme.lostCountBarColor
});
var lostBarSeries =
new ColumnSeries
{
StrokeColor = OxyColors.Transparent,
StrokeThickness = 0,
ColumnWidth = 20, // screen points - ?
ItemsSource = lostBarPlotData,
ValueField = "Value",
ColorField = "Color"
};
result.Series.Add(startedBarSeries);
result.Series.Add(wonBarSeries);
result.Series.Add(lostBarSeries);
}
#endregion Bars
#region Axes
/**
*
* Expands range to
*
* ```
* max(totalCount) + normalized( max(amount) )
* ```
*
*/
private void AddVerticalAxisToPlot(PlotModel result)
{
int countMaxValue = this.normalizedData.countMax;
double fCountMaxValue = this.normalizedData.fCountMax;
double emulatedAmountMaxValue = this.normalizedData.emulatedAmountMax;
// TODO: maybe we should round it to some "beautiful" value
// like "5 / 10 / ..."
double fNumberOfSquaresInGrid = Convert.ToDouble(this.currentTheme.numberOfSquaresInGrid);
double step = this.normalizedData.countStep;
double maxValue = Math.Max(fCountMaxValue, emulatedAmountMaxValue) + step;
var verticalAxis =
PlotHelpers.GetLinearAxis(
minimum: 0,
maximum: maxValue,
textColor: this.currentTheme.axisColor,
gridStep: step,
position: AxisPosition.Right,
moneyValues: false);
var previousFormatter = verticalAxis.LabelFormatter;
verticalAxis.LabelFormatter =
(double arg) =>
{
string currentLabel = previousFormatter(arg);
double precision = 0.001;
bool isZeroAmount =
Math.Abs(arg - this.normalizedData.emulatedAmountZeroOffset) <= precision;
bool isCountLabel =
(arg < this.normalizedData.emulatedAmountZeroOffset);
if (isZeroAmount)
{
return "$0";
}
else if (isCountLabel)
{
string lambdaResult = currentLabel;
return lambdaResult;
}
else
{
double recoveredAmount =
this.normalizedData.ConvertEmulatedAmountToOriginalScale(arg);
double millionsCount =
recoveredAmount / 1000000;
string strMillions = millionsCount.ToString("N1");
string lambdaResult = $"${strMillions}M";
return lambdaResult;
}
};
this.ConfigureGridForVerticalAxis(verticalAxis);
result.Axes.Add(verticalAxis);
}
private void AddAxisForBarsToPlot(PlotModel result)
{
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth;
// CategoryAxis is forced by OxyPlot if ColumnSeries are used
// otherwise an exception is thrown
//
var dates =
dataset.Select(
salesPipelineDataPoint => salesPipelineDataPoint.yearAndMonth
);
double lastMonthCategory = dataset.Count();
// according to the design doc
//
double eightMonthsBefore = lastMonthCategory - 8;
// to make the chart look better on
// 1. UWP desktop
// 2. iPad / Android Tablets
//
double oneYearBefore = lastMonthCategory - 13.5;
var columnAxisX = new CategoryAxis
{
Position = AxisPosition.Bottom,
ItemsSource = dates,
StringFormat = "MMM", // "MMM yy" might be more usable until year restriction is introduced
TicklineColor = OxyColors.Transparent,
TextColor = this.currentTheme.axisColor,
IsZoomEnabled = false,
IsPanEnabled = true,
// To avoid render collapse
// https://github.com/oxyplot/oxyplot/issues/1187
//
AbsoluteMinimum = -1, // dataset units
AbsoluteMaximum = dates.Count(), // dataset units
};
bool isSmallScreenSize = (Device.Idiom == TargetIdiom.Phone);
// TODO: improve detection.
// 1. maybe take page orientation into account
// 2. Subscribe to orientation change events
if (isSmallScreenSize)
{
columnAxisX.Minimum = eightMonthsBefore; // dataset units
columnAxisX.Maximum = lastMonthCategory; // dataset units
}
else
{
columnAxisX.Minimum = oneYearBefore; // dataset units
columnAxisX.Maximum = lastMonthCategory; // dataset units
}
this.categoryAxisX = columnAxisX;
this.ConfigureGridForTimeAxis(columnAxisX);
result.Axes.Add(columnAxisX);
}
#endregion Axes
#region Grid
private void ConfigureGridForVerticalAxis(Axis verticalAxis)
{
verticalAxis.MajorGridlineStyle = LineStyle.Solid;
verticalAxis.MajorGridlineThickness = 1;
var blackColor = OxyColor.FromRgb(r: 0, g: 0, b: 0);
verticalAxis.MajorGridlineColor = blackColor;
}
private void ConfigureGridForTimeAxis(Axis timeAxis)
{
timeAxis.MajorGridlineStyle = LineStyle.Dash;
timeAxis.MajorGridlineThickness = 1;
var blackColor = OxyColor.FromRgb(r: 0, g: 0, b: 0);
timeAxis.MajorGridlineColor = blackColor;
}
#endregion Grid
#region Selection
private void AddSelectionEventHandlersToPlot(PlotModel result)
{
this.selectedArea = new RectangleAnnotation
{
Fill = this.currentTheme.selectedAreaColor,
MinimumX = 0,
MaximumX = 0
};
result.Annotations.Add(this.selectedArea);
// TODO: should we unsubscribe explicitly?
//
result.MouseUp += PlotMouseUpHandler;
result.TouchCompleted += PlotGestureHandler;
}
private void PlotGestureHandler(
object sender,
OxyTouchEventArgs firedEvent)
{
// TODO: maybe we'll have to filter "tap" / "touchUpInside" gestures
//
this.OnPointSelected(firedEvent.Position);
firedEvent.Handled = true;
}
private void PlotMouseUpHandler(
object sender,
OxyMouseEventArgs firedEvent)
{
this.OnPointSelected(firedEvent.Position);
firedEvent.Handled = true;
}
private void ClearSelection()
{
this.selectedArea.MinimumX = 0;
this.selectedArea.MaximumX = 0;
}
private void OnPointSelected(global::OxyPlot.ScreenPoint screenCoordinates)
{
var dataset = this.normalizedData.emulatedAmountsDataset.ValuesByMonth;
CategoryAxis axis = this.categoryAxisX;
DataPoint selectedPoint = this.selectedArea.InverseTransform(screenCoordinates);
// We need it because integer value of X is "category middle"
// This is a voodoo magic of `OxyPlot.CategoryAxis`
//
double halfColumnBias = 0.5;
double rawSelectedCategoryIndex = Math.Floor(selectedPoint.X + halfColumnBias);
int selectedCategoryIndex = Convert.ToInt32(rawSelectedCategoryIndex);
Debug.Assert(selectedCategoryIndex >= 0);
Debug.Assert(selectedCategoryIndex <= dataset.Count());
bool isSelectionAreaEmpty = (Math.Abs(this.selectedArea.MaximumX - this.selectedArea.MinimumX) <= 0.001);
bool isSomeCategorySelected = !isSelectionAreaEmpty;
bool isUnselecting = false;
if (isSomeCategorySelected)
{
double selectedAreaMid = (this.selectedArea.MaximumX + this.selectedArea.MinimumX) / 2;
double rawPreselectedCategoryIndex = selectedAreaMid;
int preselectedCategoryIndex = Convert.ToInt32(rawPreselectedCategoryIndex);
Debug.Assert(preselectedCategoryIndex >= 0);
Debug.Assert(preselectedCategoryIndex <= dataset.Count());
bool isPreselectedColumnTapped = (preselectedCategoryIndex == selectedCategoryIndex);
isUnselecting = isPreselectedColumnTapped;
}
if (isUnselecting)
{
this.selectedArea.MinimumX = 0;
this.selectedArea.MaximumX = 0;
}
else
{
double thisCategoryStart = selectedCategoryIndex - halfColumnBias;
double thisCategoryEnd = rawSelectedCategoryIndex + halfColumnBias;
this.selectedArea.MinimumX = thisCategoryStart;
this.selectedArea.MaximumX = thisCategoryEnd;
}
this.ResultModel.InvalidatePlot(updateData: true);
}
#endregion Selection
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A solution for