Je préfère utiliser une classe DockedExpander que j'ai écrite il y a quelques temps (le code est inclus ci-dessous). Cette classe s'installe automatiquement quel que soit le côté d'un DockPanel sur lequel elle est ancrée.
Par exemple, dans:
<DockPanel>
<edf:DockedExpander DockPanel.Dock="Left">
<ListBox ...
</edf:DockedExpander>
<Grid ...
</DockPanel>
L'extension ouvrira à partir de la gauche, avec le bouton dans le bon sens. Mais le changement à:
<edf:DockedExpander DockPanel.Dock="Right">
ajustera automatiquement le reste de l'extension pour correspondre. Idem avec l'ancrage "Top" et "Bottom".
J'ai implémenté DockedExpander parce que l'idée de copier plusieurs centaines de lignes de code interne de WPF dans mon projet m'était odieuse. De plus, mon contrôle DockedExpander s'adapte automatiquement aux nouveaux styles de thème car il lit les styles internes de WPF.
Voici le code pour la classe DockedExpander:
public class DockedExpander : Expander
{
static DockedExpander()
{
_directions = new Dictionary<Dock, DirectionData>();
_directions[Dock.Left] = new DirectionData { Reverse = Dock.Right, ExpandDirection = ExpandDirection.Right };
_directions[Dock.Right] = new DirectionData { Reverse = Dock.Left, ExpandDirection = ExpandDirection.Left };
_directions[Dock.Top] = new DirectionData { Reverse = Dock.Bottom, ExpandDirection = ExpandDirection.Down };
_directions[Dock.Bottom] = new DirectionData { Reverse = Dock.Top, ExpandDirection = ExpandDirection.Up };
DockPanel.DockProperty.OverrideMetadata(typeof(DockedExpander), new FrameworkPropertyMetadata
{
PropertyChangedCallback = (obj, e) => ((DockedExpander)obj).UpdateExpandDirection()
});
ExpandDirectionProperty.OverrideMetadata(typeof(DockedExpander), new FrameworkPropertyMetadata
{
PropertyChangedCallback = (obj, e) => { throw new ArgumentException("Cannot set ExpandDirection because DockedExpander always computes its ExpandDirection from the DockPanel.Dock property"); }
});
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
UpdateExpandDirection();
}
private void UpdateExpandDirection()
{
// Can't use GetTemplateChild because non-PART_ names are not guaranteed to stay the same
var dockPanel = FindTwoElementDockPanelUnder(this);
var headerSite = dockPanel.Children[0];
var expandSite = dockPanel.Children[1];
// Compute the docking
Dock myDock = DockPanel.GetDock(this);
DirectionData myDockData = _directions[myDock];
DockPanel.SetDock(headerSite, myDockData.Reverse);
DockPanel.SetDock(expandSite, myDock);
headerSite.SetValue(FrameworkElement.StyleProperty, myDockData.HeaderSiteStyle);
}
private static Dictionary<Dock, DirectionData> _directions;
private class DirectionData
{
public Dock Reverse;
public ExpandDirection ExpandDirection;
public Style HeaderSiteStyle
{
get
{
if(_headerSiteStyle==null)
{
var expander = new Expander { ExpandDirection = this.ExpandDirection };
expander.BeginInit();
expander.EndInit();
expander.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var headerSite = FindTwoElementDockPanelUnder(expander).Children[0];
_headerSiteStyle = ((FrameworkElement)headerSite).Style;
}
return _headerSiteStyle;
}
}
private Style _headerSiteStyle;
}
private static DockPanel FindTwoElementDockPanelUnder(DependencyObject visual)
{
while(true)
switch(VisualTreeHelper.GetChildrenCount(visual))
{
case 1: visual = VisualTreeHelper.GetChild(visual, 0); continue;
case 2: return visual as DockPanel;
default: return null;
}
}
}
Comme d'habitude, vous avez besoin d'une déclaration d'espace de noms (xmlns) dans votre XAML pour être en mesure d'utiliser un contrôle personnalisé.
Cela fonctionne, mais a l'inconvénient de copier plusieurs centaines de lignes de style XAML dans votre projet. Cela peut rendre votre projet un peu plus difficile à maintenir. Il a également l'inconvénient de ne pas gérer les futurs changements de thème. –