WPF wrap panel and scrolling WPF wrap panel and scrolling wpf wpf

WPF wrap panel and scrolling


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.


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

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


And now you can scroll.



Hope I helped.


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