2009-11-09 11 views
2

Le code XAML ci-dessous fonctionne correctement, sauf que je souhaite que le bouton Expander se trouve entre la liste et la grille. Si je mets le ExpandDirection = "Left" le bouton est entre la listbox et la grille mais l'indicateur de direction sur le bouton est déroutant aux utilisateurs - il pointe vers la droite lorsqu'il est développé et pointe vers la gauche quand il n'est pas développé. Je veux que l'indicateur de direction fonctionne comme il le fait quand ExpandDirection = "Right" mais je veux la fonctionnalité de ExpandDirection = "Left".Comment ancrer un expandeur sur le côté droit d'un ListBox?

<DockPanel> 
    <Expander ExpandDirection="Right"> 
     <ListBox> 
      <ListBoxItem>Item One</ListBoxItem> 
      <ListBoxItem>Item Two</ListBoxItem> 
      <ListBoxItem>Item Three</ListBoxItem> 
      <ListBoxItem>Item Four</ListBoxItem> 
      <ListBoxItem>Item Five</ListBoxItem> 
     </ListBox> 
    </Expander> 
     <Grid Background="AliceBlue"> 
      <TextBlock > 
      Other Content 
      </TextBlock> 
     </Grid> 
</DockPanel> 

Répondre

2

Utiliser Expression Blend, modifier une copie du modèle en cours pour l'extension, allez à XAML pour le modèle, renomme « ExpanderLeftHeaderStyle » à « ExpanderRightHeaderStyle » et « ExpanderRightHeaderStyle » à « ExpanderLeftHeaderStyle ».

+0

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. –

2

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é.