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;
精彩评论