开发者

How to filter ListBox item ?

I define this simple class

 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 = "";
            }
      }


 }

Now, i define in the code this

 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");

And i define in the xaml 2 ListBox - First that show all the a1...a4 items Second that show all the b1...b4 items

Each item in the ListBox is CheckBox - and the开发者_StackOverflow社区 content is the string of the item.

Now i want to define filter that will show in some other list only the SimpleClass.Res that was checked in the listBox.

==> that mean that if in the listBox that items that was checked are b1 and a3 that the only text in the third listbox will contain

       a1_b1
       a3_b3

How can i do it ?

I trying to use the CollectionViewSource but i can't define multi filter in this cases.


In UI you will have to have the checkbox bound to something so that you are aware of the status of the checked items from both ListBoxes.

Hence we bind it TwoWay to the ancestor ListBoxItem's IsSelected property. This way when checkboxes are checked they automatically register the checked item under ListBox.SelectedItems property.

Based on this we perform multi binding ListBox3.ItemsSource to ListBox1.SelectedItems and ListBox2.SelectedItems. The multi value converter called MergeSelectedItemsHelper simply performs a union of the two selected items lists.

I have used Array of TextBlock instead of List of SimpleClass to bind to all those ListBoxes.

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>

Code Behind:

Now as ListBox.SelectedItems property is simply a non dependency property and also non-observable so the bindings will not automatically refresh. In code behind we will hage to refresh the binding when seletion is changed on the ListBox1 and ListBox2.

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

As said earlier, the multi value converter simply joins the selected items together and gets called when multi-binding is refreshed...

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
}

Hope this helps.


Put a boolean public property in SimpleClass and bind it to the checkbox. Then in the second list bind visibility to the property.

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


If your checked items are in your ViewModel, you can simply filter the collection view source using a delegate

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;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