/// <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;
 }