2009-07-25 72 views
5

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

回答

1

其犯規依賴於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); 
4

你想要的是讓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

0

感謝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使用擴展器。