26

Will AdaptiveTrigger work in a DataTemplate?

That's my code i'm using to customize my ShellNavigation, it is working fine except the visual states. They will not trigger anything.

<shell:ShellHeadView x:Key="ShellHeadView_01">
    <shell:ShellHeadView.ContentTemplate>
        <DataTemplate>
            <Grid Margin="20,0">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup>
                        <VisualState x:Name="GreenBackgroundVisualState">
                            <VisualState.Setters>
                                <Setter Target="headViewLeft.Background" Value="Green" />
                            </VisualState.Setters>
                            <VisualState.StateTriggers>
                                <AdaptiveTrigger MinWindowWidth="1000"/>
                            </VisualState.StateTriggers>
                        </VisualState>
                        <VisualState x:Name="OrangeBackgroundVisualState">
                            <VisualState.Setters>
                                <Setter Target="headViewLeft.Background" Value="Orange" />
                            </VisualState.Setters>
                            <VisualState.StateTriggers>
                                <AdaptiveTrigger MinWindowWidth="2000"/>
                            </VisualState.StateTriggers>
                        </VisualState>
                        <VisualState x:Name="RedBackgroundVisualState">
                            <VisualState.Setters>
                                <Setter Target="headViewLeft.Background" Value="Red" />
                            </VisualState.Setters>
                            <VisualState.StateTriggers>
                                <AdaptiveTrigger MinWindowWidth="3000"/>
                            </VisualState.StateTriggers>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid Grid.Column="0" x:Name="headViewLeft" Width="100" Height="90">

                </Grid>
Jens Marchewka
  • 1,372
  • 1
  • 13
  • 22

2 Answers2

61

Try wrapping your DataTemplate inside a UserControl like this -

<DataTemplate>
    <UserControl>
        <Grid>
            <VisualStateManager.VisualStateGroups>
            ...
        </Grid>
    </UserControl>
</DataTemplate>

Looks like any Control that has got a Content property will work. That's why UserControl works, so does a ContentControl.

So if you replace the UserControl with a ContentControl and give it an empty Style. It should work too.

<Style x:Key="EmptyContentControlStyle" TargetType="ContentControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ContentControl" />
        </Setter.Value>
    </Setter>
</Style>

<DataTemplate>
    <ContentControl Style="{StaticResource EmptyContentControlStyle}">
        <Grid>
            <VisualStateManager.VisualStateGroups>
            ...
        </Grid>
    </ContentControl>
</DataTemplate>
Justin XL
  • 38,763
  • 7
  • 88
  • 133
  • How can I use this to make dynamically created list items working?`ListView listView = new ListView(); listView.ItemsSource = source.Articles; listView.Template = ???; listView.IsItemClickEnabled = true; listView.ItemClick += OpenArticle_ItemClick; listView.SelectionMode = ListViewSelectionMode.None;` – Jan Chalupa Feb 25 '16 at 23:44
  • FWIW, `UserControl` works great, but `ContentControl` does not. Still a great answer! – Eric Liprandi Mar 08 '16 at 23:21
  • 2
    @EricLiprandi hmm did you give your ContentControl an empty style? – Justin XL Mar 08 '16 at 23:24
  • I missed that part :) - I guess the `UserControl` is cleaner for me then... I can't think of a functional difference in this case between `UserControl` and `ContentControl`. – Eric Liprandi Mar 08 '16 at 23:31
  • @EricLiprandi yeah `UserControl` is definitely more convenient. :) – Justin XL Mar 08 '16 at 23:38
  • Also, make sure that the VisualStateManager is not a direct child of UserControl or ContentControl – White hawk Mar 16 '16 at 14:09
  • this approach works, but the StateTrigger gets eventually garbage collected, which leads to an error later on. – thumbmunkeys Jan 24 '17 at 14:50
  • 1
    Here is another [answer](https://stackoverflow.com/a/44754384/231837) that addresses states in `ListView`'s `DataTemplate`. – Justin XL Jun 26 '17 at 14:42
  • In general I would heavily recommend against putting user controls inside datatemplates. User controls are SLOW, so having them in templates that are used over and over again can be bad for performance (custom controls with control templates are a better alternative for that). – dotMorten Nov 19 '21 at 16:55
0

Create two data templates. Use the adaptive trigger to change the ItemTemplate on your ItemsControl

TBH it feels a bit weird to me to put adaptive triggers inside a generic template, rather than in the page view.

In general I would heavily recommend against putting user controls inside datatemplates as is suggested in another answer. User controls are SLOW, so having them in templates that are used over and over again can be bad for performance (custom controls with control templates are a better alternative for that).

You could also create a custom control that switches between templates. Example:

public class AdaptiveControl : ContentControl
{
    public AdaptiveControl()
    {
        SizeChanged += AdaptiveControl_SizeChanged;
    }

    private void AdaptiveControl_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        Update();
    }
    private void Update() 
    {
        if (ActualWidth < Size)
            ContentTemplate = SmallTemplate;
        else
            ContentTemplate = LargeTemplate;
    }

    public double Size
    {
        get { return (double)GetValue(SizeProperty); }
        set { SetValue(SizeProperty, value); }
    }

    public static readonly DependencyProperty SizeProperty =
        DependencyProperty.Register(nameof(Size), typeof(double), typeof(AdaptiveControl), new PropertyMetadata(200d, (s, e) => ((AdaptiveControl)s).Update()));

    public DataTemplate SmallTemplate
    {
        get { return (DataTemplate)GetValue(SmallTemplateProperty); }
        set { SetValue(SmallTemplateProperty, value); }
    }

    public static readonly DependencyProperty SmallTemplateProperty =
        DependencyProperty.Register(nameof(SmallTemplate), typeof(DataTemplate), typeof(AdaptiveControl), new PropertyMetadata(null, (s, e) => ((AdaptiveControl)s).Update()));


    public DataTemplate LargeTemplate
    {
        get { return (DataTemplate)GetValue(LargeTemplateProperty); }
        set { SetValue(LargeTemplateProperty, value); }
    }

    public static readonly DependencyProperty LargeTemplateProperty =
        DependencyProperty.Register(nameof(LargeTemplate), typeof(DataTemplate), typeof(AdaptiveControl), new PropertyMetadata(null, (s, e) => ((AdaptiveControl)s).Update()));

}
dotMorten
  • 1,953
  • 1
  • 14
  • 10