/// <summary> /// Enter list if passing checks. /// </summary> /// <param name="ex">Candidate.</param> public override void Enter(ChartComponent ex) { if (ex is T tx && enter(ex)) { items.Add(tx); } }
/// <summary> /// Common logic for entering the chart. /// </summary> /// <param name="icelc">The context.</param> /// <param name="cc">The component entering chart.</param> protected void ComponentEnter(IChartEnterLeaveContext icelc, ChartComponent cc) { // pre-load resources if (cc is IRequireChartTheme irct) { if (Theme == null) { Report(new ChartValidationResult("Chart", $"The {nameof(Theme)} property is NULL, chart elements may not be visible", new[] { cc.NameOrType(), nameof(Theme) })); } else { irct.Theme = Theme; } } // invoke IREL if (cc is IRequireEnterLeave irel) { irel.Enter(icelc); } // for now anything can provide legend items if (cc is IProvideLegend ipl) { foreach (var li in ipl.LegendItems) { LegendItems.Add(li); } } if (cc is IProvideLegendDynamic ipld) { // attach the event ipld.LegendChanged -= Ipld_LegendChanged; ipld.LegendChanged += Ipld_LegendChanged; } // axis and DSRP are mutually-exclusive if (cc is IChartAxis ica) { Axes.Add(ica); } if (cc is IProvideDataSourceRenderer ipdsr) { Register(ipdsr.Renderer); } else if (cc is IDataSourceRenderer idsr) { Register(idsr); } foreach (var px in AllPhases) { px.Enter(cc); } }
/// <summary> /// Component is requesting a refresh. /// Mark the chart's data source dirty and render chart. /// TODO get the DS to just refresh this CC. /// This method is invoke-safe; it MAY be called from a different thread. /// </summary> /// <param name="cc">Component requesting refresh.</param> /// <param name="rrea">The request parameter.</param> private async void ChartComponent_RefreshRequest(ChartComponent cc, RefreshRequestEventArgs rrea) { _trace.Verbose($"refresh-request-cc '{cc.Name}' {cc} r:{rrea.Request} a:{rrea.Axis}"); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if (Surface == null) { return; } try { if (cc is IProvideDataSourceRenderer ipdsr) { var ds = DataSources.SingleOrDefault(dds => dds.Name == ipdsr.Renderer.DataSourceName); if (ds != null) { ds.IsDirty = true; } RenderComponents(CurrentLayout); } else if (cc is IDataSourceRenderer idsr) { var ds = DataSources.SingleOrDefault(dds => dds.Name == idsr.DataSourceName); if (ds != null) { ds.IsDirty = true; } RenderComponents(CurrentLayout); } else { // dispatch other kinds of refresh requests if (rrea.Request == RefreshRequestType.LayoutDirty && rrea.Component is IRequireLayout) { ComponentRender(CurrentLayout, rrea); } else if (rrea.Request == RefreshRequestType.ValueDirty && rrea.Component is IRequireRender) { ComponentRender(CurrentLayout, rrea); } else if (rrea.Request == RefreshRequestType.TransformsDirty && cc is IRequireTransforms) { ComponentTransforms(CurrentLayout, rrea); } } } catch (Exception ex) { _trace.Verbose($"{Name} ChartComponent_RefreshRequest.unhandled: {ex}"); } }); }
/// <summary> /// Provide a render context for given component. /// Created contexts are cached until <see cref="InitializeLayoutContext"/> is called. /// <para/> /// Sets the <see cref="DefaultRenderContext.Type"/> to the current value of <see cref="Type"/>. /// </summary> /// <param name="cc">Component to provide context for.</param> /// <param name="surf">For ctor.</param> /// <param name="ccs">For ctor.</param> /// <param name="dc">For ctor.</param> /// <returns>New or cached instance.</returns> public DefaultRenderContext RenderFor(ChartComponent cc, Canvas surf, ObservableCollection <ChartComponent> ccs, object dc) { if (rendercache.ContainsKey(cc)) { var rc = rendercache[cc]; rc.Type = Type; return(rc); } var rect = Layout.For(cc); var drc = new DefaultRenderContext(surf, ccs, LayoutDimensions, rect, Layout.RemainingRect, dc); rendercache.Add(cc, drc); drc.Type = Type; return(drc); }
/// <summary> /// Manage dynamic legend updates. /// </summary> /// <param name="sender">Component sending update.</param> /// <param name="ldea">Current state of legend.</param> private void Ipld_LegendChanged(ChartComponent sender, LegendDynamicEventArgs ldea) { foreach (var prev in ldea.PreviousItems) { if (!ldea.CurrentItems.Contains(prev)) { LegendItems.Remove(prev); } } foreach (var curr in ldea.CurrentItems) { if (!LegendItems.Contains(curr)) { LegendItems.Add(curr); } } }
/// <summary> /// Common logic for leaving the chart. /// SHOULD be strict dual of ComponentEnter sequence. /// </summary> /// <param name="icelc">The context.</param> /// <param name="cc">The component leaving chart.</param> protected void ComponentLeave(IChartEnterLeaveContext icelc, ChartComponent cc) { foreach (var px in AllPhases) { px.Leave(cc); } if (cc is IProvideDataSourceRenderer ipdsr) { Unregister(ipdsr.Renderer); } else if (cc is IDataSourceRenderer idsr) { Unregister(idsr); } if (cc is IChartAxis ica) { Axes.Remove(ica); } if (cc is IProvideLegendDynamic ipld) { // detach the event ipld.LegendChanged -= Ipld_LegendChanged; } if (cc is IProvideLegend ipl) { foreach (var li in ipl.LegendItems) { LegendItems.Remove(li); } } if (cc is IRequireEnterLeave irel) { irel.Leave(icelc); } if (cc is IRequireChartTheme irct) { irct.Theme = null; } }
/// <summary> /// Claim the indicated space for given component, subject to the quota. /// </summary> /// <param name="cc">The requestor.</param> /// <param name="sd">Requested side of layout.</param> /// <param name="amt">Amount of space in PX.</param> /// <returns>The actual bounds allocated.</returns> public Rect ClaimSpace(ChartComponent cc, Side sd, double amt) { var ul = new Point(); var sz = new Size(); switch (sd) { case Side.Top: sz.Width = Dimensions.Width; sz.Height = Math.Min(amt, Dimensions.Height * Quota.Height); ul.X = RemainingRect.Left; ul.Y = RemainingRect.Top; RemainingRect = new Rect(RemainingRect.Left, RemainingRect.Top + sz.Height, RemainingRect.Width, RemainingRect.Height - sz.Height); break; case Side.Right: sz.Width = Math.Min(amt, Dimensions.Width * Quota.Width); sz.Height = Dimensions.Height; ul.X = RemainingRect.Right - sz.Width; ul.Y = RemainingRect.Top; RemainingRect = new Rect(RemainingRect.Left, RemainingRect.Top, RemainingRect.Width - sz.Width, RemainingRect.Height); break; case Side.Bottom: sz.Width = Dimensions.Width; sz.Height = Math.Min(amt, Dimensions.Height * Quota.Height); ul.X = RemainingRect.Left; ul.Y = RemainingRect.Bottom - sz.Height; RemainingRect = new Rect(RemainingRect.Left, RemainingRect.Top, RemainingRect.Width, RemainingRect.Height - sz.Height); break; case Side.Left: sz.Width = Math.Min(amt, Dimensions.Width * Quota.Width); sz.Height = Dimensions.Height; ul.X = RemainingRect.Left; ul.Y = RemainingRect.Top; RemainingRect = new Rect(RemainingRect.Left + sz.Width, RemainingRect.Top, RemainingRect.Width - sz.Width, RemainingRect.Height); break; } var rect = new Rect(ul, sz); ClaimedRects.Add(cc, rect); return rect; }
/// <summary> /// Return the rect mapped to this component, else RemainingRect. /// </summary> /// <param name="cc"></param> /// <returns></returns> public Rect For(ChartComponent cc) { return ClaimedRects.ContainsKey(cc) ? ClaimedRects[cc] : RemainingRect; }
/// <summary> /// Remove element. /// </summary> /// <param name="ex">Candidate.</param> public override void Leave(ChartComponent ex) { items.Remove(ex as T); }
/// <summary> /// Remove element. /// </summary> /// <param name="cc">Candidate.</param> public abstract void Leave(ChartComponent cc);
/// <summary> /// Enter list if passing checks. /// </summary> /// <param name="cc">Candidate.</param> public abstract void Enter(ChartComponent cc);
/// <summary> /// Initialize. /// </summary> /// <param name="rrt">The request type.</param> /// <param name="aus">The axis update info.</param> /// <param name="cc">The component.</param> public RefreshRequestEventArgs(RefreshRequestType rrt, AxisUpdateState aus, ChartComponent cc) { this.Request = rrt; this.Component = cc; this.Axis = aus; }