Kezza
Kezza

Reputation: 746

Cannot find element that provides DataContext in a custom control

Hi I have a custom control which I use to present other xaml content.

Within the implementation of said control I have a user control that expects a view model to be bound to its data context, however the data context is always null and I am getting the following error message:

System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=OutcomeRestrictionVM; DataItem=null; target element is 'XsdEnumRestrictionView' (Name=''); target property is 'DataContext' (type 'Object')

The Generic.xaml for my custom control is:

<ControlTemplate TargetType="{x:Type local:ContextGroupBox}" x:Key="ContextGroupBoxTemplate">
    <Grid SnapsToDevicePixels="true" >
        <Grid.Resources>
            <ControlTemplate x:Key="tplFlatButton" TargetType="{x:Type Button}">
                <Border Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                      TextElement.Foreground="{TemplateBinding Foreground}"
                      TextElement.FontFamily="{TemplateBinding FontFamily}"
                      TextElement.FontSize="{TemplateBinding FontSize}"
                      TextElement.FontStretch="{TemplateBinding FontStretch}"
                      TextElement.FontWeight="{TemplateBinding FontWeight}"/>
                </Border>
            </ControlTemplate>
            <Style x:Key="stlFlatButton" TargetType="{x:Type Button}">
                <Setter Property="Background" Value="{x:Null}" />
                <Setter Property="BorderBrush" Value="{x:Null}" />
                <Setter Property="BorderThickness" Value="0" />
                <Setter Property="Template" Value="{StaticResource tplFlatButton}" />
            </Style>
        </Grid.Resources>
        <Rectangle RadiusX="10" RadiusY="10" StrokeThickness="{TemplateBinding BorderThickness}"  Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}"/>
        <Grid Margin="{TemplateBinding BorderThickness}">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <!-- Group Title -->
            <Grid Grid.Row="0" Margin="5,5,5,1" >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="auto"/>
                </Grid.ColumnDefinitions>
                <ContentPresenter Content="{TemplateBinding Header}" RecognizesAccessKey="true" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" TextElement.Foreground="{TemplateBinding Foreground}" TextElement.FontSize="{TemplateBinding HeaderFontSize}" TextElement.FontWeight="Bold"/>
                <!--<TextBlock Grid.Column="0" Text="Record Access" Foreground="#4E86B8" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>-->
                <Button Command="{TemplateBinding ShowHelpCommand}" CommandParameter="{TemplateBinding ShowHelpParameter}" Style="{StaticResource stlFlatButton}" Grid.Column="1" HorizontalAlignment="Right">
                    <Image  Source="/Hornbill.Resources.Image;component/Shared/information.png" Width="16" Height="16" Visibility="Visible" >
                    </Image>
                </Button>
            </Grid>
            <ContentControl DataContext="{TemplateBinding DataContext}" Content="{TemplateBinding Content}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Grid.Row="1" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Grid>
    </Grid>
</ControlTemplate>

<Style TargetType="{x:Type local:ContextGroupBox}">
    <Setter Property="Template" Value="{StaticResource ContextGroupBoxTemplate}" />
    <Setter Property="Padding" Value="7,4,7,7" />
    <Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>

custom control ContextGroupBox.cs

public static double defaultHeaderFontSize = 14;

public double HeaderFontSize
{
    get { return (double)GetValue(HeaderFontSizeProperty); }
    set { SetValue(HeaderFontSizeProperty, value); }
}

// Using a DependencyProperty as the backing store for HeaderFontSize.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderFontSizeProperty =
    DependencyProperty.Register("HeaderFontSize", typeof(double), typeof(ContextGroupBox), new PropertyMetadata(defaultHeaderFontSize));

public object Header
{
    get { return (object)GetValue(HeaderProperty); }
    set { SetValue(HeaderProperty, value); }
}

// Using a DependencyProperty as the backing store for Header.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderProperty =
    DependencyProperty.Register("Header", typeof(object), typeof(ContextGroupBox));



public FrameworkElement Content
{
    get { return (FrameworkElement)GetValue(ContentProperty); }
    set { SetValue(ContentProperty, value); }
}

// Using a DependencyProperty as the backing store for Content.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentProperty =
    DependencyProperty.Register("Content", typeof(FrameworkElement), typeof(ContextGroupBox));



public ICommand ShowHelpCommand
{
    get { return (ICommand)GetValue(ShowHelpCommandProperty); }
    set { SetValue(ShowHelpCommandProperty, value); }
}

// Using a DependencyProperty as the backing store for ShowHelpCommand.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowHelpCommandProperty =
    DependencyProperty.Register("ShowHelpCommand", typeof(ICommand), typeof(ContextGroupBox));

public object ShowHelpParameter
{
    get { return (object)GetValue(ShowHelpParameterProperty); }
    set { SetValue(ShowHelpParameterProperty, value); }
}

// Using a DependencyProperty as the backing store for ShowHelpParameter.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowHelpParameterProperty =
    DependencyProperty.Register("ShowHelpParameter", typeof(object), typeof(ContextGroupBox));


static ContextGroupBox()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(ContextGroupBox), new FrameworkPropertyMetadata(typeof(ContextGroupBox)));
}

and my implementation is:

<hctrl:ContextGroupBox Grid.Column="1" DataContextChanged="gbOutcomeParam_DataContextChanged_1"  Header="{l:Translate Outcome}" Name="gbOutcomeParam" VerticalAlignment="Stretch" Margin="5 5">
                        <ctrl:XsdEnumRestrictionView DataContext="{Binding OutcomeRestrictionVM, Mode=OneWay}" DataContextChanged="XsdEnumRestrictionView_DataContextChanged" />
                    </hctrl:ContextGroupBox>

The crazy thing is that if I create a dependency property in my user control (ctrl:XsdEnumRestrictionView) and bind to that everything works fine but not when I bind to DataContext explicitly.

Ok, maybe the user control should use a property anyway but I am frustrated by the loss of DataContext and would like to understand why my custom control does not seem to have one.

Any help will be much appreciated

Thanks

Kieran

Upvotes: 1

Views: 3891

Answers (1)

Kezza
Kezza

Reputation: 746

Ok so for an explicit bind to DataContext to work I had to set the base class of my Custom Control to HeaderedContentControl and remove the Header and Content properties. Now it all seems to work fine, though I still do not quite understand why it didn't work when the base class was Control.

Upvotes: 3

Related Questions