9

I've got a StackPanel with a group of expanders in, how do I set it so that only expander is expanded at any one time?

Cheers

AW

AwkwardCoder
  • 24,893
  • 27
  • 82
  • 152
  • 1
    I found an elegant, XAML-only implementation of this requirement. Click [here!](http://social.msdn.microsoft.com/forums/en-US/wpf/thread/a2988ae8-e7b8-4a62-a34f-b851aaf13886) – Dabblernl Apr 09 '10 at 19:53
  • 404 - File or directory not found. http://forums.msdn.microsoft.com/en-US/wpf/thread/a2988ae8-e7b8-4a62-a34f-b851aaf13886#expander_list – schmoopy Dec 12 '14 at 23:48
  • in XAML https://stackoverflow.com/questions/897146/making-a-collection-of-wpf-expanders-expand-exclusively-i-e-only-one-expande – Sebastian Charrier Aug 24 '17 at 00:17

4 Answers4

4

I didn't really want to do it like this as it required putting code (C#) in the class behind file for the window (I'm trying to avoid this completely by use of ViewModels etc).

Ideally I would have described this in XAML.

I hooked up every Expander 'Expanded' event I was interested in and did the following:

    private void HandleExpanderExpanded(object sender, RoutedEventArgs e)
    {
        ExpandExculsively(sender as Expander);
    }

    private void ExpandExculsively(Expander expander)
    {
        foreach (var child in findPanel.Children)
        {
            if (child is Expander && child != expander)
                ((Expander)child).IsExpanded = false;
        }
    }

Cheers

AWC

AwkwardCoder
  • 24,893
  • 27
  • 82
  • 152
  • So, that's how you'd do it in an old-school framework like WinForms. It is not "the WPF way," though, because for WPF to be fully robust, you want to derive state from data, not from events. – Jon Watte Dec 25 '09 at 18:27
  • 1
    I realise this, hence my comments, but I'm happy with this in the code behind – AwkwardCoder Dec 28 '09 at 16:56
  • 1
    Yea you can always put it in a listbox - boooo --- your way is MUCH BETTER - its not the WPF way but .. doing it wrong to make it the WPF way is not right either! https://social.msdn.microsoft.com/Forums/vstudio/en-US/a2988ae8-e7b8-4a62-a34f-b851aaf13886/windows-presentation-foundation-faq?forum=wpf#expander_list – schmoopy Dec 12 '14 at 23:51
2

User "Dabblernl" posted this as a comment, but it deserves being an answer because it is a perfect XAML-only solution, nicely customizable and without any hacks.

The idea is to place Expander controls in a ListBox, and bind the Expander.IsExpanded property to the ListBoxItem.IsSelected property.

Here is a ready-made example that you can just paste into a XAML file and try it out:

<ListBox>
    <ListBox.Resources>
        <Style TargetType="{x:Type Expander}">
            <Setter Property="IsExpanded" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
        </Style>
    </ListBox.Resources>
    <ListBox.Template>
        <ControlTemplate TargetType="{x:Type ListBox}">
            <ItemsPresenter />
        </ControlTemplate>
    </ListBox.Template>
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <ContentPresenter Content="{TemplateBinding Content}" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
    <Expander Background="Gray" Width="243" Header="Expander1">
        <StackPanel>
            <RadioButton Content="Eat Me" GroupName="Two" />
            <RadioButton Content="Eat Pork" GroupName="Two" />
            <RadioButton Content="Eat at Joe's" GroupName="Two" />
        </StackPanel>
    </Expander>
    <Expander Background="Gray" Width="243" Header="Expander2">
        <StackPanel>
            <RadioButton Content="Pork" GroupName="Two" />
            <RadioButton Content="Beef" GroupName="Two" />
            <RadioButton Content="Chicken" GroupName="Two" />
        </StackPanel>
    </Expander>
    <Expander Background="Gray" Width="243" Header="Expander3">
        <StackPanel>
            <RadioButton Content="Grill" GroupName="Two" />
            <RadioButton Content="Bake" GroupName="Two" />
            <RadioButton Content="Fry" GroupName="Two" />
        </StackPanel>
    </Expander>
</ListBox>

This used to be here: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3061227&SiteID=1 but forums.microsoft.com seems to have gone belly up.

It can still be found on this page: https://social.msdn.microsoft.com/Forums/en-US/a2988ae8-e7b8-4a62-a34f-b851aaf13886/windows-presentation-foundation-faq?forum=wpf under the title "3.4 How to keep only one Expander control opened in a group of Expander controls?"

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • 1
    This is perfect! I'm using this with some custom controls as the items and it was surprisingly easy enough to make the control inherit Expander (instead of UserControl) and drop it in here. Ironically never would have found this if not for the new "trending sort" in stackoverflow. – UndeadBob Jun 24 '22 at 19:39
1

You can add a dependency property whose value is "the expander that is expanded," and then you can bind the "expanded" property to the expression "theExpandedProperty == this" using your favorite expression binding technique (type converter, etc).

Jon Watte
  • 6,579
  • 4
  • 53
  • 63
1

Here is a more elaborate way to do it in WPF without any code behind:

<UserControl.Resources>
    <ResourceDictionary>
        ...
        <Style TargetType="{x:Type Expander}">
            <Setter Property="IsExpanded" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"/>
        </Style>

    </ResourceDictionary>

</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="{x:Null}" Margin="10">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ScrollViewer VerticalAlignment="Top" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" VerticalContentAlignment="Top" BorderThickness="0,0,0,0" Grid.RowSpan="1" Grid.Row="0">
        <ListBox x:Name="OrdersListBox" BorderThickness="0" ItemContainerStyle="{StaticResource ShellThemeListBoxStyle}" 
                 IsSynchronizedWithCurrentItem="True" 
                 prism:RegionManager.RegionName="{x:Static uiCommon:RegionNames.WorkSheetsRegion}" Background="#00000000">
            <ListBox.ItemTemplate>
                <DataTemplate DataType="typeData:WorkSheetsDetialsViewModel">
                    <local:WorkSheetsDetialsView/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </ScrollViewer>
    ...
</Grid>

<UserControl 
    x:Class="Module.ExcelDocumentManager.WorkSheets.WorkSheetsDetialsView"
    ...>
    <Expander>
        <Expander.Header>
            <TextBlock Text="{Binding HeaderInfo}" RenderTransformOrigin=".5,.5">
            </TextBlock>
        </Expander.Header>
        ...

    </Expander>
</UserControl>
Nasheayahu
  • 49
  • 8