LiveKarma
LiveKarma

Reputation: 231

Find Control in DataTemplate with a multi selection listbox

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

Answers (3)

Alex
Alex

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

Fede
Fede

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:

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

Upvotes: 1

yo chauhan
yo chauhan

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

Related Questions