Binding in nested XAML WPF not evaluated

146 views Asked by At

I have a nested WPF View where I pass Objects through to the inner layers via Dependency Properties.

My outer XAML layer passes data into RetentionChartControl.ChartParams:

<local:RetentionChartControl ChartParams="{Binding PeakRow.Chartparams[0], RelativeSource={RelativeSource AncestorType={x:Type local:PeakrowChartGroupControl}}}"></local:RetentionChartControl>

In my most inner layer I have the following XAML code:

<UserControl x:Class="DryLab.Peakmovement.RetentionChartControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DryLab.Peakmovement" 
             xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
             mc:Ignorable="d"
             x:Name="uc"
             d:DesignHeight="450" d:DesignWidth="800">
   
    
    <Grid>
        <lvc:CartesianChart x:Name="cc" Series="{Binding ChartParams.Seriescollection, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}}}" >
            <lvc:CartesianChart.DataTooltip>
                <lvc:DefaultTooltip SelectionMode="OnlySender" />
            </lvc:CartesianChart.DataTooltip>
            <lvc:CartesianChart.AxisY>
                <lvc:Axis  Foreground="Black" Title="{Binding ChartParams.TitleAxY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:RetentionChartControl}} }" LabelFormatter="{Binding ChartParams.Formatter, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}}}">
                </lvc:Axis>
            </lvc:CartesianChart.AxisY>
            <lvc:CartesianChart.AxisX>
                <lvc:Axis  Foreground="Black" Title="{Binding ChartParams.TitleAxX, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}}}" Labels="{Binding ChartParams.Labels, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}} }"></lvc:Axis>
            </lvc:CartesianChart.AxisX>
        </lvc:CartesianChart>
    </Grid>
</UserControl>

Now the Binding for <lvc:Cartesian Chart> (From LiveCharts https://lvcharts.net/App/examples/v1/wpf/Basic%20Line%20Chart) is working, the ChartParams Variable is fully evaluated and i can Access the Seriescollection.

However if i look at the deeper nested <lvc:Axis> (via LiveVisualTree) the Title Property is never evaluated.

What have i missed? Why cant i bind to ChartParams from within <lvc:Axis>?

Any help is appreciated, will post further code if necessary.

Edit: I should add, that when the application is running, and I touch my Axis.Title in Xaml, it reevaluates and then shows the results as espected

1

There are 1 answers

1
BionicCode On BEST ANSWER

The problem is, that the Axis element is not added to the visual tree the moment the bindings are resolved. All visible elements that are a child of the chart's underlying Canvas, are added later (long time after the chart control's FrameworkElement.Loaded event was raised). Therefore data bindings that bind e.g. Axis or Separator etc. to their visual parent using RelativeSource will point to nowhere (unless you set the binding after the relevant target elements are added to the Canvas (requires C# instead of XAML) by observing the CartesianChart.Content.LayoutUpdated event, but I recommend against this).

The recommended solution is to bind to the DataContext instead.

You can either set the UserControl itself as its own DataContext:

PeakrowChartGroupControl.xaml.cs

partial class PeakrowChartGroupControl : UserControl
{
  public PeakrowChartGroupControl()
  {
    InitializeComponent();
    this.DataContext = this;
  }
}

PeakrowChartGroupControl.xaml

<UserControl x:Class="DryLab.Peakmovement.RetentionChartControl">
    <Grid>
        <lvc:CartesianChart x:Name="cc" Series="{Binding ChartParams.Seriescollection}">
            <lvc:CartesianChart.AxisY>
                <lvc:Axis Title="{Binding ChartParams.TitleAxY}" 
                          LabelFormatter="{Binding ChartParams.Formatter}" />
            </lvc:CartesianChart.AxisY>
            <lvc:CartesianChart.AxisX>
                <lvc:Axis Title="{Binding ChartParams.TitleAxX}" 
                          Labels="{Binding ChartParams.Labels}" />
            </lvc:CartesianChart.AxisX>
        </lvc:CartesianChart>
    </Grid>
</UserControl>

Or alternatively e.g., in case the UserControl.DataContext is already set to something else, you can explicitly set the charts DataContext to e.g., the ChartParams property of RetentionChartControl:

PeakrowChartGroupControl.xaml

<UserControl x:Class="DryLab.Peakmovement.RetentionChartControl">
    <Grid>
        <lvc:CartesianChart DataContext="{Binding ChartParams, RelativeSource={RelativeSource AncestorType=local:RetentionChartControl}}" 
                            Series="{Binding Seriescollection}">
            <lvc:CartesianChart.AxisY>
                <lvc:Axis Title="{Binding TitleAxY}" 
                          LabelFormatter="{Binding Formatter}" />
            </lvc:CartesianChart.AxisY>
            <lvc:CartesianChart.AxisX>
                <lvc:Axis Title="{Binding TitleAxX}" 
                          Labels="{Binding Labels}" />
            </lvc:CartesianChart.AxisX>
        </lvc:CartesianChart>
    </Grid>
</UserControl>