Как фильтровать элемент списка?

#wpf #silverlight

#wpf #silverlight

Вопрос:

Я определяю этот простой класс

  public class SimpleClass
 {
      public string Val1 {get;set;};
      public string Val2 {get;set;}
      public string Res
      {
            get
            {
                 string.Format("{0}_{1}", Val1, Val2 );
            }
      }

      public SimpleClass(string v1, string v2)
      {
             Val1 = v1;
             Val2 = v2;
      }

      public SimpleClass(string v1, int i)
      {
            if(i == 0)
            {
                 Val1 = v1;
                 val2 = "";
            }

            if(i == 0)
            {
                 Val2 = v1;
                 val1 = "";
            }
      }


 }
  

Теперь я определяю в коде это

  List< SimpleClass > myList = new List<SimpleClass>();
 myList.Add(new SimpleClass("a1", "b1");
 myList.Add(new SimpleClass("a2", "b2");
 myList.Add(new SimpleClass("a3", "b3");
 myList.Add(new SimpleClass("a4", "b4");
  

И я определяю в списке xaml 2 —
Сначала отображаются все элементы a1 … a4
Второй, который показывает все элементы b1 … b4

Каждый элемент в списке является флажком, а содержимое — строкой элемента.

Теперь я хочу определить фильтр, который будет отображать в каком-либо другом списке только SimpleClass.Res, который был отмечен в списке.

==> это означает, что если в списке отмеченные элементы являются b1 и a3, то единственный текст в третьем списке будет содержать

        a1_b1
       a3_b3
  

Как я могу это сделать?

Я пытаюсь использовать CollectionViewSource, но в этом случае я не могу определить мультифильтр.

Комментарии:

1. К чему привязан пользователь CheckBox ?

2. Ничего. просто отфильтруйте текст, который отображается на флажке

3. Вы пытаетесь добиться множественного выбора? Если это так, то флажок должен быть привязан к чему-то, не так ли?

4. допустим, у меня уже есть метод, который преобразует флажок, привязанный к некоторой строке. и теперь мне нужно отфильтровать элементы представления

Ответ №1:

В пользовательском интерфейсе вам нужно будет привязать флажок к чему-либо, чтобы вы были осведомлены о статусе отмеченных элементов из обоих списков.

Следовательно, мы привязываем его TwoWay к свойству ListBoxItem предка IsSelected . Таким образом, когда флажки установлены, они автоматически регистрируют отмеченный элемент в ListBox.SelectedItems свойстве.

На основе этого мы выполняем множественную привязку ListBox3.ItemsSource к ListBox1.SelectedItems и ListBox2.SelectedItems . Вызываемый многозначный конвертер MergeSelectedItemsHelper просто выполняет объединение двух списков выбранных элементов.

Я использовал Array of TextBlock вместо List of SimpleClass для привязки ко всем этим спискам.

XAML:

    <StackPanel Orientation="Vertical">

        <StackPanel.Resources>

            <x:ArrayExtension x:Key="MyArraySource" Type="{x:Type TextBlock}">
                <TextBlock Text="A" Tag="A1" DataContext="A_A1"/>
                <TextBlock Text="B" Tag="B1" DataContext="B_B1"/>
                <TextBlock Text="C" Tag="C1" DataContext="C_C1"/>
                <TextBlock Text="D" Tag="D1" DataContext="D_D1"/>
            </x:ArrayExtension>

            <local:MergeSelectedItemsHelper 
                  x:Key="MergeSelectedItemsHelper"/>                

            <DataTemplate x:Key="ListBox1ItemTemplate">
                <CheckBox 
                      IsChecked="{Binding IsSelected, 
                                  RelativeSource={RelativeSource 
                                            AncestorType={x:Type ListBoxItem}},
                                  Mode=TwoWay}"
                      Content="{Binding Text}"/>
            </DataTemplate>
            <DataTemplate x:Key="ListBox2ItemTemplate">
                <CheckBox
                      IsChecked="{Binding IsSelected, 
                                  RelativeSource={RelativeSource 
                                             AncestorType={x:Type ListBoxItem}},
                                  Mode=TwoWay}"
                      Content="{Binding Tag}"/>
            </DataTemplate>

        </StackPanel.Resources>

        <ListBox x:Name="ListBox1"
                 SelectionMode="Extended"
                 Margin="10"
                 ItemsSource="{StaticResource MyArraySource}"
                 ItemTemplate="{StaticResource ListBox1ItemTemplate}"
                 SelectionChanged="ListBox1_SelectionChanged">
        </ListBox>

        <ListBox x:Name="ListBox2"
                 SelectionMode="Extended"
                 Margin="10"
                 ItemsSource="{StaticResource MyArraySource}"
                 ItemTemplate="{StaticResource ListBox2ItemTemplate}"
                 SelectionChanged="ListBox1_SelectionChanged">
        </ListBox>

        <ListBox x:Name="ListBox3" Margin="10" DisplayMemberPath="DataContext">
            <ListBox.ItemsSource>
                <MultiBinding Converter="{StaticResource MergeSelectedItemsHelper}">
                    <Binding Path="SelectedItems" ElementName="ListBox1"/>
                    <Binding Path="SelectedItems" ElementName="ListBox2"/>
                </MultiBinding>
            </ListBox.ItemsSource>
        </ListBox>
    </StackPanel>
</StackPanel>
  

Код, лежащий в основе:

Теперь as ListBox.SelectedItems property — это просто свойство, не зависящее от зависимостей, а также ненаблюдаемое, поэтому привязки не будут автоматически обновляться. В приведенном ниже коде мы попытаемся обновить привязку при изменении выбора в ListBox1 и ListBox2 .

     private void ListBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var bndexp = BindingOperations.GetMultiBindingExpression(
                         ListBox3, ItemsControl.ItemsSourceProperty);
        if (bndexp != null)
        {
            bndexp.UpdateTarget();
        }
    }
  

Как говорилось ранее, преобразователь нескольких значений просто объединяет выбранные элементы вместе и вызывается при обновлении множественной привязки…

 public class MergeSelectedItemsHelper : IMultiValueConverter
{

    #region IMultiValueConverter Members

    public object Convert(
           object[] values,
           Type targetType, 
           object parameter, 
           System.Globalization.CultureInfo culture)
    {

        var list1 = values[0] as IList;
        var list2 = values[1] as IList;

        var validList2 = list2 ?? new List<object>();

        return list1 != null
                 ? list1.Cast<object>().Union(validList2.Cast<object>())
                    : validList2.Cast<object>();
    }

    public object[] ConvertBack(
           object value,
           Type[] targetTypes,
           object parameter,
           System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}
  

Надеюсь, это поможет.

Ответ №2:

Поместите логическое общедоступное свойство в SimpleClass и привяжите его к флажку. Затем во втором списке привязать видимость к свойству.

     <DataTrigger Binding="{Binding Path=DispDetail, Mode=OneWay}" Value="False">
          <Setter Property="Visibility" Value="Collapsed"/>
    </DataTrigger
  

Ответ №3:

Если ваши отмеченные элементы находятся в вашей ViewModel, вы можете просто отфильтровать источник представления коллекции с помощью делегата

 ICollectionView view = CollectionViewSource.GetDefaultView(this.myList);
view.Filter = obj =>
{
    var sc = obj as SimpleClass;

    // Do your checks to see if this object is valid, and return a bool
    // For example,
    return SelectedOptions.Contains(sc.Val1) || 
           SelectedOptions.Contains(sc.Val2);
};

return view;