How to collapse/hide ListBoxItem

Emon Haque 3,176 Reputation points
2021-05-18T23:11:33.333+00:00

I've been adding search/filter functionalities in Edit Views and in two sub views, Transaction and Lease, I'd have to add some additional string properties in the Model if I want to add that functionality in the same way I did in other sub views like Plot, Space and Tenant:

97695-test.gif

in the ListBox of Lease view, I've Egypt: A/ Egypt: B and these have been converted with some IValueConverter this way in the DataTemplate:

space.SetBinding(Run.TextProperty, new Binding(nameof(Lease.SpaceId)) { Converter = App.convert.spaceId2spaceName });  
tenant.SetBinding(Run.TextProperty, new Binding(nameof(Lease.TenantId)) { Converter = App.convert.tenantId2TenantName });  

and similarly in the Transaction view, those values like BD: A | Receivable => Rent ... have been converted in this way:

...  
var tenant = new FrameworkElementFactory(typeof(Run)) { Name = "tenant" };  
...  
space.SetBinding(Run.TextProperty, new Binding(nameof(Transaction.SpaceId)) { Converter = App.convert.spaceId2spaceName });  
tenant.SetBinding(Run.TextProperty, new Binding(nameof(Transaction.TenantId)) { Converter = App.convert.tenantId2TenantName });  
control.SetBinding(Run.TextProperty, new Binding(nameof(Transaction.ControlId)) { Converter = App.convert.controlId2ControlName });  
head.SetBinding(Run.TextProperty, new Binding(nameof(Transaction.HeadId)) { Converter = App.convert.headId2headName });  
isCash.SetBinding(BiState.IsTrueProperty, new Binding(nameof(Transaction.IsCash)));  
amount.SetBinding(TextBlock.TextProperty, new Binding(nameof(Transaction.Amount)) { StringFormat = "N0" });  

so these two views don't have string properties I could add filter on. Instead of changing Lease and Transaction models, I wanted to search the content of the ListBoxItem and hide/show based on the query string of the searchbox. I've this event and handler in the Transaction view:

search.TextChanged += onQueryChanged;  
void onQueryChanged(string query) {  
    Debug.WriteLine(query);  
    foreach (Transaction item in transactions.Items) {  
        var listItem = (ListBoxItem)transactions.ItemContainerGenerator.ContainerFromItem(item);  
        var grid = listItem.ContentTemplate;  
        //grid.FindName("tenant", grid.VisualTree);  
        //listItem.Visibility = Visibility.Collapsed;  
    }  
}  

now, how to get the tenant string from the ContentTemplate and collapse ListBoxItem?

Developer technologies | Windows Presentation Foundation
0 comments No comments
{count} votes

