Search code examples
wpflayoutpanelmeasureoverridearrangeoverride

WPF - Custom panel size to content


my panel attempts to do something similar to UniformGrid , it has a ColumnNumber property, in addition to ItemContainerWidth (40.0) and ItemContainerHeight (20.0) dependency properties.

I want my panel to size to it's content .

my MesureOverride :

 protected override Size MeasureOverride(Size constraint)
 {            
     if (constraint.Width == double.PositiveInfinity || constraint.Height == double.PositiveInfinity)
         return Size.Empty;

     for (int i = 0; i < InternalChildren.Count; i++)
     {
         InternalChildren[i].Measure(new Size(ItemContainerWidth, ItemContainerHeight));
     }

     return constraint;
 }

my ArrangeOverride :

  protected override Size ArrangeOverride(Size finalSize)
  {                  
        int currentColumn = 0; 
        int currentRow = 0 ; 

        for (int i = 0; i < InternalChildren.Count; i++)
        {
            UIElement child = InternalChildren[i];

            if (currentColumn == ColumnCount)
            {
                currentColumn = 0;
                currentRow++;
            }
            currentColumn++; 

            double top = currentRow * ItemContainerHeight;
            double left = currentColumn * ItemContainerWidth;

            child.Arrange(new Rect(left, top, ItemContainerWidth, ItemContainerHeight));
        }

        return finalSize;
    }

My panel's ActualWidth and Height Equal to finalSize , i would like the panel's size to be the one returned from MeasureOvride (constraint) ,

if i force this my panel is located differently on the screen , i just want my panel to size to it's content like WrapPanel or StackPanel,

with finalSize

with finalSize

with constraint size (100,100) for test purposes

with constraint size

my use of the panel : XAML :

<Grid>
   <ItemsControl ItemsSource="{Binding Items, Mode=OneWay}" >            
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <uni:ScrollableUniformGrid  ColumnCount="12" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border BorderBrush="Black" BorderThickness="0,0,1,1">
                    <TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>   
    </ItemsControl>
 </Grid>

Solution

  • I am sorry I think I missunderstood you first time.

    Take a look at this:

    <Grid>
        <ItemsControl ItemsSource="{Binding}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <local:ScrollableUniformGrid  HorizontalAlignment="Left" VerticalAlignment="Top" ColumnCount="3" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
    
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Black" BorderThickness="0,0,1,1">
                        <TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
    

    This is how your ArrangeOverride shall look like. MeasureOverride may stay the same.

    protected override Size ArrangeOverride(Size finalSize)
    {
        int currentColumn = 0;
        int currentRow = 0;
    
    
        for (int i = 0; i < InternalChildren.Count; i++)
        {
            UIElement child = InternalChildren[i];
    
            double top = currentRow * ItemContainerHeight;
            double left = currentColumn * ItemContainerWidth;
    
            if (currentColumn == ColumnCount)
            {
                currentColumn = 0;
                currentRow++;
            }
            else
            {
                currentColumn++;
            }
    
            child.Arrange(new Rect(left, top, ItemContainerWidth, ItemContainerHeight));
        }
    
        return new Size((ColumnCount + 1) * ItemContainerWidth, (currentRow + 1) * ItemContainerHeight);
    }
    

    Take a look at this photo. The panel fits to content for me.

    enter image description here