0

I've got a UserControl that I'm using to create a simple CardView like you would find in the DevExpress toolkit. It consists of just a header and a body. The body is a <ContentPresenter/> that binds to the control's Body dependency property.

Here is the XAML for the UserControl

<UserControl x:Class="TwinAxis.UI.WPF.Controls.CardControl"
             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:TwinAxis.UI.WPF.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="TextBlock" x:Key="SensorStateLabelStyle">
            <Setter Property="FontSize" Value="18" />
        </Style>
        <Style x:Key="CardHeader" TargetType="Border" BasedOn="{StaticResource HeaderStyle}">
            <Setter Property="Height" Value="35" />
        </Style>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot">
        <Border BorderBrush="DarkSlateGray" BorderThickness="1" Background="White">
            <Border.BitmapEffect>
                <DropShadowBitmapEffect Color="{StaticResource WindowsMediaMainColor}"
                                        ShadowDepth="3"
                                        Direction="315"
                                        Softness="0.45"
                                        Opacity="0.75"/>
            </Border.BitmapEffect>
        </Border>

        <Border>
            <DockPanel>
                <Border DockPanel.Dock="Top" Style="{StaticResource CardHeader}">
                    <TextBlock Style="{StaticResource HeaderText}"
                               DockPanel.Dock="Top"
                               Padding="5"
                               Text="{Binding Header}" />
                </Border>
                <ScrollViewer HorizontalScrollBarVisibility="Auto"
                              Padding="5"
                              VerticalScrollBarVisibility="Auto">
                    <ContentPresenter Content="{Binding Body}" />
                </ScrollViewer>
            </DockPanel>
        </Border>
    </Grid>
</UserControl>

The CardControl.xaml.cs code behind

public partial class CardControl : UserControl {

    public static readonly DependencyProperty BodyProperty =
        DependencyProperty.Register(nameof(Body), typeof(object), typeof(CardControl), new PropertyMetadata());

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register(nameof(Header), typeof(string), typeof(CardControl), new PropertyMetadata());

    public CardControl() {
        InitializeComponent();
        LayoutRoot.DataContext = this;
    }

    public object Body {
        get => GetValue(BodyProperty);
        set => SetValue(BodyProperty, value);
    }

    public string Header {
        get => (string)GetValue(HeaderProperty);
        set => SetValue(HeaderProperty, value);
    }

}

If I use it like below it works perfectly fine

<!-- Works -->
<ctrl:CardControl Header="Test">
    <ctrl:CardControl.Body>
        <Grid><!-- XAML inside grid --></Grid>
    </ctrl:CardControl.Body>
</ctrl:CardControl>
<!-- Nothing in the body even though I have data templates setup properly -->
<ctrl:CardControl Header="Test">
    <ctrl:CardControl.Body>
        <ContentPresenter Content="{Binding MachineViewModel}" />
    </ctrl:CardControl.Body>
</ctrl:CardControl>

What am I doing wrong? Output

Logan K
  • 324
  • 2
  • 8
  • Shouldn't that be ``? You might also want to use the UserControl's ContentTemplate in addition to the Body property. Even better, drop the Body property and use the UserControl's Content. You would then move its current XAML (which is by default its Content) into a Style. – Clemens Apr 30 '20 at 06:20
  • I moved the current XAML into a Style that sets the control's template property. So now ` – Logan K Apr 30 '20 at 13:52
  • `LayoutRoot.DataContext = this;` - well, you forced isolated dataContext for userControl, so you can forget about *any* external binding like `Content="{Binding MachineViewModel}"` - https://stackoverflow.com/a/25699347/1506454, https://stackoverflow.com/a/39979737/1506454 – ASh Apr 30 '20 at 17:45
  • I did get rid of that concept altogether. It came from a suggestion from a different SO question. After reading the ones you provided it makes perfect sense. This is what happens when somebody who isn't a UI designer has to make views for the front end. Any idea on the issue with setting style properties or should that be a separate question? – Logan K Apr 30 '20 at 17:57

1 Answers1

0

Thanks to @Clemens and @ASh in the comments I was able to fix the issue by moving all of that "template" code where it belongs in a <Style /> tag for the UserControl's Template property. I also removed the forced isolated DataContext that was mentioned in the comments and I'm now able to fill out the content of the CardControl and have it render like I wanted.

<UserControl x:Class="TwinAxis.UI.WPF.Controls.CardControl"
             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:TwinAxis.UI.WPF.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="TextBlock" x:Key="SensorStateLabelStyle">
            <Setter Property="FontSize" Value="18" />
        </Style>
        <Style x:Key="CardHeader" TargetType="Border" BasedOn="{StaticResource HeaderStyle}">
            <Setter Property="Height" Value="35" />
        </Style>
        <Style TargetType="local:CardControl">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:CardControl">
                        <Grid>
                            <Border BorderBrush="DarkSlateGray" BorderThickness="1" Background="White">
                                <Border.BitmapEffect>
                                    <DropShadowBitmapEffect Color="{StaticResource WindowsMediaMainColor}"
                                                            ShadowDepth="3"
                                                            Direction="315"
                                                            Softness="0.45"
                                                            Opacity="0.75"/>
                                </Border.BitmapEffect>
                            </Border>

                            <Border>
                                <DockPanel>
                                    <Border DockPanel.Dock="Top" Style="{StaticResource CardHeader}">
                                        <TextBlock Style="{StaticResource HeaderText}"
                                                   DockPanel.Dock="Top"
                                                   Padding="5"
                                                   Text="{TemplateBinding Header}" />
                                    </Border>
                                    <ScrollViewer HorizontalScrollBarVisibility="Auto"
                                                  Padding="5"
                                                  VerticalScrollBarVisibility="Auto">
                                        <ContentPresenter Content="{TemplateBinding Content}"/>
                                    </ScrollViewer>
                                </DockPanel>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
</UserControl>
Logan K
  • 324
  • 2
  • 8