[英]Get all selected items across multiple nested ListBoxes with SelectionMode Multiple/Extended
I have a List
of Lists
and display it with nested ListBoxes
: 我有一个List
的Lists
和嵌套显示它ListBoxes
:
MainWindow.xaml.cs MainWindow.xaml.cs
using System.Collections.Generic;
namespace WPF_Sandbox
{
public partial class MainWindow
{
public IEnumerable<IEnumerable<string>> ListOfStringLists { get; set; } = new[] { new[] { "a", "b" }, new[] { "c", "d" } };
public MainWindow()
{
InitializeComponent();
DoSomethingButton.Click += (sender, e) =>
{
// do something with all selected items
};
}
}
}
MainWindow.xaml MainWindow.xaml
<Window x:Class="WPF_Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
x:Name="ThisControl">
<StackPanel>
<ListBox ItemsSource="{Binding ListOfStringLists, ElementName=ThisControl}">
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<ListBox ItemsSource="{Binding}" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<TextBlock Text="{Binding}" />
</ItemContainerTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ItemContainerTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="DoSomethingButton" Content="DoSomething" />
</StackPanel>
</Window>
How can I get all selected items across all ListBoxes
? 如何在所有ListBoxes
获取所有选定的项目?
I found a few solutions getting one selected item but could not figure out how to do applie those in my scenario. 我找到了一些 解决方案获得一个选定的项目,但无法弄清楚如何在我的方案中应用那些。
I have an idea on how to do this by wrapping the string
arrays but I would prefer not doing this. 我有一个关于如何通过包装string
数组来做到这一点的想法,但我宁愿不这样做。
The easiest way would be to iterate through the items in the ListBox
es: 最简单的方法是迭代ListBox
es中的项:
private void DoSomethingButton_Click(object sender, RoutedEventArgs e)
{
List<string> selectedStrings = new List<string>();
foreach (IEnumerable<string> array in outerListBox.Items.OfType<IEnumerable<string>>())
{
ListBoxItem lbi = outerListBox.ItemContainerGenerator.ContainerFromItem(array) as ListBoxItem;
if (lbi != null)
{
ListBox innerListBox = GetChildOfType<ListBox>(lbi);
if (innerListBox != null)
{
foreach (string selectedString in innerListBox.SelectedItems.OfType<string>())
selectedStrings.Add(selectedString);
}
}
}
}
private static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null)
return result;
}
return null;
}
Note that the ListBoxItem
may be virtualized away if you have a lot of inner IEnumerable<string>
. 请注意,如果您有很多内部IEnumerable<string>
, ListBoxItem
可能会被虚拟化。 You will then have to force the generation of the containers or disable UI virtualization: 然后,您必须强制生成容器或禁用UI虚拟化:
WPF ListView virtualization. WPF ListView虚拟化。 How to disable ListView virtualization? 如何禁用ListView虚拟化?
This may affect the performance negatively so if this is an issue you should probably consider binding to an IEnumerable<YourType>
and bind the SelectedItems
property of the inner ListBox
to a property of a YourType
using a behaviour. 这可能会对性能产生负面影响,因此如果这是一个问题,您应该考虑绑定到IEnumerable<YourType>
并使用行为将内部ListBox
的SelectedItems
属性绑定到YourType
的属性。
Since the SelectedItems
property of a ListBox
is read-only you can't bind to it directly: https://blog.magnusmontin.net/2014/01/30/wpf-using-behaviours-to-bind-to-readonly-properties-in-mvvm/ . 由于ListBox
的SelectedItems
属性是只读的,因此无法直接绑定到它: https : //blog.magnusmontin.net/2014/01/30/wpf-using-behaviours-to-bind-to-readonly- properties-in-mvvm / 。
I would just add an event handler to the inner ListBox
like so if not doing things the MVVM way: 我只是添加一个事件处理程序到内部ListBox
,如果没有做MVVM方式:
<ListBox ItemsSource="{Binding}" SelectionMode="Multiple" SelectionChanged="ListBox_SelectionChanged">
Then in your code behind implement the ListBox_SelectionChanged
like so: 然后在你的代码后面实现ListBox_SelectionChanged
如下所示:
public List<string> FlatStringList = new List<string>();
private void ListBox_SelectionChanged(object sender,System.Windows.Controls.SelectionChangedEventArgs e)
{
FlatStringList.AddRange(e.AddedItems.Cast<string>());
foreach(string s in e.RemovedItems)
{
FlatStringList.Remove(s);
}
}
This is assuming you don't mind storing the selected strings in a flat list. 这假设您不介意将所选字符串存储在平面列表中。 Then you could implement your DoSomething
button click event handler to do something with the FlatStringList
. 然后,您可以实现DoSomething
按钮单击事件处理程序以对FlatStringList
执行某些FlatStringList
。 Hope that helps. 希望有所帮助。
Why don't you create a wrapper (as you said): 你为什么不创建一个包装器(如你所说):
public class MyString : INotifyPropertyChanged
{
public MyString(string value) { Value = value; }
string _value;
public string Value { get { return _value; } set { _value = value; RaisePropertyChanged("Value"); } }
bool _isSelected;
public bool IsSelected { get { return _isSelected; } set { _isSelected = value; RaisePropertyChanged("IsSelected"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
Bind the IsSelected property of the ListBoxItems: 绑定ListBoxItems的IsSelected属性:
<StackPanel>
<ListBox ItemsSource="{Binding ListOfStringLists, ElementName=ThisControl}">
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<ListBox ItemsSource="{Binding}" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<TextBlock Text="{Binding Value}" />
</ItemContainerTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</ItemContainerTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="DoSomethingButton" Content="DoSomething" />
</StackPanel>
and you are already done: 你已经完成了:
public IEnumerable<IEnumerable<MyString>> ListOfStringLists { get; set; } = new[] { new[] { new MyString("a"), new MyString("b") { IsSelected = true } }, new[] { new MyString("c"), new MyString("d") } };
public MainWindow()
{
this.InitializeComponent();
DoSomethingButton.Click += (sender, e) =>
{
foreach (var i in ListOfStringLists)
foreach (var j in i)
{
if (j.IsSelected)
{
// ....
}
}
};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.