/// <summary> /// Grid coordinates: /// x: "normalized" [0..1] and scaled to the area-width /// y: "axis" scale /// </summary> /// <param name="icrc"></param> void IRequireRender.Render(IChartRenderContext icrc) { if (ValueAxis == null) { return; } if (double.IsNaN(ValueAxis.Maximum) || double.IsNaN(ValueAxis.Minimum)) { return; } // grid lines var tc = new TickCalculator(ValueAxis.Minimum, ValueAxis.Maximum); var recycler = new Recycler <Path, ItemState>(GridLines.Where(tl => tl.Element != null).Select(tl => tl.Element), (ist) => { return(CreateElement(ist)); }); var mrecycler = new Recycler <Path, ItemState>(MinorGridLines.Where(tl => tl.Element != null).Select(tl => tl.Element), (ist) => { return(CreateSubElement(ist)); }); var itemstate = new List <ItemState>(); var mitemstate = new List <ItemState>(); //_trace.Verbose($"grid range:{tc.Range} tintv:{tc.TickInterval}"); var tixarray = tc.GetTicks().ToArray(); var sc = new ValueAxisSelectorContext(ValueAxis, icrc.SeriesArea, tixarray, tc.TickInterval); for (int ix = 0; ix < tixarray.Length; ix++) { var tick = tixarray[ix]; //_trace.Verbose($"grid vx:{tick}"); var state = new ItemState() { Tick = tick }; var current = recycler.Next(state); if (!current.Item1) { // restore binding if (PathStyle != null) { BindTo(this, nameof(PathStyle), current.Item2, FrameworkElement.StyleProperty); } } state.Element = current.Item2; ApplyBinding(this, nameof(Visibility), state.Element, UIElement.VisibilityProperty); if (PathFormatter != null) { sc.SetTick(ix); // call for Style, String override var format = PathFormatter.Convert(sc, typeof(Tuple <Style, String>), null, System.Globalization.CultureInfo.CurrentUICulture.Name); if (format is Tuple <Style, String> ovx) { if (ovx.Item1 != null) { current.Item2.Style = ovx.Item1; } } } state.SetLocation(icrc.Area.Left, tick.Value); sc.Generated(tick); itemstate.Add(state); if (MinorGridLineCount != 0) { // lay out minor divisions var mintv = tc.TickInterval / (double)(MinorGridLineCount + 1); var diry = Math.Sign(tick.Index); if (diry == 0) { // special case: zero for (int mx = 1; mx <= MinorGridLineCount; mx++) { var mtick = new SubtickState(tick.Index, mx, tick.Value + (double)mx * mintv); var mstate = CreateSubtick(icrc, mrecycler, mtick); if (mstate != null) { mitemstate.Add(mstate); } var mtick2 = new SubtickState(tick.Index, -mx, tick.Value + (double)(-mx) * mintv); var mstate2 = CreateSubtick(icrc, mrecycler, mtick2); if (mstate2 != null) { mitemstate.Add(mstate2); } } } else { for (int mx = 1; mx <= MinorGridLineCount; mx++) { var mtick = new SubtickState(tick.Index, diry * mx, tick.Value + (double)(diry * mx) * mintv); var mstate = CreateSubtick(icrc, mrecycler, mtick); if (mstate != null) { mitemstate.Add(mstate); } } } } } // VT and internal bookkeeping MinorGridLines = mitemstate; GridLines = itemstate; Layer.Remove(mrecycler.Unused); Layer.Remove(recycler.Unused); Layer.Add(recycler.Created); Layer.Add(mrecycler.Created); Dirty = false; }
ItemState CreateSubtick(IChartRenderContext icrc, Recycler <Path, ItemState> recycler, SubtickState tick) { if (tick.Value <= ValueAxis.Minimum || tick.Value >= ValueAxis.Maximum) { return(null); } var state = new ItemState() { Tick = tick }; var current = recycler.Next(state); if (!current.Item1) { // restore binding if (MinorGridPathStyle != null) { BindTo(this, nameof(MinorGridPathStyle), current.Item2, FrameworkElement.StyleProperty); TransferFromStyle(current.Item2, MinorGridPathStyle); } else if (PathStyle != null) { BindTo(this, nameof(PathStyle), current.Item2, FrameworkElement.StyleProperty); TransferFromStyle(current.Item2, PathStyle); } } state.Element = current.Item2; ApplyBinding(this, nameof(Visibility), state.Element, UIElement.VisibilityProperty); state.SetLocation(icrc.Area.Left, tick.Value); return(state); }