0
votes

I have a user control of a Line Series Chart and created a bunch of dependency properties for items on the chart; title, x-axis label, the data itself, etc... The data is an observable collection of KeyValuePair. I had the chart working fine when it was in the main window. I had to have 5 line series charts on my main window so I thought a user control would be a good solution. I copied and pasted the xaml into a new user control. I added binding to all the elements I wanted to bind; title, labels, intervals and data. I then created properties in the code behind for all this binding and used DependencyProperty.Register to make sure they would be passed along. The titles and labels work just fine although I am hardcoding them in the xaml on the main window. The observable collection is not binding at all. I tried a bunch of solutions found on this site (ie - adding ElementName=NameOfUserControl to the binding, adding RelativeSource=UserControl on the main window's binding). I can't seem to get that data to pass through.

If I don't add the RelativeSource clause in the main window I get this error:

System.Windows.Data Error: 40 : BindingExpression path error: 'Disposition' property not > found on 'object' ''LineGraph' (Name='Me')'. BindingExpression:Path=Disposition.WidthData; > DataItem='LineGraph' (Name='Me'); target element is 'LineGraph' (Name='Me'); target > property is 'ActualData' (type 'ObservableCollection`1')

I've had this problem before and I eventually gave up and just put the xaml directly into my main window instead of using a user control. I'd like to figure this out so that I can use more user controls and not have 1000s of lines of xaml in my main window.

Here is the user control xaml:

<UserControl x:Class="Essar.Dispo.UI.UserControls.LineGraph"
         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:toolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
         xmlns:datavis="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit"
         mc:Ignorable="d" x:Name="Me"
         >
<UserControl.Resources>
    <Style TargetType="datavis:Title" x:Key="GraphTitleStyle">
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontSize" Value="16" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>

    <Style TargetType="datavis:Title" x:Key="AxisTitleStyle" BasedOn="{StaticResource GraphTitleStyle}">
        <Setter Property="FontSize" Value="12" />
        <Setter Property="FontWeight" Value="Medium" />
    </Style>

    <Style TargetType="datavis:Legend" x:Key="LegendStyle">
        <Setter Property="Width" Value="0" />
    </Style>

    <Style TargetType="toolkit:AxisLabel" x:Key="AxisLabelStyle">
        <Setter Property="Foreground" Value="Black" />
    </Style>
</UserControl.Resources>
<Grid>
    <toolkit:Chart Name="FinishingMillWidthChart" Title="{Binding MainTitle}" Height="350"
                                       TitleStyle="{StaticResource GraphTitleStyle}"
                                       LegendStyle="{StaticResource LegendStyle}">
        <toolkit:Chart.Axes>
            <toolkit:LinearAxis Orientation="X" Title="{Binding XAxisTitle}" Interval="{Binding XAxisInterval}" ShowGridLines="True" 
                                                    AxisLabelStyle="{StaticResource AxisLabelStyle}" TitleStyle="{StaticResource AxisTitleStyle}" />
            <toolkit:LinearAxis Orientation="Y" Title="{Binding YAxisTitle}" ShowGridLines="True" Interval="{Binding YAxisInterval}" 
                                                    AxisLabelStyle="{StaticResource AxisLabelStyle}" TitleStyle="{StaticResource AxisTitleStyle}" />
        </toolkit:Chart.Axes>
        <toolkit:LineSeries Title="tolerance high" DependentValuePath="Value" IndependentValuePath="Key"
                            ItemsSource="{Binding ToleranceHigh, ElementName=Me}" Visibility="{Binding HighLowVisible}">
            <toolkit:LineSeries.DataPointStyle>
                <Style TargetType="{x:Type toolkit:DataPoint}">
                    <Setter Property="Template" Value="{x:Null}" />
                    <Setter Property="Background" Value="YellowGreen" />
                </Style>
            </toolkit:LineSeries.DataPointStyle>
        </toolkit:LineSeries>
        <toolkit:LineSeries Title="actual" DependentValuePath="Value" IndependentValuePath="Key"
                            ItemsSource="{Binding ActualData, ElementName=Me}">
            <toolkit:LineSeries.DataPointStyle>
                <Style TargetType="{x:Type toolkit:DataPoint}">
                    <Setter Property="Template" Value="{x:Null}" />
                    <Setter Property="Background" Value="Maroon" />
                </Style>
            </toolkit:LineSeries.DataPointStyle>
        </toolkit:LineSeries>
        <toolkit:LineSeries Title="tolerance low" DependentValuePath="Value" IndependentValuePath="Key"
                            ItemsSource="{Binding ToleranceLow, ElementName=Me}" Visibility="{Binding HighLowVisible}">
            <toolkit:LineSeries.DataPointStyle>
                <Style TargetType="{x:Type toolkit:DataPoint}">
                    <Setter Property="Template" Value="{x:Null}" />
                    <Setter Property="Background" Value="DarkCyan" />
                </Style>
            </toolkit:LineSeries.DataPointStyle>
        </toolkit:LineSeries>
    </toolkit:Chart>
</Grid>

And here is the code behind for the user control:

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

    #region Properties
    public static readonly DependencyProperty MainTitleProperty = DependencyProperty.Register("MainTitle", typeof(string), typeof(LineGraph));
    public string MainTitle
    {
        get { return GetValue(MainTitleProperty).ToString(); }
        set { SetValue(MainTitleProperty, value); }
    }


    public static readonly DependencyProperty XAxisTitleProperty = DependencyProperty.Register("XAxisTitle", typeof(string), typeof(LineGraph));
    public string XAxisTitle
    {
        get { return GetValue(XAxisTitleProperty).ToString(); }
        set { SetValue(XAxisTitleProperty, value); }
    }


    public static readonly DependencyProperty YAxisTitleProperty = DependencyProperty.Register("YAxisTitle", typeof(string), typeof(LineGraph));
    public string YAxisTitle
    {
        get { return GetValue(YAxisTitleProperty).ToString(); }
        set { SetValue(YAxisTitleProperty, value); }
    }


    public static readonly DependencyProperty XAxisIntervalProperty = DependencyProperty.Register("XAxisInterval", typeof(double), typeof(LineGraph));
    public double XAxisInterval
    {
        get { return Common.DoubleParse(GetValue(XAxisIntervalProperty)); }
        set { SetValue(XAxisIntervalProperty, value); }
    }

    public static readonly DependencyProperty YAxisIntervalProperty = DependencyProperty.Register("YAxisInterval", typeof(double), typeof(LineGraph));
    public double YAxisInterval
    {
        get { return Common.DoubleParse(GetValue(YAxisIntervalProperty)); }
        set { SetValue(YAxisIntervalProperty, value); }
    }


    public static readonly DependencyProperty ToleranceHighProperty = DependencyProperty.Register("ToleranceHigh", typeof(ObservableCollection<KeyValuePair<double, double>>), typeof(LineGraph));
    public ObservableCollection<KeyValuePair<double, double>> ToleranceHigh
    {
        get 
        {
            return GetValue(ToleranceHighProperty) as ObservableCollection<KeyValuePair<double, double>>; 
        }
        set { SetValue(ToleranceHighProperty, value); }
    }

    public static readonly DependencyProperty ActualDataProperty = DependencyProperty.Register("ActualData", typeof(ObservableCollection<KeyValuePair<double, double>>), typeof(LineGraph));
    public ObservableCollection<KeyValuePair<double, double>> ActualData
    {
        get
        {
            return GetValue(ActualDataProperty) as ObservableCollection<KeyValuePair<double, double>>;
        }
        set { SetValue(ActualDataProperty, value); }
    }

    public static readonly DependencyProperty ToleranceLowProperty = DependencyProperty.Register("ToleranceLow", typeof(ObservableCollection<KeyValuePair<double, double>>), typeof(LineGraph));
    public ObservableCollection<KeyValuePair<double, double>> ToleranceLow
    {
        get
        {
            return GetValue(ToleranceLowProperty) as ObservableCollection<KeyValuePair<double, double>>;
        }
        set { SetValue(ToleranceLowProperty, value); }
    }

    public Visibility HighLowVisible
    {
        get
        {
            if (ToleranceHigh == null || ToleranceLow == null)
                return Visibility.Hidden;
            return Visibility.Visible;
        }
    }

    #endregion
}

