Как привязать расширитель к правой стороне ListBox?

Приведенный ниже код XAML работает нормально, за исключением того, что я хочу, чтобы кнопка расширения находилась между списком и сеткой. Если я устанавливаю ExpandDirection="Left", кнопка находится между списком и сеткой, но индикатор направления на кнопке сбивает пользователей с толку - он указывает вправо при расширении и указывает влево, когда он не развернут. Я хочу, чтобы индикатор направления работал так же, как при ExpandDirection="Right", но мне нужна функциональность ExpandDirection="Left".

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

person ihatemash    schedule 09.11.2009    source источник


Ответы (2)


Используйте Expression Blend, отредактируйте копию текущего шаблона для Expander, перейдите к XAML для шаблона, переименуйте «ExpanderLeftHeaderStyle» в «ExpanderRightHeaderStyle» и «ExpanderRightHeaderStyle» в «ExpanderLeftHeaderStyle».

person Sergey Aldoukhov    schedule 09.11.2009
comment
Это работает, но имеет тот недостаток, что в проект копируются несколько сотен строк XAML стиля темы. Это может затруднить поддержку вашего проекта. У него также есть недостаток, заключающийся в том, что он не обрабатывает будущие изменения темы. - person Ray Burns; 10.11.2009

Я предпочитаю использовать класс DockedExpander, который написал некоторое время назад (код приведен ниже). Этот класс автоматически настраивается для любой стороны DockPanel, на которой он закреплен.

Например, в:

<DockPanel>
  <edf:DockedExpander DockPanel.Dock="Left">
    <ListBox ...
  </edf:DockedExpander>

  <Grid ...

</DockPanel>

Расширитель откроется слева, при этом кнопка будет направлена ​​вправо. Но изменив его на:

  <edf:DockedExpander DockPanel.Dock="Right">

автоматически отрегулирует остальную часть расширителя, чтобы соответствовать. То же самое с стыковкой «Верх» и «Низ».

Я внедрил DockedExpander, потому что мысль о копировании нескольких сотен строк внутреннего кода WPF в мой проект вызывала у меня отвращение. Кроме того, мой элемент управления DockedExpander автоматически адаптируется к новым стилям темы, поскольку он считывает внутренние стили WPF.

Вот код класса 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;
      }
  }
}

Как обычно, вам потребуется объявление пространства имен (xmlns) в вашем XAML, чтобы иметь возможность использовать настраиваемый элемент управления.

person Ray Burns    schedule 09.11.2009