2016-09-22 25 views
0

HERESに例:リストビューとWrapPanelテンプレートで水平方向の間隔を均等にする方法は?

<Window x:Class="ListViewItemSpacing.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:c="clr-namespace:ListViewItemSpacing.Controls" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:ListViewItemSpacing" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <ListView FlowDirection="LeftToRight" Background="#222"> 
      <ListView.ItemsPanel> 
       <ItemsPanelTemplate> 
        <WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}" /> 
       </ItemsPanelTemplate> 
      </ListView.ItemsPanel> 
      <ListView.Items> 
       <Rectangle Fill="#27f" Width="100" Height="100"/> 
       <Rectangle Fill="#27f" Width="100" Height="100"/> 
       <Rectangle Fill="#27f" Width="100" Height="100"/> 
       <Rectangle Fill="#27f" Width="100" Height="100"/> 
       <Rectangle Fill="#27f" Width="100" Height="100"/> 
       <Rectangle Fill="#27f" Width="100" Height="100"/> 
      </ListView.Items> 
     </ListView> 
    </Grid> 
</Window> 

このXAML 6つの青い四角形、最初の行の4、次の行の2を生成します。 4つの最初の青い四角形の後に、右にいくらかのスペースがあります。

つまり、左揃えのテキストのように見えます。

正当なテキストのように見せたいので、正方形の間に水平方向の間隔を調整して、左右に均等に配置するようにしてください。ListViewは、右側ではなく要素間に均等に配置されています。

些細なことですが、どうやって行うのか分かりません。私はどこから始めるべきかわかりません。たとえば、デフォルトでいくつかのスペースが追加されています。デフォルトでは、横スペーシングは垂直よりもはるかに大きいので、デフォルトでは問題はありませんが、項目を左右に揃えるのに十分な大きさではありません。しかし、スペースはどこから来ますか?手動でもそれを変更する方法は?私は要素自体を混乱させたくありません。実際のアプリケーションの要素は別のモジュールから来て、それらのモジュールは適切に分離されているはずです。

SizeChangedのイベントはListViewを処理し、手動で水平間隔を調整するのが最も分かりやすいと思いますが、どこにスペースがありますか?私はコードの背後にある項目のスペースにどうやってアクセスできますか?明確にするため

:残り2左および前の行の正方形に合わせ、所望のレンダリングはまだ6つの同一の青色の正方形を含むべきであるが、第4は、左右の制御縁に整列されます。

デフォルトの外観はこのようなものです:

---------------- 
|[] [] [] [] | 
|[] []   | 
---------------- 

目的の外観は、このようなものです:

---------------- 
|[] [] [] []| 
|[] []  | 
---------------- 
+0

私は今年初めwrapPanelについて同様のWPF質問を見てきました覚えています。その質問にも賞金がありました。それは愚か者になることができますが、残念ながら今私はそれを見つけることができません – ASh

+0

あなたはコメントで、アイテムの '幅 'を知っているので、あなたはコンバータを考えることができます。利用可能な幅と項目の幅を渡してから、コンバーターに 'AvailableWidth/ItemWidth = numOfItems; AvailableWidth%ItemWidth = remainingSpace;残りのスペース/ numOfItems = extraSpacePerItem; 'あなたがそれを必要とすることを割り当てます。おそらく2で割り、その量を各項目の左/​​右の余白に置きます。 – Rachel

+1

