Search code examples
wpfscrollitemscontrolmeasureoverridearrangeoverride

WPF - IScrollInfo implementation


The problem: An Window that has an ItemsControl with some items(let's say Rectangles). The window has MinWidth and MinHeight setted(eg. 300) I need that when I resize window if the rectangles has not enough space to be displayed to be displayed in 2 columns. And if in 2 columns still does not have enought space to show a scroll viewer.

What I've tried: 1. Create an extended ItemsControl:

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <local:MyGrid IsItemsHost="True" x:Name="PART_ItemsPanel" Initialized="OnItemsPanelInitialized" CanVerticallyScroll="True" CanHorizontallyScroll="True">
            <local:MyGrid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </local:MyGrid.ColumnDefinitions>
            <local:MyGrid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
            </local:MyGrid.RowDefinitions>
        </local:MyGrid>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

My ItemsControl has as ItemsPanelTemplate an Extended grid:

public class MyGrid : Grid, IScrollInfo { ....IScrollInfo implementation }

I use this grid thinking that when ItemsControl will PrepareContainerForItemOverride() I can use this to split the Items in two columns.

The ideea is "taken" from a conference....but I no not know what to do next... I have questions like: when I override Measure and Arrange for data grid I set the position of the items in DataGrid, but then it is called PrepareContainerForItemOverride()...then what? I should compute the no of rows I should make? but if then I resize again the window...PrepareContainerForItemOverride() won't be called ...

It is over me this issue...please give me a clue if some of you have one. Thank you guys!


Solution

  • I wanted to share the solution I took...if anyone needs it.

    To avoid the implementation of IScrollInfo I changed ItemsControl with ListBox control. So it remains that I override Arrange. In Arrange I calculate my first column's items height and I arrange the Items in GridPanel. I use the dispatcher because the scrollViewer arrange is not always VALID and I am counting on it to get the visible height.

    <ListBox x:Uid="allListBox" x:Name="adminListBoxControl" ItemTemplate ="{DynamicResource LinkButtonItemTemplate}" Margin="15" BorderBrush="Transparent" Background="Transparent">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:GridPanel>
                            <local:GridPanel.ColumnDefinitions>
                                <ColumnDefinition Width="0.45*"/>
                                <ColumnDefinition Width="0.45*"/>
                                <ColumnDefinition Width="0.1*"/>
                            </local:GridPanel.ColumnDefinitions>
                            <local:GridPanel.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </local:GridPanel.RowDefinitions>
                        </local:GridPanel>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
            </ListBox>
    
    protected override Size ArrangeOverride(Size finalSize)
        {
           Size arrangeSize = base.ArrangeOverride(finalSize);
    
            lastItemInFirstColumn = -1;
            double totalHeight = 0;
    
            DispatcherPriority p = ScrollViewer.IsArrangeValid ? DispatcherPriority.Normal : DispatcherPriority.Input;
            if (o != null)
                o.Abort();
    
            o = Dispatcher.BeginInvoke(p, (Action)(() =>
            {
                double fitHeight = ScrollViewer.ActualHeight;
                foreach (UIElement child in this.InternalChildren)
                {
                    if (totalHeight + child.DesiredSize.Height < fitHeight)
                    {
                        totalHeight += child.DesiredSize.Height;
                        lastItemInFirstColumn++;
                    }
                    else
                    {
                        if (lastItemInFirstColumn + 1 < InternalChildren.Count - (lastItemInFirstColumn + 1))
                            lastItemInFirstColumn++;
                        break;
                    }
                }
    
                //set items positions
                ArrangeItemsInGrid();
            }));
    
            o.Completed += (s, e) => { o = null; };
            return arrangeSize;
        }