deblendewim
deblendewim

Reputation: 425

Vertical WrapPanel inside expander; I'm not able to wrap at window bottom

I'm working on a WPF project build a couple of years ago.

One control is an expander control which groups a list of check-controls.

This list of check-controls is arranged from Left to Right, Top to Bottom by a WrapPanel!

Now my goal is to arrange the check-controls from Top to Bottom, Left to Right where the vertical wrap occurs at the bottom of the parent window! Any tips/directions in how to achieve this would be awsome!!

I have extracted the issue in a code example to highlight the issue.

Behavior of the example: The code example has the WrapPanel's orientation set to Horizontal. This is to show you what sort of behavior I would like to achieve! The wrap breaks at the Right side of the parent window and a new row starts. When the check-controls overflow the Bottom of the screen a vertical scrollbar appears.

Behavior I would like to achieve: When the Bottom side of the parent window is reached (when the wrappanel is set to Vertical orientation) I want the wrap to break and start at the Top again. When the check-controls overflow the Right side of the window I want a horizontal scrollbar to appear.

Example: In the code: change the Orientation attribute of the Wrappanel to Vertical. Notice that there is only one column overflowing all the way down, there is no wrap at the bottom of the window! Because I'm not sure how to attach a zip file (with the demo project) I have added the code files below. Creating a WPF application (.net framework 3.5) and copying the code in the proper locations should do. If you do have issues running the example I can always mail you a VS2010 solution!

Project called VerticalWrapPanel

UserControl CheckControl.xaml

<UserControl x:Class="VerticalWrapPanel.CheckControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Border BorderThickness="1,1,1,1" BorderBrush="Black">
        <Label Content="{Binding Label}"/>
    </Border>
</UserControl>

UserControl CheckGroupControl.xaml

<UserControl x:Class="VerticalWrapPanel.CheckGroupControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:controls="clr-namespace:VerticalWrapPanel"
             x:Name="GroupControl">

<UserControl.Resources>
    <DataTemplate x:Key="CheckTemplate">
        <controls:CheckControl />
    </DataTemplate>
</UserControl.Resources>

<Expander BorderBrush="Black" Header="TEST" IsExpanded="{Binding ElementName=GroupControl, Path=IsExpanded}">
    <ItemsControl ItemsSource="{Binding Checks}" ItemTemplate="{StaticResource CheckTemplate}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Expander>
</UserControl>

CheckGroupControl.xaml.cs

using System.Windows;
using System.Windows.Controls;

namespace VerticalWrapPanel
{
    /// <summary>
    /// Interaction logic for CheckGroupControl.xaml
    /// </summary>
    public partial class CheckGroupControl : UserControl
    {
        public CheckGroupControl()
        {
            InitializeComponent();
        }

        public static DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(CheckGroupControl));
        public bool IsExpanded
        {
            get { return (bool)GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, value); }
        }

    }
}

MainWindow.xaml

<Window x:Class="VerticalWrapPanel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:controls="clr-namespace:VerticalWrapPanel"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">

    <Window.Resources>
        <DataTemplate x:Key="CheckGroupsTemplate">
            <controls:CheckGroupControl />
        </DataTemplate>
    </Window.Resources>

    <ScrollViewer CanContentScroll="False">
        <ItemsControl ItemsSource="{Binding CheckGroups}" ItemTemplate="{StaticResource CheckGroupsTemplate}" />
    </ScrollViewer>
</Window>

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;

namespace VerticalWrapPanel
{
    public class MyModel
    {
        public ObservableCollection<CheckGroup> CheckGroups { get; set; }
    }

    public class Check
    {
        public string Label { get; set; }
    }

    public class CheckGroup
    {
        public List<Check> Checks { get; set; }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            DataContext = new MyModel
                                   {
                                       CheckGroups = new ObservableCollection<CheckGroup>
                                                         {
                                                             new CheckGroup
                                                                 {
                                                                     Checks =
                                                                         new List<Check>
                                                                             {
                                                                                  new Check {Label = "Check 0001"}
                                                                                 ,new Check {Label = "Check 0002"}
                                                                                 ,new Check {Label = "Check 0003"}
                                                                                 ,new Check {Label = "Check 0004"}
                                                                                 ,new Check {Label = "Check 0005"}
                                                                                 ,new Check {Label = "Check 0006"}
                                                                                 ,new Check {Label = "Check 0007"}
                                                                                 ,new Check {Label = "Check 0008"}
                                                                                 ,new Check {Label = "Check 0009"}
                                                                                 ,new Check {Label = "Check 0000"}
                                                                                 ,new Check {Label = "Check 0002"}
                                                                                 ,new Check {Label = "Check 0003"}
                                                                                 ,new Check {Label = "Check 0004"}
                                                                                 ,new Check {Label = "Check 0005"}
                                                                                 ,new Check {Label = "Check 0006"}
                                                                                 ,new Check {Label = "Check 0007"}
                                                                                 ,new Check {Label = "Check 0008"}
                                                                                 ,new Check {Label = "Check 0009"}
                                                                                 ,new Check {Label = "Check 0000"}
                                                                                 ,new Check {Label = "Check 0002"}
                                                                                 ,new Check {Label = "Check 0003"}
                                                                                 ,new Check {Label = "Check 0004"}
                                                                                 ,new Check {Label = "Check 0005"}
                                                                                 ,new Check {Label = "Check 0006"}
                                                                                 ,new Check {Label = "Check 0007"}
                                                                                 ,new Check {Label = "Check 0008"}
                                                                                 ,new Check {Label = "Check 0009"}
                                                                                 ,new Check {Label = "Check 0000"}
                                                                             }
                                                                 }
                                                              , new CheckGroup
                                                                 {
                                                                     Checks =
                                                                         new List<Check>
                                                                             {
                                                                                  new Check {Label = "Check 0011"}
                                                                                 ,new Check {Label = "Check 0012"}
                                                                                 ,new Check {Label = "Check 0013"}
                                                                                 ,new Check {Label = "Check 0014"}
                                                                                 ,new Check {Label = "Check 0015"}
                                                                                 ,new Check {Label = "Check 0016"}
                                                                                 ,new Check {Label = "Check 0017"}
                                                                                 ,new Check {Label = "Check 0018"}
                                                                                 ,new Check {Label = "Check 0019"}
                                                                                 ,new Check {Label = "Check 0010"}
                                                                             }
                                                                 }
                                                         }

                                   };
        }
    }
}

