XamDataChart の積層系列(StackedBarSeries/StackedColumnSeries)で、各フラグメント(セグメント)の矩形中央にデータ値を表示するには、StackedFragmentSeries の MarkerTemplate プロパティに、値に応じて Margin を計算するコンバーターをバインドした DataTemplate を設定します。
XamDataChart の設定
まず、積層系列(ここでは垂直方向の StackedColumnSeries を例に説明します)を構成します。各データ系列(Coal、Oil など)を StackedColumnSeries.Series 配下に StackedFragmentSeries として追加し、ValueMemberPath で対応するプロパティをバインドします。水平方向に積み上げる場合は同様の構成を StackedBarSeries で行います。
<ig:XamDataChart>
<ig:XamDataChart.Axes>
<ig:CategoryXAxis x:Name="xAxis" ItemsSource="{Binding}" Label="{}{Country}"/>
<ig:NumericYAxis x:Name="yAxis" MinimumValue="0"/>
</ig:XamDataChart.Axes>
<ig:XamDataChart.Series>
<ig:StackedColumnSeries XAxis="{Binding ElementName=xAxis}"
YAxis="{Binding ElementName=yAxis}"
ItemsSource="{Binding}">
<ig:StackedColumnSeries.Series>
<ig:StackedFragmentSeries x:Name="fragmentCoal" ValueMemberPath="Coal" Title="Coal" />
<ig:StackedFragmentSeries x:Name="fragmentOil" ValueMemberPath="Oil" Title="Oil" />
<!-- Gas, Nuclear, Hydro も同様に追加 -->
</ig:StackedColumnSeries.Series>
</ig:StackedColumnSeries>
</ig:XamDataChart.Series>
</ig:XamDataChart>
MarkerTemplate の設定
各 StackedFragmentSeries に MarkerTemplate を設定し、内部の TextBlock の Text にデータ値(Item.Coal など)を、Margin に位置計算用のコンバーターをそれぞれバインドします。
<ig:StackedFragmentSeries x:Name="fragmentCoal" ValueMemberPath="Coal" Title="Coal">
<ig:StackedFragmentSeries.MarkerTemplate>
<DataTemplate>
<TextBlock Text="{Binding Item.Coal}" Margin="{Binding Converter={StaticResource markerConverter}, ConverterParameter=Coal}"/>
</DataTemplate>
</ig:StackedFragmentSeries.MarkerTemplate>
</ig:StackedFragmentSeries>
ラベル位置を計算するコンバーターの実装
IValueConverter を実装し、対象フラグメントの値を軸の ScaleValue でピクセル座標に変換した差分から Margin(Thickness)を算出します。水平方向の BarFragment では XAxis、垂直方向の ColumnFragment では YAxis の ScaleValue を使い分けることで、ラベルが各フラグメントの矩形内の適切な位置に表示されます。
public class SeriesMarkerConverter : IValueConverter
{
private static readonly Dictionary<string, Func<EnergyProduction, double>> _accessors
= new Dictionary<string, Func<EnergyProduction, double>>
{
{ "Coal", e => e.Coal },
// Oil, Gas, Nuclear, Hydro も同様に登録
};
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DataContext context &&
context.Item is EnergyProduction energyProduction &&
parameter is string propName &&
_accessors.TryGetValue(propName, out var accessor))
{
var val = accessor(energyProduction);
// 水平方向(StackedBarSeries): XAxisのスケール値から差分を計算
if (context.Series is BarFragment barFragment)
{
var xAxis = barFragment.XAxis;
var scaledValue = xAxis.ScaleValue(val);
var scaled0 = xAxis.ScaleValue(0);
var diff = Math.Abs(scaledValue - scaled0);
return new Thickness(0, 0, diff, 0);
}
// 垂直方向(StackedColumnSeries): YAxisのスケール値から差分を計算
if (context.Series is ColumnFragment fragment)
{
var yAxis = fragment.YAxis as NumericYAxis;
var scaledMin = yAxis?.ScaleValue(yAxis.ActualMinimumValue);
var scaledValue = yAxis?.ScaleValue(val);
if (scaledMin.HasValue && scaledValue.HasValue)
{
return new Thickness(0, scaledMin.Value - scaledValue.Value, 0, 0);
}
}
}
return new Thickness(0, 0, 0, 0);
}
}
RefreshCompleted イベントの処理
XamDataChart の RefreshCompleted イベントで、各 StackedFragmentSeries の MarkerTemplate を一度 null に戻してから元に戻すことで、バインディングを再評価させ、チャートの再描画後もラベルが正しく表示されるようにします。
private void XamDataChart_RefreshCompleted(object sender, EventArgs e)
{
if (_isRefreshing) return;
_isRefreshing = true;
ResetMarkerTemplate(fragmentCoal);
ResetMarkerTemplate(fragmentOil);
// Gas, Nuclear, Hydro も同様にリセット
Dispatcher.BeginInvoke(new Action(() => _isRefreshing = false),
System.Windows.Threading.DispatcherPriority.DataBind);
}
private void ResetMarkerTemplate(StackedFragmentSeries fragment)
{
var template = fragment.MarkerTemplate;
fragment.MarkerTemplate = null;
fragment.MarkerTemplate = template;
}
実行結果
上記の実装により、StackedBarSeries(水平)・StackedColumnSeries(垂直)のいずれの積み上げチャートでも、各フラグメントの矩形内にデータ値のラベルが表示され、ズームなどによるチャートの再描画後もラベルが正しく維持されます。