あるいは、[このカスタムパネル](http://stackoverflow.com/a/7747002/302677)があなたにとってうまくいくでしょうか?これは、コンテンツを整列する機能を持っていることを除いて、WrapPanelのように動作するように構築されています。 – Rachel

答えて

0

みましょう私たちは列の数を知らないと言います。 Windows xamlとコードのコードの完全なコードです(あなたのクラス名と一致するようにクラス名を修正します)。これを試してみてください:

XAML:

<Window x:Class="SO39640127.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:so39640127="clr-namespace:SO39640127" 
     mc:Ignorable="d" 
     Title="MainWindow" 
     Height="{Binding LayoutHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
     Width="{Binding LayoutWidth,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
     d:DataContext="{d:DesignInstance so39640127:MainWindow}"> 
    <Grid> 
     <ListView Background="#222" VerticalContentAlignment="Top"> 
      <ListView.ItemsPanel> 
       <ItemsPanelTemplate> 
        <UniformGrid HorizontalAlignment="Center" VerticalAlignment="Top" 
           Columns="{Binding LayoutColumns, UpdateSourceTrigger=PropertyChanged}" 
           Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"></UniformGrid> 
       </ItemsPanelTemplate> 
      </ListView.ItemsPanel> 
      <ListView.Items> 
       <Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/> 
       <Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/> 
       <Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/> 
       <Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/> 
       <Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/> 
       <Rectangle Fill="#27f" Width="{Binding ItemWidth}" Height="{Binding ItemHeight}" Margin="{Binding VerticalSpacing}"/> 
      </ListView.Items> 
     </ListView> 
    </Grid> 
</Window> 

分離コード:

public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     private double itemHeight; 
     private double itemWidth; 
     private int layoutColumns; 
     private int layoutHeight; 
     private int layoutWidth; 
     private Thickness verticalSpacing; 

     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = this; 

      InitializeDesign(); 
     } 

     public double ItemWidth 
     { 
      get { return itemWidth; } 
      set 
      { 
       if (value.Equals(itemWidth)) 
        return; 
       itemWidth = value; 
       OnPropertyChanged(); 
      } 
     } 

     public double ItemHeight 
     { 
      get { return itemHeight; } 
      set 
      { 
       if (value.Equals(itemHeight)) 
        return; 
       itemHeight = value; 
       OnPropertyChanged(); 
      } 
     } 

     public int LayoutColumns 
     { 
      get { return layoutColumns; } 
      set 
      { 
       if (value == layoutColumns) 
        return; 
       layoutColumns = value; 
       OnPropertyChanged(); 
      } 
     } 

     public int LayoutWidth 
     { 
      get { return layoutWidth; } 
      set 
      { 
       if (value == layoutWidth) 
        return; 
       layoutWidth = value; 
       OnPropertyChanged(); 
       UpdateCalculations(); 
      } 
     } 

     public int LayoutHeight 
     { 
      get { return layoutHeight; } 
      set 
      { 
       if (value == layoutHeight) 
        return; 
       layoutHeight = value; 
       OnPropertyChanged(); 
       UpdateCalculations(); 
      } 
     } 

     public Thickness VerticalSpacing 
     { 
      get { return verticalSpacing; } 
      set 
      { 
       if (value.Equals(verticalSpacing)) 
        return; 
       verticalSpacing = value; 
       OnPropertyChanged(); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void UpdateCalculations() 
     { 
      //Calculate the # of elements that fit on the list view 
      var totalItems = (int) (LayoutWidth/ItemWidth); 
      LayoutColumns = totalItems; 
     } 

     private void InitializeDesign() 
     { 
      LayoutWidth = 525; 
      LayoutHeight = 350; 

      ItemWidth = 100; 
      ItemHeight = 100; 

      VerticalSpacing = new Thickness(3); 
     } 

     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
     { 
      var handler = PropertyChanged; 
      if (handler != null) 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

私は項目に余裕を通じて垂直方向の間隔を達成しています。ウィンドウのサイズに応じて列がどのように正しく調整されるかを確認するために、サイズ変更を試みることができます。また、デモの目的でバインディングを行うためにMVVMを少し使用しています。もちろんそれに応じてそれを適応させるべきです。

希望すると便利です。

+0

まあ、私はそれはウィンドウのサイズに依存するので、列の数を知らない。しかし、私は要素の幅と高さを知っています。 'SizeChanged'イベントハンドラの列数を計算できます。別の問題は、コントロールの高さと項目数に依存する垂直間隔です。 – Harry

+0

垂直間隔をどのように扱いたいですか?均等に配分する必要がありますか?それとも、それを一番上に並べるだろうか? –

+0

正当なテキストの場合とまったく同じです。垂直方向の間隔は、水平方向の間隔や水平方向の配置とはまったく異なります。 'WrapPanel'は行く方法のようです。私は間隔を変更する方法を考え出しました。正しい間隔値を計算する方法や、ListView.ItemContainerStyleに醜いハックを使わずにそれらを渡す方法はわかりませんでした。 BTW:アイテムのサイズがわかっていて、ウィンドウのサイズが不明です。アイテムの数が不明です。 – Harry

1

RachelDTigのおかげで私はついにそれを作った。 WrapPanelのような要素を調整できるパネルはありませんが、カスタマイズ可能なのはHorizontalContentAlignmentです。そして明らかに - HorizontalContentAlignment = HorizontalAlignment.Stretchは私がここで必要だったものです。

Rachelアドバイスに続いて、私はthisを試しましたが、HorizontalAlignment.Stretchの値をサポートしていませんでした。

だから設けられた素子幅動作をサポートし、追加互いに等しい:Stretch配向を有するこの基本的Tig's溶液を添加

/// <summary> 
/// <see cref="Panel"/> like <see cref="WrapPanel"/> which supports <see cref="HorizontalContentAlignment"/> property. 
/// </summary> 
public class AlignableWrapPanel : Panel { 

    /// <summary> 
    /// <see cref="HorizontalAlignment"/> property definition. 
    /// </summary> 
    public static readonly DependencyProperty HorizontalContentAlignmentProperty = 
     DependencyProperty.Register(
      "HorizontalContentAlignment", 
      typeof(HorizontalAlignment), 
      typeof(AlignableWrapPanel), 
      new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange) 
     ); 

    /// <summary> 
    /// Gets or sets the horizontal alignment of the control's content. 
    /// </summary> 
    [BindableAttribute(true)] 
    public HorizontalAlignment HorizontalContentAlignment { 
     get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); } 
     set { SetValue(HorizontalContentAlignmentProperty, value); } 
    } 

    /// <summary> 
    /// Measures the size in layout required for child elements and determines a size for the <see cref="AlignableWrapPanel"/>. 
    /// </summary> 
    /// <param name="constraint">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param> 
    /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns> 
    protected override Size MeasureOverride(Size constraint) { 
     var curLineSize = new Size(); 
     var panelSize = new Size(); 
     var children = base.InternalChildren; 
     for (var i = 0; i < children.Count; i++) { 
      var child = children[i] as UIElement; 
      // Flow passes its own constraint to children 
      child.Measure(constraint); 
      var sz = child.DesiredSize; 
      if (curLineSize.Width + sz.Width > constraint.Width) { //need to switch to another line 
       panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width); 
       panelSize.Height += curLineSize.Height; 
       curLineSize = sz; 
       if (sz.Width > constraint.Width) { // if the element is wider then the constraint - give it a separate line      
        panelSize.Width = Math.Max(sz.Width, panelSize.Width); 
        panelSize.Height += sz.Height; 
        curLineSize = new Size(); 
       } 
      } 
      else { //continue to accumulate a line 
       curLineSize.Width += sz.Width; 
       curLineSize.Height = Math.Max(sz.Height, curLineSize.Height); 
      } 
     } 
     // the last line size, if any need to be added 
     panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width); 
     panelSize.Height += curLineSize.Height; 
     return panelSize; 
    } 

    /// <summary> 
    /// Positions child elements and determines a size for a <see cref="AlignableWrapPanel"/>. 
    /// </summary> 
    /// <param name="arrangeBounds">The final area within the parent that this element should use to arrange itself and its children.</param> 
    /// <returns>The actual size used.</returns> 
    protected override Size ArrangeOverride(Size arrangeBounds) { 
     var firstInLine = 0; 
     var curLineSize = new Size(); 
     var accumulatedHeight = 0.0; 
     var children = InternalChildren; 
     for (var i = 0; i < children.Count; i++) { 
      var sz = children[i].DesiredSize; 
      if (curLineSize.Width + sz.Width > arrangeBounds.Width) { //need to switch to another line 
       ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i); 
       accumulatedHeight += curLineSize.Height; 
       curLineSize = sz; 
       if (sz.Width > arrangeBounds.Width) { //the element is wider then the constraint - give it a separate line      
        ArrangeLine(accumulatedHeight, sz, arrangeBounds.Width, i, ++i); 
        accumulatedHeight += sz.Height; 
        curLineSize = new Size(); 
       } 
       firstInLine = i; 
      } 
      else { //continue to accumulate a line 
       curLineSize.Width += sz.Width; 
       curLineSize.Height = Math.Max(sz.Height, curLineSize.Height); 
      } 
     } 
     if (firstInLine < children.Count) 
      ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count); 
     return arrangeBounds; 
    } 

    /// <summary> 
    /// Arranges elements within a line. 
    /// </summary> 
    /// <param name="y">Line vertical coordinate.</param> 
    /// <param name="lineSize">Size of the items line.</param> 
    /// <param name="boundsWidth">Width of the panel bounds.</param> 
    /// <param name="start">Index of the first child which belongs to the line.</param> 
    /// <param name="end">Index of the last child which belongs to the line.</param> 
    private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end) { 
     var children = InternalChildren; 
     var x = 0.0; 
     var stretchOffset = 0.0; 
     if (HorizontalContentAlignment == HorizontalAlignment.Center) x = (boundsWidth - lineSize.Width)/2; 
     else if (HorizontalContentAlignment == HorizontalAlignment.Right) x = (boundsWidth - lineSize.Width); 
     else if (HorizontalAlignment == HorizontalAlignment.Stretch) { 
      var childWidth = children[start].DesiredSize.Width; // warning, this works only when all children have equal widths 
      int n = (int)boundsWidth/(int)childWidth; 
      if (children.Count > n) { 
       var takenWidth = n * childWidth; 
       var spaceWidth = boundsWidth - takenWidth; 
       stretchOffset = spaceWidth/(n - 1); 
      } 
     } 
     for (var i = start; i < end; i++) { 
      var child = children[i]; 
      child.Arrange(new Rect(x, y, child.DesiredSize.Width, lineSize.Height)); 
      x += child.DesiredSize.Width + stretchOffset; 
     } 
    } 

} 

