Reputation: 231
I have a multi selection listbox where a user can tick multiple items in the list. At the moment I have it so when a checkbox is ticked the ListBoxItem it is within also gets selected:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
//Select the Item using the DataContext of the Button
object clicked = (e.OriginalSource as FrameworkElement).DataContext;
var lbi = LstDistro.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
lbi.IsSelected = true;
}
Now I am trying to do it the other way. Whenever a ListBoxItem is selected the checkbox within it gets ticked. So far I have it so the first item you select will get ticked but after that none of the other items you select get ticked. I need to somehow have it loop through all the current selected items.
My current code:
WPF:
<ListBox x:Name="LstDistro" HorizontalAlignment="Left" Margin="10,10,0,42" Width="235" BorderBrush="Black" BorderThickness="2,2,1,1" SelectionMode="Multiple" SelectionChanged="LstDistro_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Canvas x:Name="EventItem" HorizontalAlignment="Left" Height="42" VerticalAlignment="Top" Width="215" Background="{Binding Path=LBackground}">
<Label Content="{Binding LInits}" HorizontalAlignment="Left" Height="33" VerticalAlignment="Top" Width="40" FontWeight="Bold" FontSize="12" Canvas.Top="5"/>
<Label Content="{Binding LFullName}" HorizontalAlignment="Left" Height="33" VerticalAlignment="Top" Width="164" FontSize="12" Canvas.Left="40" Canvas.Top="5"/>
<CheckBox x:Name="ChkName" Height="20" Width="20" Canvas.Left="190" Canvas.Top="12" Checked="CheckBox_Checked" IsChecked="False"/>
</Canvas>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
//Select the Item using the DataContext of the Button
object clicked = (e.OriginalSource as FrameworkElement).DataContext;
var lbi = LstDistro.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
lbi.IsSelected = true;
}
private void LstDistro_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Get the current selected item
ListBoxItem item = LstDistro.ItemContainerGenerator.ContainerFromIndex(LstDistro.SelectedIndex) as ListBoxItem;
CheckBox ChkName = null;
//Get the item's template parent
ContentPresenter templateParent = GetFrameworkElementByName<ContentPresenter>(item);
//Get the DataTemplate that the Checkbox is in.
DataTemplate dataTemplate = LstDistro.ItemTemplate;
ChkName = dataTemplate.FindName("ChkName", templateParent) as CheckBox;
ChkName.IsChecked = true;
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{ break; }
else if (child != null)
{
child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
Upvotes: 1
Views: 927
Reputation: 1072
You should set the Checkbox IsChecked binding like this:
IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"
This means whenever you select a ListBoxItem your checkbox will also become checked.
Hope this helped :)
Upvotes: 1
Reputation: 44068
Ok. Delete all that code and start all over.
If you're working with WPF, you really need to understand and embrace The WPF Mentality.
This is how you do what you're looking for, in proper WPF:
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<Button Content="Show Selected Item Count" Click="Button_Click"
DockPanel.Dock="Top"/>
<Button Content="Select All" Click="SelectAll"
DockPanel.Dock="Top"/>
<Button Content="Select All" Click="UnSelectAll"
DockPanel.Dock="Top"/>
<ListBox ItemsSource="{Binding}" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding DisplayName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DockPanel>
</Window>
Code behind:
public partial class MainWindow : Window
{
private ObservableCollection<SelectableItem> Items { get; set; }
public MainWindow()
{
InitializeComponent();
//Create dummy items, you will not need this, it's just part of the example.
var dummyitems = Enumerable.Range(0, 100)
.Select(x => new SelectableItem()
{
DisplayName = x.ToString()
});
DataContext = Items = new ObservableCollection<SelectableItem>(dummyitems);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Items.Count(x => x.IsSelected).ToString());
}
private void SelectAll(object sender, RoutedEventArgs e)
{
foreach (var item in Items)
item.IsSelected = true;
}
private void UnSelectAll(object sender, RoutedEventArgs e)
{
foreach (var item in Items)
item.IsSelected = false;
}
}
Data Item:
public class SelectableItem:INotifyPropertyChanged
{
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
public string DisplayName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Result:
INotifyPropertyChanged
. That's how you program in WPF. No need for complicated VisualTreeHelper.Whatever()
stuff, no need to manipulate the UI in procedural code. Just simple, beautiful DataBinding.SelectAll()
and UnSelectAll()
methods, rather than the UI. The UI is not responsible for maintaining the state of data, only for showing it.File -> New Project -> WPF Application
and see the results for yourself.Upvotes: 1
Reputation: 12315
See the VisualHelper class here
It provides extension methods FindVisualChild, FindVisualChilds
var checkedCheckBoxes= LstDistro.FindVisualChilds<CheckBox>().Where(s=>s.IsChecked==true);
Upvotes: 1