Upvotes: 2

Views: 2345

Answers (3)

deblendewim
deblendewim

Reputation: 425

Thanks Jefim and Rachel for pushing me into the right direction. For me the missing link was actually the MaxHeight on the CheckGroup DataTemplate as Jefim pointed out.

I now set MaxHeight to the axtual height of the window minus some height to make sure the other groups stay visible.

I know this might look like a dirty solution, but with some nifty wpf brushes it will look sweet and the behavior is very close to my expectations!

Changes to the code to become the behavior I wanted:

MainWindow.xaml

<Window 
    x:Class="VerticalWrapPanel.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:controls="clr-namespace:VerticalWrapPanel" 
    xmlns:converters="clr-namespace:VerticalWrapPanel" 
    Title="MainWindow" 
    Height="350"
    Width="525" 
    Loaded="Window_Loaded"
    x:Name="MyWindow">

    <Window.Resources>
        <converters:ActualHeightReduce x:Key="ActualHeightReduce" />
        <DataTemplate x:Key="CheckGroupsTemplate">
            <controls:CheckGroupControl MaxHeight="{Binding ElementName=MyWindow, Path=ActualHeight, Converter={StaticResource ActualHeightReduce}}" />
        </DataTemplate>
    </Window.Resources>

    <ScrollViewer CanContentScroll="False">
        <ItemsControl ItemsSource="{Binding CheckGroups}" ItemTemplate="{StaticResource CheckGroupsTemplate}" />
    </ScrollViewer>
</Window>

Addition to MainWindow.xaml.cs (inside the namespace VerticalWrapPanel)

public class ActualHeightReduce : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || string.IsNullOrEmpty(value.ToString()))
            return value;

        double vValue;

        if (Double.TryParse(value.ToString(), out vValue))
        {
            return vValue - 75;
        }

        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

And the CheckGroupControl.xaml now has a nice horizontal scroll:

<UserControl
    x:Class="VerticalWrapPanel.CheckGroupControl" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:controls="clr-namespace:VerticalWrapPanel"
    x:Name="GroupControl">

    <UserControl.Resources>
        <DataTemplate x:Key="CheckTemplate">
            <controls:CheckControl />
        </DataTemplate>
    </UserControl.Resources>

    <Expander BorderBrush="Black" Header="TEST" IsExpanded="{Binding ElementName=GroupControl,     Path=IsExpanded}">
        <ItemsControl ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Auto" ItemsSource="{Binding Checks}" ItemTemplate="{StaticResource CheckTemplate}">
            <ItemsControl.Template>
                <ControlTemplate>
                    <ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
                        <ItemsPresenter />
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Vertical" IsItemsHost="True" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Expander>
</UserControl>

And of course the Orientation of the wrappanel in the CheckControl still has to be set to Vertical ;)

Upvotes: 0

Rachel
Rachel

Reputation: 132548

To make your WrapPanel wrap vertically, you need to limit it's Height

For example, if made your WrapPanel definition

<WrapPanel Orientation="Vertical" Height="100" />

you would get the Items wrapping the way you want.

enter image description here

Also, your ScrollViewer will by default not show Horizontal scroll bars. To enable them, set the HorizontalScrollBarVisibility

<ScrollViewer CanContentScroll="False" HorizontalScrollBarVisibility="Auto">
    <ItemsControl ItemsSource="{Binding CheckGroups}" ItemTemplate="{StaticResource CheckGroupsTemplate}" />
</ScrollViewer>

Upvotes: 2

Jefim
Jefim

Reputation: 3077

Try setting the VerticalScrollBarVisibility="Disabled" and HorizontalScrollBarVisibility="Auto" on the items control. This will disable the vertical scrolling. Also, alter the template of the items control to include a nice scroll viewer to enable scrolling.

<Expander BorderBrush="Black" Header="TEST" IsExpanded="{Binding ElementName=GroupControl,     Path=IsExpanded}">
    <ItemsControl ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Auto" ItemsSource="{Binding Checks}" ItemTemplate="{StaticResource CheckTemplate}">
        <ItemsControl.Template>
            <ControlTemplate>
                <ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
                    <ItemsPresenter />
                </ScrollViewer>
            </ControlTemplate>
        </ItemsControl.Template>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical" IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Expander>

UPDATE

Your code is still going with one column of items - that is normal in your case. The XAML code I posted will only work if you constraint you top-level items' height (e.g. using Height or MaxHeight). My XAML code assumes that you have a limited amount of space for the expander and you are showing that expander within an ItemsControl which gives its items as much space as they want. For example, alter your data template to look as follows:

<Window.Resources>
    <DataTemplate x:Key="CheckGroupsTemplate">
        <controls:CheckGroupControl MaxHeight="100"/>
    </DataTemplate>
</Window.Resources>

Now your expanders will have a maximum height and when it is reached they will start wrapping. Without that MaxHeight the expander will be given the opportunity to take as much space as it wants and the WrapPanel inside it will, obviously, just lay out all the items in one vertical line since there is no constraint.

Upvotes: 2

Related Questions