Search code examples
wpfitemscontrolstackpaneluniformgrid

UniformGrid with DataTemplate trigger not working as expected, WPF


I host a list of 64 UserControl in an ItemsControl, the DataContext is an array of objects. Then the DataContext for the individual instance of the UserControl becomes the instance of the object.

The objects have a boolean variable called Exists, this is a DataTemplate trigger to determine if the Usercontrol will be displayed or not.

I use a Uniformgrid to display the list, but I'm experiencing some weird behavior. The Usercontrol don't resize. See attached picture. If I use a StackPanel instead, it works just fine. But I would like to use the UnifromGrid instead.

Here is the code - Only 4 objects have the Exist variable set to true.

    <Grid Grid.Row="1" Grid.Column="1" x:Name="gridSome" Background="#FF5AC1F1">
        <Viewbox>
            <ItemsControl ItemsSource="{Binding SomeVM.SomeModel.SomeArray}"  
                          Margin="15" HorizontalAlignment="Center" VerticalContentAlignment="Center">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <tensioner:UCView  Margin="5"/>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding Exists}" Value="False">
                                <Setter Property="Visibility" Value="Collapsed"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <!--<StackPanel IsItemsHost="true"/> This works-->
                        <UniformGrid Columns="1"/> <!-- This does not work-->
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
             </ItemsControl>
        </Viewbox>
    </Grid>

Uniform Grid

Stack Panel

-----Update------

//SSCCE MainWindow

<Window x:Class="WpfAppItemIssue.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAppItemIssue"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <!--<Viewbox>-->
            <ItemsControl ItemsSource="{Binding Model.Cars}">
            <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBox Text="ABC"></TextBox>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding exists}" Value="False">
                                <Setter Property="Visibility" Value="Collapsed"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="1"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        <!--</Viewbox>-->
    </Grid>
</Window>

MainViewModel

using System.ComponentModel;

namespace WpfAppItemIssue
{
    class MainViewModel:INotifyPropertyChanged
    {

        public MainViewModel()
        {
            Model = new MainModel();
        }

        private MainModel model;
        public MainModel Model
        {
            get
            {
                return model;
            }
            set
            {
                model = value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

Model

namespace WpfAppItemIssue
{
    class MainModel
    {
        public Car[] Cars { get; set; }

        public MainModel()
        {
            Cars = new Car[64];
            for (int i = 0; i < Cars.Length; i++)
            {
                Cars[i] = new Car(i);
            }
        }
    }

    internal class Car
    {
        public int someVal { get; set; }
        public bool exists { get; set; }

        public Car(int someVal)
        {
            this.someVal = someVal;
            if (someVal < 5)  //Just enable few items for debug
            {
                exists = true;
            }
            else
            {
                exists = false;
            }
        }
    }
}

See attached images :

Picture 1 shows Design View. Why are the user controls not being resized? Picture 2 shows On Execute. Why are the user controls not being resized? Picture 3 shows On Any resize event. The Controls are being resized correctly. Design ViewExecute Resize


Solution

  • Well I finally got your problem after discussion in comments. It is all about DataTrigger in your ItemTemplate. Just move it to ItemContainerStyle Triggers and elements will be resized correctly.

    <ItemsControl ItemsSource="{Binding Model.Cars}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="1"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding exists}" Value="False">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="ABC"></TextBox>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    Note that TextBox'es will be resized "by border" only (this behavior is shown on your last picture), font size will not be changed. If you want to scale your elements uniformly with their content you really need to wrap ItemsControl to Viewbox.