WPF Expander y Splitter trabajando juntos..

Bueno aquí dejo publicado la solución de un problema que recientemente tuve haciendo que funcionaran juntos un Expander y un splitter, bueno la meta final era tener 2 expanders separados por 1 splitter y que cada expander contuviera algún tipo de grid o incluso una vista, con este aproach ya se puede jugar con lo que uno quiera poner en cada region.

Yo anduve buscando algo similar y no pude encontrar mucho al respecto así que aquí dejo mi solución y espero que a alguien pueda servirle..

Aquí tenemos el XAML con el código que contiene al expander y al splitter o más bien dos expander conteniendo un grid cada uno separados por un splitter..

View.xaml

<window x:Class="ExpanderAndSplitter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" x:Name="MainWdw">

    <grid x:Name="MainContainer" Loaded="MainContainer_Loaded" Margin="5,5,5,5" VerticalAlignment="Stretch"
          HorizontalAlignment="Stretch" Background="Transparent">
        </grid><grid .ColumnDefinitions>
            <columndefinition Width="Auto" />
            <columndefinition Width="Auto" />
            <columndefinition Width="Auto" />
            <columndefinition Width="*" />
            <columndefinition Width="Auto" />
        </grid>
        <grid .RowDefinitions>
            <rowdefinition Height="Auto"/>
            <rowdefinition Height="*"/>
        </grid>

        <label x:Name="DummyLabel" Content="Dummy Label" />
        <combobox x:Name="DummyComboBox" Grid.Column="1" MinWidth="100"
                      Margin="0,0,5,0"/>

        <button Grid.Column="2" Width="100" Content="Dummy Button" />
        <label x:Name="Label" Content="Dummy Label 2" Grid.Column="4" HorizontalAlignment="Right" />

        <grid Grid.Row="1" Grid.ColumnSpan="5"
              VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Transparent">
            </grid><grid .ColumnDefinitions>
                <columndefinition />
            </grid>
            <grid .RowDefinitions>
                <rowdefinition x:Name="MasterRow" Height="{Binding MasterRowHeight, Mode=TwoWay}" />
                <rowdefinition x:Name="SplitterRow" Height="{Binding SplitterRowHeight, Mode=TwoWay}" />
                <rowdefinition x:Name="DetailRow" Height="{Binding DetailRowHeight, Mode=TwoWay}" />
            </grid>

            <expander x:Name="MasterExpander" IsExpanded="True"
                      BorderBrush="Gray" Margin="0,5,0,0"
                      BorderThickness="1"  IsEnabled="True"
                      ExpandDirection="Down" Background="Transparent"
                      Expanded="MasterExpander_Expanded"
                      Collapsed="MasterExpander_Collapsed" >
                </expander><expander .Header>
                    <textblock FontWeight="Bold" Text="Master Region" />
                </expander>

                <grid Background="Transparent">
                    </grid><grid .ColumnDefinitions>
                        <columndefinition Width="*" />
                    </grid>
                    <grid .RowDefinitions>
                        <rowdefinition Height="*"/>
                    </grid>

                    <datagrid x:Name="MasterGrid"
                              Loaded="MasterGrid_Loaded"
                              AutoGenerateColumns="True"
                              CanUserDeleteRows="False"
                              ColumnWidth="*"
                              HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

            <gridsplitter Grid.Row="1" Background="Gray"
                      BorderThickness="1,1,1,1" Width="Auto" HorizontalAlignment="Stretch"
                      Height="6" VerticalContentAlignment="Top"
                      Padding="0,0,0,0" Margin="5,0,5,0" VerticalAlignment="Center"/>

            <expander x:Name="DetailExpander" Grid.Row="2" Margin="0,5,0,0"
                      BorderBrush="Gray" BorderThickness="1"  IsEnabled="True"
                      ExpandDirection="Down" Background="Transparent"
                      Expanded="DetailExpander_Expanded"
                      Collapsed="DetailExpander_Collapsed" >
                </expander><expander .Header>
                    <textblock FontWeight="Bold" Text="Detail Region" />
                </expander>

                <grid Background="Transparent">
                    </grid><grid .ColumnDefinitions>
                        <columndefinition Width="*" />
                    </grid>
                    <grid .RowDefinitions>
                        <rowdefinition Height="*"/>
                    </grid>

                    <datagrid x:Name="DetailGrid"
                              Loaded="DetailGrid_Loaded"
                              AutoGenerateColumns="True"
                              CanUserDeleteRows="False"
                              ColumnWidth="*"
                              HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

</window>

Y su respectivo code behind que se encarga de hacer las operaciones correspondientes de tamaño para que estos controles funcionen armoniosamente uno con el otro..

