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>
    
Clemens
  • 123,504
  • 12
  • 155
  • 268
Steven_BDawg
  • 796
  • 6
  • 21
  • Couldnt understand problem but seem there is no twoway binding – Hamit YILDIRIM Jun 02 '19 at 20:24
  • 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. – Steven_BDawg Jun 02 '19 at 20:27
  • Ok i understand. You need to debug your converter for the empty.. – Hamit YILDIRIM Jun 02 '19 at 20:34
  • 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. – Steven_BDawg Jun 02 '19 at 20:36
  • I think they will be always exist but will be shown according a properity and converter decide which ones will be visible – Hamit YILDIRIM Jun 02 '19 at 20:40
  • 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. – Steven_BDawg Jun 02 '19 at 20:45
  • 1
    In other words, if I hardcoded 10 buttons, the six Collapsed buttons would not take up space. – Steven_BDawg Jun 02 '19 at 21:02
  • Ok i think you need a relative source binding because grid has changing its context Look this example https://stackoverflow.com/a/51336668/914284 – Hamit YILDIRIM Jun 02 '19 at 21:38
  • 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. – Steven_BDawg Jun 03 '19 at 01:41
  • 1
    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. – Clemens Jun 03 '19 at 08:16
  • @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! :) – Steven_BDawg Jun 05 '19 at 04:31
  • @Steven_BDawg 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. – Clemens Jun 05 '19 at 05:04
  • 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. – Steven_BDawg Jun 05 '19 at 13:48

1 Answers1

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>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • can please tell me why it has no effect on Visibility thank you – Avinash Reddy Jun 03 '19 at 08:08
  • 1
    @AvinashReddy because the container is still there, even if the element in its ContentTemplate is collapsed. – Clemens Jun 03 '19 at 08:09
  • 2
    @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! – Steven_BDawg Jun 05 '19 at 04:48