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" 
+0

啊是的 - 这是有道理的,它的工作原理! 谢谢。 – IUnknown 2009-07-26 00:13:05

+0

它现在可以工作 - 但有不良副作用。当我展开第二个扩展器时,第一个会自动崩溃。有没有办法做到这一点,而不自动折叠前一个扩展项目? – IUnknown 2009-07-26 00:42:15

感谢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。这给我我正在寻找的经验。

  1. 有人第一次扩展扩展器时,它选择新扩展的ListBoxItem。

  2. 如果有人展开另一个Expander,则取消选择前一个ListBoxItem,但保持展开状态,则选择新展开的ListBoxItem。

  3. 如果有人折叠选定的扩展器,则取消选择ListBoxItem。

  4. 如果有几个展开器展开,有人折叠一个非选定的ListBoxItem展开器,则先前选定的ListBoxItem保持选定状态。

感谢您的帮助 - 我想这是任何人都非常有用的小代码片断谁在ListBox使用扩展器。