【问题标题】:Find Control in DataTemplate with a multi selection listbox使用多选列表框在 DataTemplate 中查找控件
【发布时间】:2013-12-10 16:37:27
【问题描述】:

我有一个多选列表框,用户可以在其中勾选列表中的多个项目。目前我有它,所以当一个复选框被勾选时,它所在的 ListBoxItem 也会被选中:

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

现在我正在尝试另一种方式。 Whenever a ListBoxItem is selected the checkbox within it gets ticked.到目前为止,我拥有它,因此您选择的第一个项目将被勾选,但之后您选择的其他项目都不会被勾选。我需要以某种方式让它遍历所有当前选定的项目。

我当前的代码:

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

【问题讨论】:

    标签: c# wpf visual-studio-2012 listbox datatemplate


    【解决方案1】:

    您应该像这样设置 Checkbox IsChecked 绑定:

    IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"
    

    这意味着每当您选择一个 ListBoxItem 时,您的复选框也会被选中。

    希望这有帮助:)

    【讨论】:

      【解决方案2】:

      VisualHelper class here

      提供了扩展方法FindVisualChild、FindVisualChilds

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

      【讨论】:

        【解决方案3】:

        好的。删除所有代码并重新开始。

        如果您正在使用 WPF,您确实需要了解并接受 The WPF Mentality

        这就是你在适当的 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>
        

        后面的代码:

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

        数据项:

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

        结果:

        • 注意当您使用 WPF 的功能而不是手动、程序化、类似 winforms 的方法时,生活是多么简单和幸福。
        • 简单、简单的属性和INotifyPropertyChanged。这就是您在 WPF 中编程的方式。无需复杂的VisualTreeHelper.Whatever() 东西,无需在程序代码中操作 UI。简单漂亮的DataBinding
        • 看看我是如何在SelectAll()UnSelectAll() 方法而不是UI 中对我的Data 进行操作的。 UI 不负责维护数据的状态,只负责显示它。
        • 复制我的代码并将其粘贴到 File -&gt; New Project -&gt; WPF Application 中,然后亲自查看结果。
        • WPF 摇滚

        【讨论】:

        • 首先非常感谢您的回答。但是我刚刚尝试将它与我的代码一起使用,但它不起作用。所选项目和复选框之间没有链接。它所做的唯一一件事就是改变了复选框主题,现在列表项选择颜色已经从我的绿色变为窗口蓝色,就像你的图片一样。
        • @user2911340 确保您的项目中没有任何其他冲突的Styles。如果这样做,您将不得不将ItemContainerStyle 与您自己的样式合并。
        • 我已将您提供的所有代码放入公共 MainWindow() {} 区域之外,但我仍然看不到所选列表框项和选中复选框之间的链接。这是我背后的代码:pastebin.com/qTD2Ukxb 和我的 wpf:pastebin.com/xfpBKUpX
        猜你喜欢
        • 1970-01-01
        • 2015-10-16
        • 1970-01-01
        • 2013-10-22
        • 2013-09-19
        • 2011-05-29
        • 1970-01-01
        • 2016-08-17
        • 1970-01-01
        相关资源
        最近更新 更多