I’ve realized I needed an Accordion control for my WPF application. Implementing one seemed like a hassle, so I’ve looked for one. Amazingly, I have found that Silverlight has one, but WPF does not. Moreover – all the major control vendors don’t have such a control. Strange.
Btw, if you do come across one – do let me know.
The accordion had one flaw – it didn’t resize the children when it was resized, only when one of them expanded.
Ariel’s Ugly hack: resize them on the MeasureOverride() method. Problem solved.
I hate myself.
So, if you also need an Accordion control and want to hate yourself (or me!) – here’s the code.
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace TestGen2.UI.UIInfra
{
public class Accordion: StackPanel
{
private const string ExpandSiteName = "ExpandSite";
private const string HeaderSiteName = "HeaderSite";
static Accordion()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Accordion), new FrameworkPropertyMetadata(typeof(Accordion)));
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
InitializeAccordion();
}
private void InitializeAccordion()
{
foreach (Expander childExpender in Children.OfType<Expander>())
{
childExpender.Expanded += ChildExpanded;
}
}
private double _PreviousHeight;
protected override Size MeasureOverride(Size constraint)
{
var returnedSize = base.MeasureOverride(constraint);
if (constraint.Height == _PreviousHeight)
{
return returnedSize;
}
_PreviousHeight = constraint.Height;
var expandedExpander = Children.OfType<Expander>().Where(x => x.IsExpanded).FirstOrDefault();
if (expandedExpander != null)
{
double totalCollapsedExpandersHeight = Children.OfType<Expander>().Where(x => !x.IsExpanded).Sum(x => x.ActualHeight);
double accordionHeight = constraint.Height;
var expanderContentPresenter =
(ContentPresenter)(expandedExpander.Template.FindName(ExpandSiteName, expandedExpander));
var header = (FrameworkElement)(expandedExpander.Template.FindName(HeaderSiteName, expandedExpander));
expanderContentPresenter.Height = Math.Max(0,
accordionHeight - totalCollapsedExpandersHeight -
(header.ActualHeight + header.Margin.Top + header.Margin.Bottom +
expandedExpander.BorderThickness.Top + expandedExpander.BorderThickness.Bottom));
}
return returnedSize;
}
private void ChildExpanded(object sender, RoutedEventArgs e)
{
var selectedExpander = e.Source as Expander;
if (selectedExpander == null || !Children.OfType<Expander>().Contains(selectedExpander))
{
return;
}
double totalExpanderHeight = 0;
foreach (Expander otherExpander in Children.OfType<Expander>())
{
if (otherExpander == selectedExpander)
{
continue;
}
if (otherExpander.IsExpanded)
{
var contentPresenter = otherExpander.Template.FindName(ExpandSiteName, otherExpander) as ContentPresenter;
if(contentPresenter != null)
{
totalExpanderHeight -= contentPresenter.ActualHeight;
}
otherExpander.IsExpanded = false;
}
totalExpanderHeight += otherExpander.ActualHeight;
}
if (selectedExpander.IsExpanded)
{
var contentPresenter = selectedExpander.Template.FindName(ExpandSiteName, selectedExpander) as ContentPresenter;
if(contentPresenter != null)
{
contentPresenter.Height = ActualHeight - totalExpanderHeight - selectedExpander.ActualHeight;
}
}
}
}
}