WPF ListBox + Expander事件
题
我在ListBox的ItemTemplate中有一个扩展器。渲染很好。我遇到的问题是,我希望ListBox_SelectionChanged事件在扩展和/或选择时发射。 Mousedown事件似乎并没有冒出ListBox。
我需要的是列表框的SelectedIndex。由于ListBox_SelectionChanged不会被触发,因此索引为-1,我无法确定已选择哪个项目。
如果用户在扩展后单击扩展器的内容,则将触发ListBox_SelectionChanged事件。如果他们只单击扩展器,则不会开除该事件。这使用户感到困惑,因为从视觉上讲,他们认为实际单击扩展器标头时已经单击该项目。我需要在用户扩展扩展扩展器时选择的列表框项目,因为就用户而言,现在选择该项目时已选择该项目。
有任何建议,如何使其工作或确定列表框中的选择框架的其他方法?
简化的代码以供参考:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
Loaded="Window_Loaded">
<Grid Name="Root">
<ScrollViewer>
<ListBox SelectionChanged="ListBox_SelectionChanged" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate >
<DataTemplate>
<Border>
<Expander>
<Expander.Header>
<TextBlock Text="{Binding Path=Name}"/>
</Expander.Header>
<Expander.Content>
<StackPanel>
<TextBlock Text="{Binding Path=Age}"/>
<TextBlock Text="Line 2"/>
<TextBlock Text="Line 3"/>
</StackPanel>
</Expander.Content>
</Expander>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</Window>
简单的绑定类:
public class Person
{
public string Name {
get;
set;
}
public int Age {
get;
set;
}
}
创建和填充数据以绑定:
private void Window_Loaded(object sender, RoutedEventArgs e) {
data = new ObservableCollection<Person>();
data.Add(new Person {
Name = "One",
Age=10
});
data.Add(new Person {
Name = "Two",
Age = 20
});
data.Add(new Person {
Name = "Three",
Age = 30
});
Root.DataContext = data;
}
这是我需要的事件(实际上只是我需要的SelectedIndex)
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ListBox box = (ListBox)sender;
// This value is not set because events from Expander are not bubbled up to fire SelectionChanged Event
int index = box.SelectedIndex;
}
解决方案
一种不取决于该问题的替代方式,您可以将扩展的扩展式事件连接到后面的代码上,并使用以下代码来查找单击的列表框索引。
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is ListViewItem))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
int index = yourListBox.ItemContainerGenerator.IndexFromContainer(dep);
其他提示
您想要的是获取扩展器控件控制列表框选择。您可以通过在膨胀属性上设置二线绑定到您单击的即时listBoxItem来轻松存档。
<Expander IsExpanded="{Binding IsSelected,Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
更新:如果您需要在选择另一个项目时避免自动折叠,请将列表框选择模式变为多个。
<ListBox SelectionMode="Multiple"
谢谢约翰。那很聪明。 WPF的兔子洞不断越来越深。
这是我根据您的建议所做的:
private void Expander_Expanded(object sender, RoutedEventArgs e) {
DependencyObject dep = (DependencyObject)sender;
while ((dep != null) && !(dep is ListBoxItem)) {
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
int index = PersonList.ItemContainerGenerator.IndexFromContainer(dep);
PersonList.SelectedIndex = index;
}
private void Expander_Collapsed(object sender, RoutedEventArgs e) {
DependencyObject dep = (DependencyObject)sender;
while ((dep != null) && !(dep is ListBoxItem)) {
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
int index = PersonList.ItemContainerGenerator.IndexFromContainer(dep);
if (PersonList.SelectedIndex == index)
PersonList.SelectedIndex = -1;
}
我必须将ListViewItem更改为ListBoxItem(我正在使用listBox)。
另外,我使用索引选择或删除listbox.selectedIndex。这给了我我想要的经验。
第一次有人扩展扩展器时,它选择了新扩展的ListBoxItem。
如果有人扩展了另一个扩展器,则取消了先前的listBoxItem,但仍在扩展中,选择了新扩展的ListBoxItem。
如果有人折叠了选定的扩展器,则取消了ListBoxItem。
如果有几个扩展器扩展,则有人折叠了一个未选择的ListBoxItem eblander,则仍然选择了先前选择的ListBoxItem。
感谢您的帮助 - 我认为这是一个非常有用的小型代码段,对于使用ListBox中扩展程序的任何人。