。ここで

は、このためのテストXAMLです:

<Window 
    x:Class="ListViewItemSpacing.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:c="clr-namespace:CustomControls" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:local="clr-namespace:ListViewItemSpacing" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    Title="MainWindow" 
    Width="525" 
    Height="250" 
    mc:Ignorable="d"> 
    <Grid> 
     <ListView Background="#222"> 
      <ListView.ItemsPanel> 
       <ItemsPanelTemplate> 
        <c:AlignableWrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}" HorizontalContentAlignment="Stretch" /> 
       </ItemsPanelTemplate> 
      </ListView.ItemsPanel> 
      <ListView.Items> 
       <Rectangle 
        Width="100" 
        Height="100" 
        Fill="#27f" /> 
       <Rectangle 
        Width="100" 
        Height="100" 
        Fill="#27f" /> 
       <Rectangle 
        Width="100" 
        Height="100" 
        Fill="#27f" /> 
       <Rectangle 
        Width="100" 
        Height="100" 
        Fill="#27f" /> 
       <Rectangle 
        Width="100" 
        Height="100" 
        Fill="#27f" /> 
       <Rectangle 
        Width="100" 
        Height="100" 
        Fill="#27f" /> 
      </ListView.Items> 
     </ListView> 
    </Grid> 
</Window> 

これは完璧ではないが、それだけで完全に特定の仕事をしていません。子要素のサイズが異なる場合、正しく動作しません。この場合、は、startからendまでのインデックスを持つ子の合計として計算する必要があります。最後の行にも異なる条件を指定する必要があります。

おかげで再び、種類見知らぬ人:)

+0

あなたは実用的な解決策を見つけてうれしく思います。同じことを求めている人たちのために共有してくれてありがとう:) – Rachel

関連する問題