如何将扩展器锚定到列表框的右侧?

发布于 2024-08-10 09:16:31 字数 832 浏览 10 评论 0原文

下面的 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>

The XAML code below works fine except I want the expander button to be between the listbox and the grid. If I set the ExpandDirection="Left" the button is between the listbox and the grid but the direction indicator on the button is confusing to users - it point to the right when expanded and it points to the left when it is not expanded. I want the direction indicator to work the way it does when ExpandDirection="Right" but I want the functionality of 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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花想c 2024-08-17 09:16:31

使用 Expression Blend,编辑 Expander 当前模板的副本,转到模板的 XAML,将“ExpanderLeftHeaderStyle”重命名为“ExpanderRightHeaderStyle”,将“ExpanderRightHeaderStyle”重命名为“ExpanderLeftHeaderStyle”。

Use Expression Blend, edit a copy of the current template for the Expander, go to XAML for the template, rename "ExpanderLeftHeaderStyle" to "ExpanderRightHeaderStyle" and "ExpanderRightHeaderStyle" to "ExpanderLeftHeaderStyle".

烟柳画桥 2024-08-17 09:16:31

我更喜欢使用我不久前编写的 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;
      }
  }
}

与往常一样,您需要在 XAML 中声明命名空间 (xmlns) 才能使用自定义控件。

I prefer to use a DockedExpander class I wrote a while back (the code is included below). This class automatically sets itself up for whatever side of a DockPanel it is docked on.

For example, in:

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

  <Grid ...

</DockPanel>

The expander will open from the left, with the button facing the right way. But changing it to:

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

will automatically adjust the rest of the expander to match. Same with "Top" and "Bottom" docking.

I implemented DockedExpander because the thought of copying several hundred lines of WPF's internal code into my project was abhorrent to me. Also, my DockedExpander control automatically adapts to new theme styles because it reads WPF's internal styles.

Here is the code for the DockedExpander class:

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

As usual, you need a namespace declaration (xmlns) in your XAML to be able to use a custom control.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文