View.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace ExpanderAndSplitter
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        #region Fields

        private static double _oldMasterHeight = 1;
        private static double _oldDetailHeight = 1;

        #endregion // Fields

        #region Properties

        public GridLength MasterRowHeight
        {
            get { return (GridLength)GetValue(MasterRowHeightProperty); }
            set { SetValue(MasterRowHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MasterRowHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MasterRowHeightProperty =
            DependencyProperty.Register("MasterRowHeight", typeof(GridLength), typeof(MainWindow), new UIPropertyMetadata(new GridLength(1, GridUnitType.Star)));

        public GridLength SplitterRowHeight
        {
            get { return (GridLength)GetValue(SplitterRowHeightProperty); }
            set { SetValue(SplitterRowHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SplitterRowHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SplitterRowHeightProperty =
            DependencyProperty.Register("SplitterRowHeight", typeof(GridLength), typeof(MainWindow), new UIPropertyMetadata(new GridLength(0)));

        public GridLength DetailRowHeight
        {
            get { return (GridLength)GetValue(DetailRowHeightProperty); }
            set { SetValue(DetailRowHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DetailRowHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DetailRowHeightProperty =
            DependencyProperty.Register("DetailRowHeight", typeof(GridLength), typeof(MainWindow), new UIPropertyMetadata(new GridLength(1, GridUnitType.Auto)));

        public ObservableCollection<DummyItem> MasterItems
        {
            get;
            set;
        }

        public ObservableCollection<DummyItem> DetailItems
        {
            get;
            set;
        }

        #endregion // Properties

        #region Constructors

        public MainWindow()
        {
            InitializeComponent();

            MasterItems = new ObservableCollection<DummyItem>();
            DetailItems = new ObservableCollection<DummyItem>();

            for (int i=0; i<1000; i++)
            {
                MasterItems.Add(new DummyItem("Master Dummy " + i.ToString(), i.ToString()));
                DetailItems.Add(new DummyItem("Detail Dummy " + i.ToString(), i.ToString()));
            }
        }

        #endregion // Constructors

        #region Events

        private void MasterExpander_Expanded(object sender, RoutedEventArgs e)
        {
            DoHandleExpandCollapse();
        }

        private void MasterExpander_Collapsed(object sender, RoutedEventArgs e)
        {
            if (DetailExpander.IsExpanded)
            {
                _oldMasterHeight = MasterRowHeight.Value;
                _oldDetailHeight = DetailRowHeight.Value;
            }
            DoHandleExpandCollapse();
        }

        private void DetailExpander_Expanded(object sender, RoutedEventArgs e)
        {
            DoHandleExpandCollapse();
        }

        private void DetailExpander_Collapsed(object sender, RoutedEventArgs e)
        {
            if (MasterExpander.IsExpanded)
            {
                _oldMasterHeight = MasterRowHeight.Value;
                _oldDetailHeight = DetailRowHeight.Value;
            }
            DoHandleExpandCollapse();
        }

        private void MasterGrid_Loaded(object sender, RoutedEventArgs e)
        {
            if (MasterGrid.ItemsSource == null)
                MasterGrid.ItemsSource = MasterItems;
        }

        private void DetailGrid_Loaded(object sender, RoutedEventArgs e)
        {
            if (DetailGrid.ItemsSource == null)
                DetailGrid.ItemsSource = DetailItems;
        }

        private void MainContainer_Loaded(object sender, RoutedEventArgs e)
        {
            MainContainer.DataContext = this;
        }

        #endregion // Events

        #region Helpers

        private void DoHandleExpandCollapse()
        {
            if (MasterExpander == null || DetailExpander == null)
                return;

            SplitterRowHeight = new GridLength(0);
            if (MasterExpander.IsExpanded && DetailExpander.IsExpanded)
            {
                MasterRowHeight = new GridLength(_oldMasterHeight, GridUnitType.Star);
                DetailRowHeight = new GridLength(_oldDetailHeight, GridUnitType.Star);
                SplitterRowHeight = new GridLength(20);
                return;
            }

            if (MasterExpander.IsExpanded)
                MasterRowHeight = new GridLength(1, GridUnitType.Star);
            else
                MasterRowHeight = new GridLength(1, GridUnitType.Auto);

            if (DetailExpander.IsExpanded)
                DetailRowHeight = new GridLength(1, GridUnitType.Star);
            else
                DetailRowHeight = new GridLength(1, GridUnitType.Auto);
        }

        #endregion // Helpers

    }

    public class DummyItem
    {
        public DummyItem(string name, string id)
        {
            this.Name = name;
            this.ID = id;
        }
        public string Name
        {
            get;
            set;
        }
        public string ID
        {
            get;
            set;
        }
    }
}

Al final así es como luce la solución:
La solución ejemplo esta hecha con Visual Studio 2010 y el código fuente lo puedes bajar del siguiente link ExpanderAndSplitter.

¡Enjoy! 🙂
Yohan Rodriguez

Leave a Reply

Your email address will not be published. Required fields are marked *