Accepted answer
  1. DaisyTian-1203 11,646 Reputation points
    2021-05-19T05:11:17.57+00:00

    I made a sample to filter objects in ListBox, the groups hide when filter condition doesn't exist. It may give you some help.
    XAML code:

     <Window.DataContext>  
            <local:ViewModel></local:ViewModel>  
        </Window.DataContext>  
        <Grid>  
            <Grid.RowDefinitions>  
                <RowDefinition Height="30"></RowDefinition>  
                <RowDefinition></RowDefinition>  
            </Grid.RowDefinitions>  
            <StackPanel Orientation="Horizontal" Margin="2">  
                <Label>Name:</Label>  
                <TextBox Width="100" Text="{Binding NameFilterStr,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>  
            </StackPanel>  
            <ListBox Name="lbMain"  Grid.Row="1" ItemsSource="{Binding StudentListView}">  
                <ListBox.ItemTemplate>  
                    <DataTemplate>  
                        <StackPanel Orientation="Horizontal">  
                            <TextBlock Text="{Binding Name}"  
                                Width="150" />  
                            <TextBlock Text="{Binding Age}"  
                                Width="100" />  
                            <TextBlock Text="{Binding Description}"  
                                Width="100" />  
                        </StackPanel>  
                    </DataTemplate>  
                </ListBox.ItemTemplate>  
                <ListBox.GroupStyle>  
                    <GroupStyle>  
                        <GroupStyle.ContainerStyle>  
                            <Style TargetType="{x:Type GroupItem}">  
                                <Setter Property="Template">  
                                    <Setter.Value>  
                                        <ControlTemplate TargetType="{x:Type GroupItem}">  
                                            <Expander IsExpanded="True" ExpandDirection="Down">  
                                                <Expander.Header>  
                                                    <StackPanel Orientation="Horizontal">  
                                                        <TextBlock Text="Age:"></TextBlock>  
                                                        <TextBlock Text="{Binding Path=Name}" VerticalAlignment="Center" />  
                                                        <TextBlock Text="{Binding Path=ItemCount, StringFormat=Count:{0}}"  
                                                            VerticalAlignment="Center"  
                                                            Margin="5,0,0,0" />  
                                                    </StackPanel>  
                                                </Expander.Header>  
                                                <ItemsPresenter />  
                                            </Expander>  
                                        </ControlTemplate>  
                                    </Setter.Value>  
                                </Setter>  
                            </Style>  
                        </GroupStyle.ContainerStyle>  
                    </GroupStyle>  
                </ListBox.GroupStyle>  
            </ListBox>  
        </Grid>  
    

    C# code is:

    public class ViewModel : INotifyPropertyChanged  
        {  
            public event PropertyChangedEventHandler PropertyChanged;  
      
            private List<Student> students = new List<Student>();  
            public List<Student> Students  
            {  
                get { return students; }  
                set { students = value; }  
            }  
      
            private string nameFilterStr;  
      
            public string NameFilterStr  
            {  
                get { return nameFilterStr; }  
                set  
                {  
                    nameFilterStr = value;  
                    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("NameFilterStr"));  
                    StudentListView.Refresh();  
                }  
            }  
      
            public ListCollectionView StudentListView { get; set; }  
            public ViewModel()  
            {  
                StudentListView = new ListCollectionView(Students);  
                Students.Add(new Student() { Name = "AA1", Age = 12, Description = "654646" });  
                Students.Add(new Student() { Name = "AA2", Age = 12, Description = "7967969" });  
                Students.Add(new Student() { Name = "AA3", Age = 13, Description = "11111123" });  
                Students.Add(new Student() { Name = "BB1", Age = 14, Description = "123131" });  
                Students.Add(new Student() { Name = "BB6", Age = 13, Description = "232525" });  
                Students.Add(new Student() { Name = "BB7", Age = 16, Description = "57474674" });  
      
                StudentListView.GroupDescriptions.Add(new PropertyGroupDescription("Age"));  
                StudentListView.Filter = Filter;  
            }  
            private bool Filter(object obj)  
            {  
                Student s = obj as Student;  
                if (s == null) return false;  
      
                if (string.IsNullOrEmpty(NameFilterStr))  
                {  
                    return true;  
                }  
                else if (!string.IsNullOrEmpty(NameFilterStr))  
                {  
                    if (s.Name.Contains(NameFilterStr))  
                    {  
                        return true;  
                    }  
                    return false;  
                }            
                else  
                {  
                    if (s.Name.Contains(NameFilterStr) )  
                    {  
                        return true;  
                    }  
                    return false;  
                }  
            }  
        }  
      
        public class Student  
        {  
            public string Name { get; set; }  
            public int Age { get; set; }  
            public string Description { get; set; }  
        }  
    

    The Result picture is:
    97738-3.gif


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Emon Haque 3,176 Reputation points
    2021-05-19T00:00:19.083+00:00

    FindVisualChild works, so with this:

    void onQueryChanged(string query) {  
        foreach (Transaction item in transactions.Items) {  
            var listItem = (ListBoxItem)transactions.ItemContainerGenerator.ContainerFromItem(item);  
            if (string.IsNullOrWhiteSpace(query))   
                listItem.Visibility = Visibility.Visible;  
                  
            else {  
                var presenter = FindVisualChild<ContentPresenter>(listItem);  
                var grid = listItem.ContentTemplate;  
                var tenant = (Run)grid.FindName("tenant", presenter);  
                if (!tenant.Text.ToLower().Contains(query.Trim().ToLower())) {  
                    listItem.Visibility = Visibility.Collapsed;  
                }  
            }  
        }  
    }  
    

    it works perfectly for A but for B there's a problem:

    97722-test.gif

    B doesn't exist in Africa and North America BUT those group names are still visible! I've tried with HidesIfEmpty = true in GroupStyle of the ListBox:

    transactions = new ListBox() {  
        BorderThickness = new Thickness(0, 0, 1, 0),  
        HorizontalContentAlignment = HorizontalAlignment.Stretch,  
        ItemTemplate = new TransactionTemplate(),  
        GroupStyle = {  
            new GroupStyle() {  
                HidesIfEmpty = true,  
                ContainerStyle = new Style(typeof(GroupItem)){  
                    Setters = {  
                        new Setter(GroupItem.TemplateProperty, new GroupedTransactionTemplate())  
                    }  
                }  
            }  
        }  
    };  
    

    BUT that doesn't work! How to hide those Groups where B doesn't exist?

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.