【问题标题】:WPF wrap panel and scrollingWPF 包装面板和滚动
【发布时间】:2011-01-08 07:22:34
【问题描述】:

我有一个简单的WrapPanel,其中包含许多宽控件。当我调整WindowWidth 的大小时,一切都按预期工作。如果有足够的空间,这些控件将在一行上穿过,或者在没有空间时折回到下一行。

但是,我需要发生的是,如果所有控件基本上垂直堆叠(因为没有更多的水平空间)并且WindowWidth 减少了更多,则会出现水平滚动条这样我就可以滚动并查看整个控件,如果我愿意的话。下面是我的xaml。我尝试将WrapPanel 包装在ScrollViewer 中,但我无法实现我的目标。

<Window x:Class="WpfQuotes.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="Auto" Width="600" Foreground="White">
    <WrapPanel>
      <Button Width="250">1</Button>
      <Button Width="250">2</Button>
      <Button Width="250">3</Button>
    </WrapPanel>
</Window>

因此,如果您将上述WindowWidth 减少到最小,您将无法看到按钮的文本。我希望出现一个水平滚动条,以便我可以滚动查看文本,但不会干扰通常的换行功能。

谢谢。

更新: 我遵循了 Paul 在下面的建议,水平滚动条现在按预期出现。但是,我还希望可以使用垂直滚动,所以我设置了VerticalScrollBarVisibility="Auto"。问题是,如果我调整窗口大小以显示垂直滚动条,水平滚动条也总是出现,即使它不需要(有足够的水平空间来查看整个控件)。似乎出现的垂直滚动条弄乱了滚动查看器的宽度。有没有办法纠正这个问题,除非确实需要,否则水平滚动条不会出现?

下面是我的 xaml,也是我在CustomWrapPanel 中添加的唯一代码:

<Window x:Class="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cwp="clr-namespace:CustomWrapPanelExample"
        Title="Window1" Height="Auto" Width="300" Foreground="White" Name="mainPanel">
  <ScrollViewer x:Name="MyScrollViewer" HorizontalScrollBarVisibility="Auto"
                VerticalScrollBarVisibility="Auto">
    <cwp:CustomWrapPanel Width="{Binding ElementName=MyScrollViewer, Path=ActualWidth}">
      <Button Width="250">1</Button>
      <Button Width="250">2</Button>
      <Button Width="250">3</Button>
      <Button Width="250">4</Button>
      <Button Width="250">5</Button>
      <Button Width="250">6</Button>
      <Button Width="250">7</Button>
      <Button Width="250">8</Button>
      <Button Width="250">9</Button>
    </cwp:CustomWrapPanel>
  </ScrollViewer>
</Window>

CustomWrapPanel 中唯一被覆盖的东西:

protected override Size MeasureOverride(Size availableSize)
{
  double maxChildWidth = 0;
  if (Children.Count > 0)
  {
    foreach (UIElement el in Children)
    {
      if (el.DesiredSize.Width > maxChildWidth)
      {
        maxChildWidth = el.DesiredSize.Width;
      }
    }
  }      
  MinWidth = maxChildWidth;
  return base.MeasureOverride(availableSize);
}

【问题讨论】:

  • 你能用 ScrollViewer 发布 XAML 吗?

标签: c# wpf scroll wrappanel


【解决方案1】:

这是我的解决方案:

    <Grid Width="475">
        <ItemsControl ItemsSource="{Binding Items}" 
                          Height="450" Width="475" >

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:HorizontalListItemControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.Template>
                <ControlTemplate>
                    <ScrollViewer>
                        <ItemsPresenter />
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>

        </ItemsControl>
    </Grid>



我会尽力解释:
我使用了 ItemsControl,它的 ItemsSource 绑定到我的 Items 集合。 在其中,我将 WrapPanel 定义为 ItemsPanelTemplate。这就是完成包装工作的原因。

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>


但是现在,没有滚动,对吧?
为了解决这个问题,我在 ScrollViewer 中定义了一个 ItemsPresenter 作为 ControlTemplate:

            <ItemsControl.Template>
                <ControlTemplate>
                    <ScrollViewer>
                        <ItemsPresenter />
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>


现在你可以滚动了。



希望我能帮上忙。

