C# WPF 环绕面板和滚动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2119009/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:40:21  来源:igfitidea点击:

WPF wrap panel and scrolling

c#wpfscrollwrappanel

提问by Flack

I have a simple WrapPanelwhich contains a number of wide controls. When I resize the Widthof the Windoweverything works as expected. The controls will go across on a single line if there is enough space or wrap down to the next line when there isn't.

我有一个简单的WrapPanel,其中包含许多广泛的控件。当我调整大小Width时,Window一切都按预期工作。如果有足够的空间,控件将在单行上显示,如果没有,则返回到下一行。

However, what I need to happen is that if all of the controls are basically stacked vertically (since there is no more horizontal space) and the Widthof the Windowis decreased even more, a horizontal scroll bar appears so that I can scroll and see the entire control if I want to. Below is my xaml. I tried wrapping the WrapPanelin a ScrollViewerbut I couldn't achieve my goal.

不过,我需要发生的是,如果所有的控件都基本上垂直堆叠(因为没有更多的横向空间)和WidthWindow更加降低,水平滚动条出现,这样我可以滚动看到整个如果我想控制。下面是我的xaml。我尝试将 包裹WrapPanel在 a 中,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>

So if you reduce the Widthof the above Windowto its minimum, you will not be able to see the text of the buttons. I would like a horizontal scroll bar appear so that I can scroll to see the text but not interfere with the usual wrapping functionality.

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

Thanks.

谢谢。

Update:I have followed Paul's suggestion below and the horizontal scrollbar appears as expected now. However, I also wanted vertical scrolling available so I set VerticalScrollBarVisibility="Auto". The thing is, if I resize the window so that a vertical scroll bar appears, the horizontal one also always appears, even if it is not needed (there is enough horizontal space to see the entire control). It seems like the vertical scrollbar appearing is messing with the width of the scrollviewer. Is there a way to correct this so that the horizontal scrollbar doesn't appear unless it is actually needed?

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

Below is my xaml and the only code I added in the CustomWrapPanel:

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

The only thing overridden in 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);
}

采纳答案by Paul Rohde

Here's the thing, if your going to use a wrap panel, it does two things, it will take up as much available space in one direction, and expand as needed in the other. For instance, if you place it inside of a window like you have it, it takes up as much horizontal space as it can, and then expands as needed downward, that's why a vertical scroll bar will work, the parent container says "this is how wide I am, but you can make yourself as big as you want vertically", if you change it to a horizontal scroll bar, the scroll viewer is essentially saying "this is how tall you can be, but you can be as wide as you want" in this case the wrap panel doesn't wrap because there is no horizontal constraints.

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

One potential solution is to change the direction the wrap panel wraps from horizontal to vertical like this (Which is probably not the ideal or expected behavior):

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

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

In order to get the behavior your expecting, you'll have to do something closer to this:

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

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

However, this second solution only works if you already know the width of your child elements, ideally you want your max width to be set to the actual width of the largest child item, but in order to do that you'd have to create a custom control that derives from wrap panel and write the code yourself to check for that.

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

回答by Suhas Bothe

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

        }

回答by Elyasaf755

This is my solution for this:

这是我的解决方案:

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



I'll try to explain:
I used an ItemsControl, its ItemsSource was bound to my Items collection. Inside it, I defined a WrapPanel as the ItemsPanelTemplate. This is what makes the wrapping job done.



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

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


But now, there is no scrolling, right?
To solve this, I defined an ItemsPresenter inside a ScrollViewer as the ControlTemplate:


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

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


And now you can scroll.


现在你可以滚动了。



Hope I helped.



希望我有所帮助。