5

ListBox の ItemTemplate に Expander があります。うまくレンダリングします。私が遭遇した問題は、エキスパンダーが展開または選択されたときに ListBox_SelectionChanged イベントを発生させたいということです。MouseDown イベントは、ListBox までバブルアップしていないようです。

必要なのは、ListBox の SelectedIndex です。ListBox_SelectionChanged が起動されないため、インデックスは -1 になり、どの項目が選択されているかを判断できません。

ListBox_SelectionChanged イベントは、展開された後にユーザーが Expander のコンテンツをクリックすると発生します。エキスパンダーのみをクリックした場合、イベントは発生しません。これは、Expander ヘッダーを実際にクリックしたときに、そのアイテムを既にクリックしたと視覚的に考えるため、ユーザーを混乱させます。ユーザーが Expander を展開するときに ListBox Item を選択する必要があります。これは、ユーザーに関する限り、アイテムが実際には選択されていないときに選択されているためです。

これを機能させる方法、またはエキスパンダーを含むリストボックスの 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>

Binding の単純なクラス:

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;
}
4

3 に答える 3

4

あなたが望んでいたのは、Expander コントロールが ListBox Selection を制御するようにすることです。Expander の IsExpanded プロパティの TwoWay Binding を、クリックしたすぐの ListBoxItem に設定することで、これを簡単にアーカイブできます。

 <Expander IsExpanded="{Binding IsSelected,Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">

更新: 別の項目を選択したときに自動的に折りたたまれるのを避ける必要がある場合は、リストボックスの選択モードを複数にします。

<ListBox SelectionMode="Multiple"
于 2009-07-26T00:07:34.360 に答える
1

IsSelected に依存しない別の方法として、エキスパンダーの Expanded/Collapsed イベントをコード ビハインドにフックし、次のコードを使用して、クリックした 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);
于 2009-07-26T05:57:04.287 に答える
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を使用していました)。

また、インデックスを使用して、ListBox.SelectedIndexを選択または選択解除しました。これは私が探していた経験を私に与えてくれます。

  1. 誰かが初めてExpanderを展開すると、新しく展開されたListBoxItemが選択されます。

  2. 誰かが別のExpanderを展開すると、前のListBoxItemは選択解除されますが、展開されたままの場合、新しく展開されたListBoxItemが選択されます。

  3. 誰かが選択したエキスパンダーを折りたたむと、ListBoxItemの選択が解除されます。

  4. 複数のエキスパンダーが展開されている場合、誰かが選択されていないListBoxItemエキスパンダーを折りたたむと、以前に選択されたListBoxItemが選択されたままになります。

助けてくれてありがとう-これは、リストボックスでエキスパンダーを使用する人にとって非常に便利な小さなコードスニペットだと思います。

于 2009-07-26T22:47:36.873 に答える