7

How can I hide a ListViewItem in a bound ListView? Note: I do not want to remove it.

3
  • Hide it forever? Or is this after some action? More details would be good... Commented Oct 29, 2010 at 21:48
  • Its because i have a listview bbinded to a list. if i want to remove a item from listview i have to removeit on the list. so i didnt want that. thne i think is better t just hide it. Commented Nov 3, 2010 at 13:57
  • 1
    Remember to mark the right answer. Commented Aug 19, 2011 at 15:46

7 Answers 7

14

Yeah, this is easy.

The first thing you need to do is to add a property to the class you are binding to. For example, if you are binding to a User class with FirstName and LastName, just add a Boolean IsSupposedToShow property (you can use any property you like, of course). Like this:

class User: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string FirstName { get; set; }
    public string LastName { get; set; }

    private bool m_IsSupposedToShow;
    public bool IsSupposedToShow
    {
        get { return m_IsSupposedToShow; }
        set
        {
            if (m_IsSupposedToShow == value)
                return;
            m_IsSupposedToShow = value;
            if (PropertyChanged != null)
                PropertyChanged(this, 
                    new PropertyChangedEventArgs("IsSupposedToShow"));
        }
    }
}

Then, remember, to hide some item, don't do it in the UI - no no no! Do it in the data. I mean, look for the User record that you want to hide and change that property in it behind the scenes (like in a View Model) - let the UI react. Make the XAML obey the data.

Like this:

<DataTemplate DataType="{x:Type YourType}">
    <DataTemplate.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSupposedToShow}" Value="False">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataTemplate.Resources>
    <!-- your UI here -->
    <TextBlock> 
        <TextBlock.Text> 
        <MultiBinding StringFormat="{}{0}, {1}"> 
            <Binding Path="LastName" /> 
            <Binding Path="FirstName" /> 
        </MultiBinding> 
        </TextBlock.Text>
    </TextBlock>
</DataTemplate>

When you change IsSupposedToShow to false, then the XAML understands it is supposed to change the visibility of the whole DataTemplate. It's all wired up for you by WPF and presto, it's what you wanted in your question!

Best of luck!

Sign up to request clarification or add additional context in comments.

4 Comments

This won't work dynamically unless you've implemented INotifyPropertyChanged in the User class and raise PropertyChange when IsSupposedToShow changes. (Also, I'd name that property IsVisible.)
@Robert Rossney, Welp :S You are correct. I updated the code to reflect your point. I wan't really trying to write production-ready, but it was a simple thing to add to side step a follow-up question. Good catch.
I feel like I've missed something. The XAML makes the text of the item vanish, but not the item in the list.
I like your writing style, it is very welcoming. Good answer too! +1
7

The approaches that I'd follow, from most to least preferable:

  • In ListView.ItemContainerStyle, use a DataTrigger to set Visibility based on a bound property.
  • Use a style in the ItemTemplate, or in the DataTemplate for the items if you're getting default templates from the resource dictionary.
  • Set the ItemsSource for the ListView to a CollectionView, and handle the CollectionView's Filter event in code-behind. See MSDN's discussion of collection views for details.
  • Maintain a separate ObservableCollection as the ItemsSource for the ListView and add/remove items as appropriate.

Under no circumstances would I use a ValueConverter, because I have a possibly-irrational distaste for them.

I think that using a CollectionView is probably the most correct way of doing this, but they're kind of inelegant because you have to write an event handler to implement filtering.

4 Comments

Why didn't a style trigger in the DataTemplate make your list? It seems to be the most simple approach.
It did! It's the second item. I tend to prefer using ItemContainerStyle by default. It's just as simple, and it also means that the behavior is localized to the ListView. This can be an important distinction when the DataTemplate isn't defined locally to the ListView.
Yes, I see your point and agree. But, a style defined in the datatemplate for an itemtemplate is also local, to pick nits. But if the datatemplate is pushed up into Resources, then you have a very valid point.
I originally tried a DataTemplate DataTrigger, and it removed the content from my ListViewItem but left the item (empty but taking a small amount of space) behind. Putting the DataTrigger in ListView.ItemContainerStyle (as described in this answer) worked as needed.
2

Use a style with a trigger to set the items visibility to collapsed.

1 Comment

May be you should use DataTrigger
1

This page gave me the answer I needed: http://www.abhisheksur.com/2010/08/woring-with-icollectionviewsource-in.html (See section "Filtering".)

Wow, so much easier than XAML.

Example:

bool myFilter(object obj)
{
    // Param 'obj' comes from your ObservableCollection<T>.
    MyClass c = obj as MyClass;
    return c.MyFilterTest();
}

// apply it
myListView.Items.Filter = myFilter;
// clear it
myListView.Items.Filter = null;

Comments

1

The approach with ListView.ItemContainerStyle

<ListView ItemsSource="{Binding Path=Messages}" Grid.Column="1" Grid.Row="1" x:Name="Messages"
          SelectedItem="{Binding Path=SelectedMessage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsVisible}" Value="False" >
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemTemplate >
        <DataTemplate>                    
            <StackPanel Orientation="Horizontal" >
                <TextBlock VerticalAlignment="Center" >
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} => {1}">
                            <Binding Path="AuthorName" />
                            <Binding Path="ReceiverName"/>
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Jerry Nixon's answer did not work for me completely. I've got to change the xaml a little bit. Collapsed list view item was using small layout space when I was using DataTemplate.Resources,

Comments

0
<ItemsControl>
 <ItemTemplate>
  <DataTemplate>
   <Image Visibility='{Binding Converter=my:MaybeHideThisElementConverter}' />
   </Image>
  </DataTemplate>
 </ItemTemplate>
</ItemsControl>

What we're doing here is delegating the decision to your implementation of MaybeHideThisElementConverter. This is where you might return Collapsed if the User property of your object is null, or if the Count is an even number, or whatever custom logic your application requires. The converter will be passed each item in your collection, one by one, and you can return either Visibility.Collapsed or Visibility.Visible on a case by case basis.

Comments

0

Another approach with converter and ListView.ItemContainerStyle this time with Setter property of Visibility instead of Data Triggers. I prefer this approach if you have complicated business logic about how to decide if show or collapse. Also this approach allows reuse of the converter on multiple screens.

Here is a ListView snippet from XAML. Remember to substitute Rows for your list:

<ListView ItemsSource="{Binding Rows}">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Visibility" Value="{Binding ., Converter={StaticResource RowVisibilityConverter}}" />
            </Style>
        </ListView.ItemContainerStyle>
 </ListView>

Here is the converter snippet. Remember to substitute Row for your class.:

public class RowVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is Row row)
        {
            if (HideRow(row))
            {
                return Visibility.Collapsed;
            }
        }
        
        return Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    private static bool HideRow(Row row)
    {
        // TODO: Complex business logic that returns True or False
    }
}

Remember to add the converter declaration to the App.xaml or as a resource of the user control/window or the ListView so everything is wired up.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.