【问题标题】:How do I anchor an expander to right side of a ListBox?如何将扩展器锚定到列表框的右侧?
【发布时间】:2009-11-09 20:44:41
【问题描述】:

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

【问题讨论】:

    标签: wpf listbox expander


    【解决方案1】:

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

    【讨论】:

    • 这可行,但缺点是将数百行主题样式 XAML 复制到您的项目中。这可能会使您的项目更难维护。它还有一个缺点是不能处理未来的主题变化。
    【解决方案2】:

    我更喜欢使用我不久前编写的 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) 才能使用自定义控件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-06
      • 2022-11-16
      • 2015-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      相关资源
      最近更新 更多