1

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;
            }
Filburt
  • 17,626
  • 12
  • 64
  • 115
LiveKarma
  • 221
  • 2
  • 13

3 Answers3

1

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:

enter image description here

  • Notice How simple and happy life is when you're using WPF's capabilities instead of a manual, procedural, winforms-like approach.
  • Simple, simple properties and 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.
  • See how I'm operating against my Data in the SelectAll() and UnSelectAll() methods, rather than the UI. The UI is not responsible for maintaining the state of data, only for showing it.
  • Copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.
  • WPF Rocks
Community
  • 1
  • 1
Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
  • 1
    First of all thank you very much for your answer. However I have just tried to use it with my code but it does not work. No link between the selected item and checkbox is made. The only thing it has done is that is has altered the checkbox theme and now the list item select color has gone from my green to windows blue like in your picture. – LiveKarma Dec 11 '13 at 09:57
  • @user2911340 make sure you don't have any other conflicting `Styles` in your project. If you do, you will have to merge the `ItemContainerStyle` with your own style. – Federico Berasategui Dec 11 '13 at 12:41
  • 1
    I have put all your code in that you have given apart from the public MainWindow() {} area and I still cannot see a link between the listboxitem selected and checkbox selected. This is my code behind: http://pastebin.com/qTD2Ukxb and my wpf: http://pastebin.com/xfpBKUpX – LiveKarma Dec 16 '13 at 10:47
1

See the VisualHelper class here

It provides extension methods FindVisualChild, FindVisualChilds

var checkedCheckBoxes= LstDistro.FindVisualChilds<CheckBox>().Where(s=>s.IsChecked==true);
yo chauhan
  • 12,079
  • 4
  • 39
  • 58
1

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 :)

Alex
  • 1,052
  • 7
  • 19