Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I vary the layout of a UserControl by a Property?

I've made the smallest project I can to demonstrate the problem:

Code behind:

public partial class AxisControl : UserControl
{
    public static readonly DependencyProperty LayoutProperty =
        DependencyProperty.Register("Layout", typeof(Orientation), typeof(AxisControl),
            new PropertyMetadata(Orientation.Horizontal));

    public Orientation Layout
    {
        get { return (Orientation)GetValue(LayoutProperty); }
        set { SetValue(LayoutProperty, value); }
    }

    public AxisControl()
    {
        InitializeComponent();
    }
}

Xaml:

<UserControl.Resources>
    <ContentControl x:Key="horizontalLayout" Height="60">
        <TextBlock Text="{Binding Layout, RelativeSource={RelativeSource AncestorType=UserControl}}" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
    </ContentControl>
    <ContentControl x:Key="verticalLayout" Width="60">
        <TextBlock Text="{Binding Layout, RelativeSource={RelativeSource AncestorType=UserControl}}" HorizontalAlignment="Left" VerticalAlignment="Center">
            <TextBlock.LayoutTransform>
                <RotateTransform Angle="-90"/>
            </TextBlock.LayoutTransform>
        </TextBlock>
    </ContentControl>
</UserControl.Resources>
<UserControl.Style>
    <Style TargetType="UserControl">
        <Style.Setters>
            <Setter Property="Content" Value="{StaticResource horizontalLayout}"/>
        </Style.Setters>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Layout, ElementName=root}" Value="Vertical">
                <Setter Property="Content" Value="{StaticResource verticalLayout}"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Style>

Edit: Xaml now includes the elements I want to arrange in the UserControl.

MainWindow Xaml:

<Grid>
    <local:AxisControl Layout="Vertical"/>
</Grid>

The idea is to set the layout of the UserControl according to its Layout property so I put both layouts in static resources and made a Style to set the Content to the one I want according to Layout which is of type Orientation.

Edit: I want the Content to include elements arranged in a different order according to the Orientation.

The UserControl displays correctly but there's an error in the output window that concerns me:

Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=Layout; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

Does this mean it's trying to do a Binding before it's in the visual tree possibly from the Trigger?

Note the use of RelativeSource and ElementName in the Bindings as it's incorrect to set the DataContext at the root of a UserControl because it breaks the DataContext inheritance.

What am I doing wrong and how can I get rid of the error?

like image 805
FreddyFlares Avatar asked Nov 19 '25 01:11

FreddyFlares


1 Answers

Inspired by Clemens comment and further research I realized I needed a ControlTemplate resource for each layout and not resources containing instances of the elements themselves.

<UserControl.Resources>
    <ControlTemplate x:Key="horizontalLayout">
        <Border Height="60" Background="LightBlue">
            <TextBlock Text="{Binding Layout, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
        </Border>
    </ControlTemplate>
    <ControlTemplate x:Key="verticalLayout" TargetType="UserControl">
        <Border Width="60" Background="LightBlue">
            <TextBlock Text="{Binding Layout, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Left" VerticalAlignment="Center">
                <TextBlock.LayoutTransform>
                    <RotateTransform Angle="-90"/>
                </TextBlock.LayoutTransform>
            </TextBlock>
        </Border>
    </ControlTemplate>
</UserControl.Resources>
<UserControl.Style>
    <Style TargetType="UserControl">
        <Style.Setters>
            <Setter Property="Template" Value="{StaticResource horizontalLayout}"/>
        </Style.Setters>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Layout, ElementName=root}" Value="Vertical">
                <Setter Property="Template" Value="{StaticResource verticalLayout}"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Style>

Layout = Horizontal

Layout = Orientation.Horizontal

Layout = Vertical

enter image description here

like image 166
FreddyFlares Avatar answered Nov 20 '25 16:11

FreddyFlares



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!