question

EmonHaque-1485 avatar image
0 Votes"
EmonHaque-1485 asked EmonHaque-1485 edited

How to collapse/hide ListBoxItem

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?

windows-wpf
test.gif (1.2 MiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

DaisyTian-1203 avatar image
1 Vote"
DaisyTian-1203 answered EmonHaque-1485 edited

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.


3.gif (24.2 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@DaisyTian-MSFT, if you've string properties, like Name, of your example, and add filter on that string property, it updates everything, including the ItemCount, perfectly. In my database, I don't store names in Lease and Transaction tables and these two models, of my app, were one-to-one mapping of those tables. I'd tried to add filter on the ListBox.Items but that didn't work, probably because I've ItemsSource property set.

0 Votes 0 ·
EmonHaque-1485 avatar image
0 Votes"
EmonHaque-1485 answered EmonHaque-1485 edited

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?


test.gif (77.6 KiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.