【讨论】:

    【解决方案2】:
         public bool CheckUIElementInBounary(UIElement element, Rect r)
                {
                    bool inbound = false;
                    Point p1 = element.PointToScreen(new Point(0, 0));
                    Point p2 = element.PointToScreen(new Point(0, element.RenderSize.Height));
                    Point p3 = element.PointToScreen(new Point(element.RenderSize.Width, 0));
                    Point p4 = element.PointToScreen(new Point(element.RenderSize.Width, element.RenderSize.Height));
                    if (CheckPoint(p1, r) || CheckPoint(p2, r) || CheckPoint(p3, r) || CheckPoint(p4, r))
                    {
                        inbound = true;
                    }
                    return inbound;
                }
                public bool CheckPoint(Point p, Rect bounday)
                {
                    bool inbound = false;
                    if (p.X >= bounday.Left && p.X <= bounday.Right && p.Y <= bounday.Top && p.Y <= bounday.Bottom)
                    {
                        inbound = true;
                    }
                    return inbound;
                }
    
    ===================
    void mainViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
            {
                foreach (var item in this.mainContent.Items)
                {
                    Button btn = item as Button;
                    Point p1 = mainViewer.PointToScreen(new Point(0, 0));
                    Point p2 = mainViewer.PointToScreen(new Point(mainViewer.ActualWidth, mainViewer.ActualHeight));
                    Rect bounds = new Rect(p1, p2);
                    if (!CheckUIElementInBounary(btn, bounds))
                    {
                        this.Title = btn.Content.ToString();
                        mainContent.ScrollIntoView(btn);
                        break;
                    }
                }
    
            }
    

    【讨论】:

    • 你能补充解释吗!?
    【解决方案3】:

    事情是这样的,如果您要使用环绕面板,它会做两件事,它会在一个方向上占用尽可能多的可用空间,并在另一个方向上根据需要展开。例如,如果你把它放在一个窗口内,就像你有它一样,它会占用尽可能多的水平空间,然后根据需要向下扩展,这就是垂直滚动条可以工作的原因,父容器说“这是我有多宽,但你可以让自己垂直变大”,如果你把它改成水平滚动条,滚动查看器本质上是在说“这是你可以有多高,但你可以像你想要”在这种情况下,换行面板不会换行,因为没有水平约束。

    一种可能的解决方案是将环绕面板的环绕方向从水平更改为垂直,如下所示(这可能不是理想或预期的行为):

        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
            <WrapPanel Orientation="Vertical">
                <Button Width="250">1</Button>
                <Button Width="250">2</Button>
                <Button Width="250">3</Button>
            </WrapPanel>
        </ScrollViewer>
    

    为了获得您期望的行为,您必须做一些更接近此的事情:

        <ScrollViewer x:Name="MyScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
            <WrapPanel MinWidth="250" Width="{Binding ElementName=MyScrollViewer, Path=ViewportWidth}">
                <Button Width="250">1</Button>
                <Button Width="250">2</Button>
                <Button Width="250">3</Button>
            </WrapPanel>
        </ScrollViewer>
    

    但是,第二个解决方案仅在您已经知道子元素的宽度时才有效,理想情况下,您希望将最大宽度设置为最大子项的实际宽度,但为了做到这一点,您需要创建一个从包装面板派生的自定义控件并自己编写代码来检查。

    【讨论】:

    • 谢谢保罗。我按照您的描述进行了更改,现在效果好多了。但是,我看到一个小问题 w.r.t.垂直滚动。我还希望在包装面板的项目在垂直方向上不完全可见时出现一个垂直滚动条。我编辑了我的原始帖子以显示我所做的更改和我面临的问题。
    • 啊...玩了一下,您需要做的就是将包装面板上的绑定路径从 ActualWidth 更改为 ViewportWidth,当添加垂直滚动条时,它会使视口宽度变小,尽管实际控件的大小保持不变。那应该可以解决您看到的奇怪的水平滚动条错误,我会更新我的帖子。
    • 太棒了!看起来它现在工作得很好。非常感谢您的帮助:)
    • 这可以与 C# 代码一起使用,也可以像这样 ScrollViewer sv = new ScrollViewer(); WrapPanel wp = new WrapPanel(); sv.VerticalScrollBarVisibility = ScrollBarVisibility.Visible; sv.Content = wp;并且在窗口大小发生变化时绑定一个事件,以便按钮大小与 WrapPanel 相对于窗口当前大小发生变化谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多