追追追,找出 ChartEntityMetaData 到底在干甚么。

IChartEntity interface 的内容很简单,就两个属性而已。

public interface IChartEntity
{
    ChartEntityMetaData? MetaData { get; set; }
    
    Coordinate Coordinate { get; set; }
}

Coordinate 和座标相关这件事情比较好懂一点,让我有点不好理解的是 MetaData 这个属性,从上面的程式码内容可以看出来它的型别是 ChartEntityMetaData  。

ChartEntityMetaData class

这个类别有几个有趣的地方,第一个是 EntityIndex 属性,说明里面讲的是这个执行个体在集合中的位置,事实上这个变动会发生于渲染画面时期,并非是在加进集合的时候;第二个有趣的地方是它的建构式会传一个 Action<int> 委派,这个委派在类别内部被呼叫的时机是当 EntityIndex 属性值改变的时候。

 public int EntityIndex
 {
     get
     {
         return _entityIndex;
     }
     set
     {
         if (value != _entityIndex)
         {
             _entityIndex = value;
             _entityIndexChangedCallback?.Invoke(value);
         }
     }
 }

从程式码可以看出来当 EntityIndex 属性值改变会引发委派执行,并且将新的 index 值作为引数。

玩看看

我写一个测试范例想找出 EntityIndex 属性变动的时机,完整范例参考此处。以下列出主要的段落:

 public ICommand ClickCommand
 {
     get
     {
         return new RelayCommand(async x =>
         {
             People = new ObservableCollection<PersonViewModel>()
             {
                 new PersonViewModel { Name = "A", Score = 98 },
                 new PersonViewModel { Name = "B", Score = 68 },
                 new PersonViewModel { Name = "C", Score = 77 },
             };
             await Display(0);
             var Points = new ColumnSeries<PersonViewModel>();
             Points.Values = People;
             await Display(1);
             var Series = new ObservableCollection<ISeries>();
             Series.Add(Points);
             await Display(2);
             var XAxes = new ObservableCollection<Axis>();
             XAxes.Add(new Axis { Labels = new ObservableCollection<string>(People.Select(x => x.Name)) });
             await Display(3);
             var YAxes = new ObservableCollection<Axis>();
             YAxes.Add(new Axis { Labeler = value => $"{value} S", });
             await Display(4);
             var chart = new CartesianChart()
             {
                 Height = 150,
                 Series = Series,
                 XAxes = XAxes,
                 YAxes = YAxes,
                 Background = new SolidColorBrush(Colors.Yellow)
             };
             await Display(5);
             panel.Children.Add(chart);
             await Display(6);
             async Task Display(int testIndex)
             {
                 await Task.Delay(80);
                 Debug.WriteLine($"({testIndex}) {string.Join(",", People.Select(x => x.MetaData.EntityIndex))}");
             }
         });
     }
 }

之所以需要 Task Delay 是因为 UI Thread 和 Rendering Thread 是分开的,如果不 Delay 的话,渲染还没完成前可能就会跑到 Debug.WriteLine,这样就测不出结果了。

执行输出大致是以下这样,在 (6) 的时候已经渲染完成才会发现 EntityIndex 属性值改变了 :

(0) -1,-1,-1
(1) -1,-1,-1
(2) -1,-1,-1
(3) -1,-1,-1
(4) -1,-1,-1
(5) -1,-1,-1
(6) 0,1,2

前一个 Sample004 因为还没解说这个 MetaData 的内容,所以近乎硬干的在 PersonViewModel 里加入了一个 Index 属性,现在我们大致了解这东西怎么用了,下一篇应用这个特性来改造 Sample004。