WPFリストボックス +エキスパンダーイベント
質問
リストボックスのアイテムテンプレートにエキスパンダーがあります。元気になります。私が遭遇した問題は、expanderが拡張および/または選択されたときにListBox_SelectionChangedイベントを発射したいということです。 Mousedownイベントは、ListBoxにバブルアップしていないようです。
必要なのは、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に依存しない別の方法では、エキスパンダーの拡張/崩壊したイベントを背後にあるコードにフックし、次のコードを使用してクリックしたListBoxインデックスを見つけることができます。
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 Control Controlがリストボックスの選択を取得することです。これを簡単にアーカイブすることができます。これは、クリックした即時のリストボックス項目に拡張されたエキスパンダーの等張りのプロパティをTwowayバインディングします。
<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を使用していました)。
また、インデックスを使用して、listbox.selectedIndexを選択または解除しました。これは私が探していた経験を私に与えてくれます。
誰かがエキスパンダーを初めて拡張したとき、新しく拡張されたListBoxItemを選択します。
誰かが別のエキスパンダーを拡張した場合、以前のリストボックス項目は選択されていませんが、拡張されたままで、新しく拡張されたListBoxItemが選択されます。
誰かが選択したエキスパンダーを倒すと、リストボックス項目は選択されていません。
いくつかの拡張器が拡張されている場合、誰かが選択されていないListBoxItemエキスパンダーを崩壊させる場合、以前に選択されたListBoxItemが選択されたままです。
助けてくれてありがとう - これは、リストボックスで拡張器を使用する人にとって非常に便利な小さなコードスニペットだと思います。