/// <summary> /// Use the <see cref="bx"/> evaluator to return the x-axis value, or index if it is NULL. /// </summary> /// <param name="ox">Object to evaluate.</param> /// <param name="index">Index value if <see cref="bx"/> is NULL.</param> /// <returns></returns> public double CategoryFor(object ox, int index) { // TODO this is coming back int from model object var valuex = bx != null ? (double)bx.For(ox) : index; return(valuex); }
/// <summary> /// Core element processing. /// The <see cref="RectangleGeometry"/> inside the <see cref="Path"/> is now location-invariant wrt x-axis. /// This means that during incremental updates, no re-calculation is required, only adjusting the <see cref="Canvas.LeftProperty"/>. /// </summary> /// <param name="index">Data index.</param> /// <param name="valuec1">C1 (x-axis) index.</param> /// <param name="valuec2">C2 (y-axis) index.</param> /// <param name="value">Cell value.</param> /// <param name="item"></param> /// <param name="recycler"></param> /// <param name="byl"></param> /// <returns></returns> ItemState <Path> ElementPipeline(int index, int valuec1, int valuec2, double value, object item, Recycler <Path, ItemState <Path> > recycler, BindingEvaluator byl) { var path = recycler.Next(null); if (path == null) { return(null); } var shim = new GeometryWith2OffsetShim <RectangleGeometry>() { // MUST use invariant geometry here so it can translate. PathData = new RectangleGeometry() { Rect = new Rect(new Point(0, 0), new Point(1, 1)) } }; path.Item2.DataContext = shim; // connect the shim to template root element's Visibility BindTo(shim, nameof(shim.Visibility), path.Item2, UIElement.VisibilityProperty); BindTo(shim, nameof(shim.Offset), path.Item2, Canvas.LeftProperty); BindTo(shim, nameof(shim.Offset2), path.Item2, Canvas.TopProperty); if (byl == null) { return(new SeriesItemState_Double(index, valuec1, 0, valuec2, value, path.Item2)); } else { var cs = byl.For(item); return(new SeriesItemState_Custom(index, valuec1, 0, valuec2, value, cs, path.Item2)); } }
/// <summary> /// Take the actual value from the source and coerce it to the double type, until we get full polymorphism on the y-value. /// <para/> /// Currently handles <see cref="double"/>, <see cref="int"/>, <see cref="short"/>,and Nullable{double,int,short} types. /// </summary> /// <param name="item">Source instance.</param> /// <param name="be">Evaluator or NULL. If NULL returns <see cref="Double.NaN"/>.</param> /// <returns>Coerced value or THROWs.</returns> public static double CoerceValue(object item, BindingEvaluator be) { if (be == null) { return(double.NaN); } var ox = be.For(item); if (ox is short sx) { return((double)sx); } if (ox is int ix) { return((double)ix); } if (ox is long lx) { return((double)lx); } if (ox is DateTime dt) { return(dt == default(DateTime) ? double.NaN : (double)dt.Ticks); } // now nullable types if (ox is double?) { double?ddx = (double?)ox; return(ddx ?? double.NaN); } if (ox is int?) { int?ddx = (int?)ox; return(ddx ?? double.NaN); } if (ox is short?) { short?ddx = (short?)ox; return(ddx ?? double.NaN); } return((double)ox); }
void IRequireDataSourceUpdates.Add(IChartRenderContext icrc, int startAt, IList items) { // mimic the DSRP sequence var widx = LabelStyle?.Find(FrameworkElement.WidthProperty); var bl = new BindingEvaluator(LabelPath); // keep a separate list; easier at the end var reproc = new List <ItemState>(); for (int ix = 0; ix < items.Count; ix++) { // add requested item var label = bl.For(items[ix]); var istate = makeit(startAt + ix, label, widx == null); AxisLabels.Insert(startAt + ix, istate); reproc.Add(istate); } // re-sequence remaining items for (int ix = startAt + reproc.Count; ix < AxisLabels.Count; ix++) { AxisLabels[ix].index = ix; AxisLabels[ix].value = ix; } // render new items // run the element pipeline on the added items var recycler = new Recycler <FrameworkElement, ItemState>(CreateElement); var labels = new List <ICategoryLabelState>(AxisLabels); var sc = new SelectorContext(this, icrc.SeriesArea, labels); foreach (var istate in reproc) { ElementPipeline(sc, istate, recycler); } // configure axis limits; just based on count-of-elements UpdateLimits(0); UpdateLimits(AxisLabels.Count); // finish up Layer.Add(recycler.Created); RebuildAxisGeometry(); Dirty = false; }
/// <summary> /// Core element processing. /// The <see cref="RectangleGeometry"/> inside the <see cref="Path"/> is now location-invariant wrt x-axis. /// This means that during incremental updates, no re-calculation is required, only adjusting the <see cref="Canvas.LeftProperty"/>. /// </summary> /// <param name="index"></param> /// <param name="valuex"></param> /// <param name="valuey"></param> /// <param name="item"></param> /// <param name="recycler"></param> /// <param name="byl"></param> /// <returns></returns> ItemState <Path> ElementPipeline(int index, double valuex, double valuey, object item, Recycler <Path, ItemState <Path> > recycler, BindingEvaluator byl) { var y1 = ValueAxis.For(valuey); var y2 = ValueAxis.For(0); var topy = Math.Max(y1, y2); var bottomy = Math.Min(y1, y2); var leftx = CategoryAxis.For(valuex); var barx = BarOffset; var rightx = barx + BarWidth; _trace.Verbose($"{Name}[{index}] {valuey} ({barx},{topy}) ({rightx},{bottomy})"); var path = recycler.Next(null); if (path == null) { return(null); } var shim = new GeometryWithOffsetShim <RectangleGeometry>() { PathData = new RectangleGeometry() { Rect = new Rect(new Point(barx, topy), new Point(rightx, bottomy)) } }; path.Item2.DataContext = shim; // connect the shim to template root element's Visibility BindTo(shim, nameof(shim.Visibility), path.Item2, UIElement.VisibilityProperty); BindTo(shim, nameof(shim.Offset), path.Item2, Canvas.LeftProperty); if (byl == null) { return(new SeriesItemState_Double(index, leftx, BarOffset, y1, path.Item2)); } else { var cs = byl.For(item); return(new SeriesItemState_Custom(index, leftx, BarOffset, y1, cs, path.Item2)); } }
/// <summary> /// Force (non-NULL) results to be a <see cref="String"/>. /// <para/> /// If <see cref="String.Empty"/>, <see cref="byl"/> was NULL. If NULL, the evaluation result was NULL. /// </summary> /// <param name="item">Source instance.</param> /// <returns>Evaluated string, NULL, or <see cref="String.Empty"/>.</returns> public String LabelStringFor(object item) { return(byl != null?byl.For(item)?.ToString() : String.Empty); }
/// <summary> /// Evaluate the label or NULL. /// <para/> /// If NULL, either <see cref="byl"/> was NULL, OR the evaluation result was NULL. /// </summary> /// <param name="item">Source instance.</param> /// <returns>Evaluated value or NULL.</returns> public object LabelFor(object item) { return(byl?.For(item)); }
public int Category2For(object ox) { var valuex = (int)bc2.For(ox); return(valuex); }
ItemState <Path> ElementPipeline( int index, double valuex, double valueO, double valueH, double valueL, double valueC, object item, Recycler <Path, ItemState <Path> > recycler, BindingEvaluator bvl ) { // map through axes var y1 = ValueAxis.For(valueO); var y2 = ValueAxis.For(valueC); var y3 = ValueAxis.For(valueH); var y4 = ValueAxis.For(valueL); var leftx = CategoryAxis.For(valuex); var offsetx = BarOffset; var rightx = offsetx + BarWidth; // force them to be a min/max var topy = Math.Max(y1, y2); var bottomy = Math.Min(y1, y2); var highy = Math.Max(y3, y4); var lowy = Math.Min(y3, y4); _trace.Verbose($"{Name}[{index}] {valueO}/{valueH}/{valueL}/{valueC} ({offsetx},{topy}) ({rightx},{bottomy})"); // create geometry var path = recycler.Next(null); if (path == null) { return(null); } var pg = new PathGeometry(); // body (open/close) var body = PathHelper.Rectangle(offsetx, topy, rightx, bottomy); pg.Figures.Add(body); // upper shadow (high) var centerx = offsetx + (rightx - offsetx) / 2; var upper = PathHelper.Line(centerx, topy, centerx, highy); pg.Figures.Add(upper); // lower shadow (low) var lower = PathHelper.Line(centerx, bottomy, centerx, lowy); pg.Figures.Add(lower); var shim = new GeometryWithOffsetShim <PathGeometry>() { PathData = pg }; path.Item2.DataContext = shim; // establish the style for "forward" or "reverse" polarity BindTo(this, valueO < valueC ? nameof(PathStyle) : nameof(ReversePathStyle), path.Item2, Path.StyleProperty); // bind offset BindTo(shim, nameof(shim.Offset), path.Item2, Canvas.LeftProperty); var figs = new Tuple <double, PathFigure> [4]; figs[0] = new Tuple <double, PathFigure>(y1, body); figs[1] = new Tuple <double, PathFigure>(y2, body); figs[2] = new Tuple <double, PathFigure>(y3, upper); figs[3] = new Tuple <double, PathFigure>(y4, lower); if (bvl == null) { return(new SeriesItemState(index, leftx, BarOffset, y1, path.Item2, figs)); } else { var cs = bvl.For(item); return(new SeriesItemState_Custom(index, leftx, BarOffset, y1, cs, path.Item2, figs)); } }
/// <summary> /// Use the <see cref="bx"/> evaluator to return the x-axis value, or index if it is NULL. /// </summary> /// <param name="ox">Object to evaluate.</param> /// <param name="index">Index value if <see cref="bx"/> is NULL.</param> /// <returns></returns> public double CategoryFor(object ox, int index) { var valuex = bx != null ? (double)bx.For(ox) : index; return(valuex); }