繁体   English   中英

WPF 环绕面板和滚动

[英]WPF wrap panel and scrolling

我有一个简单的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唯一被覆盖的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 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>

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

这是我的解决方案:

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


现在你可以滚动了。



希望我有所帮助。

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

        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM