web-dev-qa-db-ja.com

WPFラップパネルとスクロール

私は単純なWrapPanelを持っています。これには多くの幅広いコントロールが含まれています。 WidthWindowのサイズを変更すると、すべてが期待どおりに機能します。十分なスペースがある場合、コントロールは1行にまたがるか、ない場合は次の行に折り返します。

ただし、私が行う必要があるのは、すべてのコントロールが基本的に垂直に積み重ねられ(水平方向のスペースがないため)、WidthWindowがさらに減少すると、水平になります。スクロールバーが表示されるので、スクロールしてコントロール全体を表示できます。以下は私のxamlです。 WrapPanelScrollViewerでラップしてみましたが、目標を達成できませんでした。

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

したがって、上記のWidthWindowを最小限にすると、ボタンのテキストが表示されなくなります。水平スクロールバーを表示して、スクロールしてテキストを表示できるようにしますが、通常の折り返し機能を妨げることはありません。

ありがとう。

更新:以下の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);
}
21
Flack

ラップパネルを使用する場合、2つのことを行います。1つの方向で使用可能なスペースを使用し、必要に応じて他の方向に拡張します。たとえば、現在のようにウィンドウ内に配置すると、可能な限り水平方向のスペースを占有し、必要に応じて下方向に拡大します。そのため、垂直スクロールバーが機能し、親コンテナは「これは私はどのくらいの幅ですが、垂直方向に好きなだけ大きくすることができます」、それを水平方向のスクロールバーに変更すると、スクロールビューアは基本的に「これは非常に高いことができますが、同じくらいの幅にすることができますこの場合、水平方向の制約がないため、ラップパネルは折り返しません。

考えられる解決策の1つは、ラップパネルがラップする方向を次のように水平から垂直に変更することです(これはおそらく理想的または期待される動作ではありません)。

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

ただし、この2番目のソリューションは、子要素の幅がすでにわかっている場合にのみ機能します。理想的には、最大幅を最大の子項目の実際の幅に設定する必要がありますが、そのためには、ラップパネルから派生し、それをチェックするコードを自分で記述するカスタムコントロール。

52
Paul Rohde
     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;
                }
            }

        }
0
Suhas Bothe