WPF列表框+扩展器事件
我在ListBox的ItemTemplate中有一个扩展器。渲染正常。我遇到的问题是,我希望ListBox_SelectionChanged事件在扩展器展开和/或选择时触发。 MouseDown事件似乎没有起泡到ListBox。WPF列表框+扩展器事件
我需要的是ListBox的SelectedIndex。由于ListBox_SelectionChanged未被触发,因此索引为-1,我无法确定哪个项目已被选中。
如果用户在扩展器之后单击扩展器的内容,则会触发ListBox_SelectionChanged事件。如果他们只点击扩展器,事件不会被解雇。这使用户感到困惑,因为在视觉上,他们认为在实际点击扩展头时他们已经点击了该项目。当用户扩展扩展器时,我需要选择列表框项目,因为就用户而言,现在选择的项目实际上不是。
任何建议如何让这个工作或替代方式确定列表框的SelectedIndex与其中的扩展器?参考
简化代码:
<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;
}
其犯规依赖于IsSelected的另一种方法,你可以背后勾扩张的展开/折叠事件的代码,并使用下面的代码,找出所点击列表框指数。
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);
你想要的是让Expander控件控制ListBox的选择。您可以通过将Expander的IsExpanded属性上的TwoWay Binding设置为您单击的直接ListBoxItem来轻松进行归档。
<Expander IsExpanded="{Binding IsSelected,Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
更新:如果您需要避免在选择其他项目时自动折叠,请将列表框选择模式设置为多个。
<ListBox SelectionMode="Multiple"
感谢Jobi。这很聪明。 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.SelectedIndex。这给我我正在寻找的经验。
有人第一次扩展扩展器时,它选择新扩展的ListBoxItem。
如果有人展开另一个Expander,则取消选择前一个ListBoxItem,但保持展开状态,则选择新展开的ListBoxItem。
如果有人折叠选定的扩展器,则取消选择ListBoxItem。
如果有几个展开器展开,有人折叠一个非选定的ListBoxItem展开器,则先前选定的ListBoxItem保持选定状态。
感谢您的帮助 - 我想这是任何人都非常有用的小代码片断谁在ListBox使用扩展器。
啊是的 - 这是有道理的,它的工作原理! 谢谢。 – IUnknown 2009-07-26 00:13:05
它现在可以工作 - 但有不良副作用。当我展开第二个扩展器时,第一个会自动崩溃。有没有办法做到这一点,而不自动折叠前一个扩展项目? – IUnknown 2009-07-26 00:42:15