Find Control in DataTemplate with a multi selection listbox

我的未来我决定 提交于 2019-12-24 12:12:06

问题


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

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




回答2:


See the VisualHelper class here

It provides extension methods FindVisualChild, FindVisualChilds

var checkedCheckBoxes= LstDistro.FindVisualChilds<CheckBox>().Where(s=>s.IsChecked==true);



回答3:


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:

  • 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


来源:https://stackoverflow.com/questions/20500042/find-control-in-datatemplate-with-a-multi-selection-listbox

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!