Just a note that Common.DoubleParse is a function in my Framework that does a double.TryParse, nothing special really.

And finally here is the xaml in my main window, not sure if this is important but the graph is inside a tabitem, scrollviewer and dockpanel.

<uc:LineGraph MainTitle="finishing mill width" Margin="5"
              XAxisTitle="length (meters)" XAxisInterval="10" 
              YAxisTitle="millimeters" YAxisInterval="15"
              ToleranceHigh="{Binding Disposition.WidthToleranceHigh}"
              ActualData="{Binding Disposition.WidthData}"
              ToleranceLow="{Binding Disposition.WidthToleranceLow}"/>

Another note, my MainWindowViewModel has a property called Disposition that is another view model with it's own properties. Keep in mind when the chart was at this exact same location but written out in full it worked perfectly. I'm thinking I'm missing something stupid with my binding in the main window or the user control.

Any help is appreciated :-)

1

1 Answers

0
votes

never set the datacontext of your usercontrol to this! so simply remove your line in your ctor.

public LineGraph()
{
    InitializeComponent();
}

now of course you should add to ALL your bindings your elementname, eg.

        <toolkit:LinearAxis Orientation="X" 
             Title="{Binding XAxisTitle, ElementName=Me}" 
             Interval="{Binding XAxisInterval, ElementName=Me}" 
             ShowGridLines="True" AxisLabelStyle="{StaticResource AxisLabelStyle}" TitleStyle="{StaticResource AxisTitleStyle}" />

EDIT: can you pls try this code just to see wether your binding to your collection work?

<UserControl x:Class="Essar.Dispo.UI.UserControls.LineGraph"
     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:toolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
     xmlns:datavis="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit"
     mc:Ignorable="d" x:Name="Me"
     >
<Grid>
<ItemsControl ItemsSource="{Binding Path=ActualData, ElementName=Me}"/>
</Grid>

and within your mainview add the following

<uc:LineGraph MainTitle="finishing mill width" Margin="5"
          XAxisTitle="length (meters)" XAxisInterval="10" 
          YAxisTitle="millimeters" YAxisInterval="15"
          ToleranceHigh="{Binding Disposition.WidthToleranceHigh}"
          ActualData="{Binding Disposition.WidthData}"
          ToleranceLow="{Binding Disposition.WidthToleranceLow}"/>
<ItemsControl ItemsSource="{Binding Disposition.WidthData}"/>

both itemscontrols should show the same.

and btw you can check your binding at runtime with SNOOP