WPF - Hide items in ItemControl -> UniformGrid from taking up UI space based on databinding
Asked Answered
M

1

6

Databound to an ObservableCollection, I am filling an ItemsControl with Buttons. I am using UniformGrid to help evenly spread things out whether there are 5 or 5000 objects in the ObservableCollection.

Desire: After the user searches/filters the ObservableCollection, I would like to update an IsVisible property on items to show/hide them... while also consolidating the space.

Rationale: I figured, performance wise, updating a property would be better than doing a Clear() and loop to re-add the filtered items back to the databound ObservableCollection.

Problem: While the current implementation (code below) does visibly hide the buttons, the space they take up still is present regardless of which Visibility property I try to use.

Disclaimer: I am open to more than just simply "fixing" my current code. For example, if a viable solution does not use UniformGrid for example but still achieves a sustainable result, I can probably use it! The same is true on the ViewModel side.

<ItemsControl Name="ItemsList"
            Width="Auto"
            HorizontalContentAlignment="Left"
            ItemsSource="{Binding ItemsVM.WorkList}"
            ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="true"
            VirtualizingStackPanel.VirtualizationMode="Standard"
            >
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="5" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button
                        Width="250" Height="50"
                        FontSize="18" FontWeight="Bold"
                        Background="{Binding TextColor, Converter={StaticResource TextToColorConvert}, UpdateSourceTrigger=PropertyChanged}"
                        Margin="1,1,1,1" HorizontalAlignment="Center" VerticalAlignment="Center"
                        BorderBrush="WhiteSmoke" BorderThickness="0" Click="workNumSelect"
                        Content="{Binding Workload.WorkNum}"
                        Cursor="Hand" Opacity=".8"
                        Visibility="{Binding IsVisible, Converter={StaticResource BoolToCollapsedVisConvert}, UpdateSourceTrigger=PropertyChanged}"
                        />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <ScrollViewer Width="Auto" VerticalScrollBarVisibility="Visible">
                        <ItemsPresenter />
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>
        </ItemsControl>

Update: I did not simply copy and paste the entire answer.

  1. In the code above, at Button, inside of the DataTemplate, I removed the Visibility line.
  2. I only added the following code:

            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsVisible}" Value="False">
                            <Setter Property="Visibility" Value="Collapsed" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ItemsControl.ItemContainerStyle>
    
Manual answered 2/6, 2019 at 20:7 Comment(13)
Couldnt understand problem but seem there is no twoway bindingConductor
If I have 10 buttons on screen and the user (in another field not listed) filters the buttons down to 4, 6 of the buttons will disappear. However, the space allocated for those 6 buttons still is taken up.Manual
Ok i understand. You need to debug your converter for the empty..Conductor
I do not understand. The visibility converter simply converts true/false to Visible or Collapsed, setting the visibility property on the button. In the example, in the ObservableCollection, all 10 items still actually exist.Manual
I think they will be always exist but will be shown according a properity and converter decide which ones will be visibleConductor
The difference between Visibility.Hidden and Visibility.Collapsed is supposed to be whether or not the object in question takes up UI space. However, It does not seem like it works when inside a UniformGrid or perhaps ItemTemplate.Manual
In other words, if I hardcoded 10 buttons, the six Collapsed buttons would not take up space.Manual
Ok i think you need a relative source binding because grid has changing its context Look this example https://mcmap.net/q/1772611/-wpf-binding-to-visibility-of-an-itemtemplate-of-a-listview-not-working-duplicateConductor
The example link you provided has a solution that seems to show an issue making objects within the listview item hidden/collapsed. My above code works just fine to accomplish that. In respect to that question, I am trying to collapse the entire listview item from taking up a row, not collapse the content of the listview item.Manual
As a note, setting UpdateSourceTrigger=PropertyChanged on OneWay Bindings (like those of the Background and Visibility) has no effect. UpdateSourceTrigger only affects TwoWay or OneWayToSource Bindings and has nothing to do with the PropertyChanged event.Eldaelden
@Clemens, that is a great point. We started including it on most bindings because "it did not hurt anything." I am no XAML master by any sense of the word, but I kept having to correct XAML issues that someone would forget to do. Because of that, it was more efficient to try being consistent with the XAML bindings than someone(s) trying to find what was forgotten. I chose consistency and efficiency over absolute correctness... Forgive me please! :)Manual
@Manual I didn't mention it explicitly in my post, but it seems pretty obvious to remove the Visibility Binding from the Button in the DataTemplate when it had no effect.Eldaelden
I agree it is obvious, but if someone comes to S.O. 3 years later for this QnA, it may not be for whatever reason.Manual
E
10

It has no effect to set the Visibility of the Button in the DataTemplate. You should instead set the Visibility of the item container, i.e. the ContentPresenter that displays the individual items.

You would achieve that by setting the ItemContainerStyle of the ItemsControl to a Style with a Setter that binds the Visibility property, or with a DataTrigger instead of a Binding with Converter.

<ItemsControl ItemsSource="{Binding}">
    <ItemsControl.Template>
        <ControlTemplate TargetType="ItemsControl">
            <ScrollViewer VerticalScrollBarVisibility="Visible">
                <ItemsPresenter />
            </ScrollViewer>
        </ControlTemplate>
    </ItemsControl.Template>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="5" VerticalAlignment="Top"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button ... />
        </DataTemplate>
    </ItemsControl.ItemTemplate>

    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsVisible}" Value="False">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>
Eldaelden answered 3/6, 2019 at 8:3 Comment(3)
can please tell me why it has no effect on Visibility thank youDistinctly
@AvinashReddy because the container is still there, even if the element in its ContentTemplate is collapsed.Eldaelden
@AvinashReddy It is like you were given 10 sheets of paper and someone was telling you what to fold each paper into... except the person purposely told you to do nothing with 6 of the sheets. You still have 10 sheets of paper! Clemens: Great explanation, and great solution for something I assume could get applied to a multitude of things. I did not know ItemContainerStyle could be used in such a way! Thank you!Manual

© 2022 - 2024 — McMap. All rights reserved.