none
Scrolling ListBox and Header

    Question

  • I have a complex ListBox, even more so than that below, with headers. 
    The only thing I am trying to do is scroll them both together horizontally.

    The item collection in Oject1 and Object2 will always be in sync with the number of items and should not be considered but can range from {1..n}.  This is what will push the control out horizontally.

    I've included the XAML for the User control and code behind which loads data as well as a design time data file.

    Any help is appreciated.

    <UserControl x:Class="Junk.TestControl"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="272" d:DesignWidth="1284"
                 d:DataContext="{d:DesignData Source=/DesignData/Data.xaml}"
        >
    
        <UserControl.Resources>
            <DataTemplate x:Key="TurnTemplate">
                <TextBox Width="80" MaxWidth="80" MinWidth="80" Height="40" Text="{Binding Path=Field6}" VerticalAlignment="Stretch" Background="LightGray" />
            </DataTemplate>
    
            <DataTemplate x:Key="ItemTemplate">
                <TextBox MaxWidth="80" MinWidth="80" Text="{Binding Path=.}" />
            </DataTemplate>
    
            <DataTemplate x:Key="custTemplate">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBox Text="{Binding Path=Field1}" Width="225"/>
    
                        <DockPanel HorizontalAlignment="Stretch" LastChildFill="True">
    
                            <ListBox DockPanel.Dock="Left" ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource ItemTemplate}" BorderThickness="0" >
                                <ListBox.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Orientation="Horizontal"></StackPanel>
                                    </ItemsPanelTemplate>
                                </ListBox.ItemsPanel>
                            </ListBox>
    
                            <TextBox Width="45" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="{Binding Path=Field5}" TextAlignment="Right"  Margin="0,0,0,1" />
                            <TextBox Width="75" DockPanel.Dock="Left" VerticalAlignment="Bottom" TextAlignment="Right" Margin="0,0,0,1" />
                            <CheckBox Width="35" DockPanel.Dock="Left" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="10,0,-10,6" />
                            <TextBox Width="40" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0,0,0,1" />
                            <Button Width="15" Height="22" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0,0,0,1">A</Button>
                            <Button Width="15" Height="22" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0,0,0,1">B</Button>
    
                        </DockPanel>
    
    
                    </StackPanel>
    
                    <StackPanel Orientation="Horizontal">
                        <TextBox Text="{Binding Path=Field2}" Width="200" />
                        <TextBox Text="{Binding Path=Field3}" Width="200" />
                        <TextBox Text="{Binding Path=Field4}" MinWidth="150" />
                    </StackPanel>
    
                    <Rectangle x:Name="rectangle1" Height="2" Fill="Black" Stroke="Black" HorizontalAlignment="Stretch" />
    
                </StackPanel>
            </DataTemplate>
    
        </UserControl.Resources>
    
    
        <DockPanel LastChildFill="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" >
    
            <StackPanel DockPanel.Dock="Left" Margin="5">
                <TextBlock Text="Other things" />
                <TextBlock Text="Other things" />
            </StackPanel>
    
            <DockPanel LastChildFill="True">
                <DockPanel DockPanel.Dock="Top" HorizontalAlignment="Stretch" LastChildFill="False">
                    <TextBox DockPanel.Dock="Left" Width="229" />
                    <ListBox DockPanel.Dock="Left" ItemsSource="{Binding Path=MyObjects2}" ItemTemplate="{StaticResource TurnTemplate}" BorderThickness="0" >
                        <ListBox.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal"></StackPanel>
                            </ItemsPanelTemplate>
                        </ListBox.ItemsPanel>
                    </ListBox>
                    <TextBox Width="45" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H1" TextAlignment="Right" Margin="0,0,0,1" />
                    <TextBox Width="75" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H2" TextAlignment="Right" Margin="0,0,0,1" />
                    <TextBox Width="35" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H3" Margin="0,0,0,1" />
                    <TextBox Width="40" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H4" Margin="0,0,0,1" />
    
                </DockPanel>
    
                <ListBox DockPanel.Dock="Bottom" ItemsSource="{Binding Path=MyObjects1}" ItemTemplate="{StaticResource custTemplate}" 
                         Margin="0,0,20,20"
                         ScrollViewer.VerticalScrollBarVisibility="Auto"
                         ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                        />
            </DockPanel>
        </DockPanel>
    
    </UserControl>
    
    
    <vm:MyDataContext 
        xmlns:vm="clr-namespace:Junk;assembly=Junk"
        xmlns:System="clr-namespace:System;assembly=mscorlib">
        <vm:MyDataContext.MyObjects1>
            <vm:Object1
                Field1 = "Item A"
                Field2 = "a"
                Field3 = "b"
                Field4 = "c"
                <vm:Object1.Items>
                    <System:String>Thing 1</System:String>
                    <System:String>Thing 2</System:String>
                    <System:String>Thing 3</System:String>
                    <System:String>Thing 4</System:String>
                    <System:String>Thing 5</System:String>
                    <System:String>Thing 6</System:String>
                    <System:String>Thing 7</System:String>
                    <System:String>Thing 8</System:String>
                </vm:Object1.Items>
            </vm:Object1>
            <vm:Object1
                Field1 = "Item D"
                Field2 = "d"
                Field3 = "e"
                Field4 = "f"
                <vm:Object1.Items>
                    <System:String>Thing 1</System:String>
                    <System:String>Thing 2</System:String>
                    <System:String>Thing 3</System:String>
                    <System:String>Thing 4</System:String>
                    <System:String>Thing 5</System:String>
                    <System:String>Thing 6</System:String>
                    <System:String>Thing 7</System:String>
                    <System:String>Thing 8</System:String>
                </vm:Object1.Items>
            </vm:Object1>
            <vm:Object1
                Field1 = "Item G"
                Field2 = "g"
                Field3 = "h"
                Field4 = "i"
                <vm:Object1.Items>
                    <System:String>Thing 1</System:String>
                    <System:String>Thing 2</System:String>
                    <System:String>Thing 3</System:String>
                    <System:String>Thing 4</System:String>
                    <System:String>Thing 5</System:String>
                    <System:String>Thing 6</System:String>
                    <System:String>Thing 7</System:String>
                    <System:String>Thing 8</System:String>
                </vm:Object1.Items>
            </vm:Object1>
        </vm:MyDataContext.MyObjects1>
    
        <vm:MyDataContext.MyObjects2>
            <vm:Object2 Field6="Something 1" />
            <vm:Object2 Field6="Something 2" />
            <vm:Object2 Field6="Something 3" />
            <vm:Object2 Field6="Something 4" />
            <vm:Object2 Field6="Something 5" />
            <vm:Object2 Field6="Something 6" />
            <vm:Object2 Field6="Something 7" />
            <vm:Object2 Field6="Something 8" />
        </vm:MyDataContext.MyObjects2>
    
    </vm:MyDataContext>
    
       public partial class TestControl : UserControl
        {
            public TestControl()
            {
                InitializeComponent();
    
                ObservableCollection<Object1> myObjects1 = new ObservableCollection<Object1>();
    
    
                myObjects1.Add(new Object1
                {
                    Field1 = "A",
                    Field2 = "a",
                    Field3 = "b",
                    Field4 = "c",
                    Items = new ObservableCollection<string> { "Thing 1", "Thing 2", "Thing 3", "Thing 4", "Thing 5", "Thing 6", "Thing 7", "Thing 8" }
                });
    
                myObjects1.Add(new Object1
                {
                    Field1 = "D",
                    Field2 = "d",
                    Field3 = "e",
                    Field4 = "f",
                    Items = new ObservableCollection<string> { "Thing 1", "Thing 2", "Thing 3", "Thing 4", "Thing 5", "Thing 6", "Thing 7", "Thing 8" }
                });
    
                myObjects1.Add(new Object1
                {
                    Field1 = "G",
                    Field2 = "g",
                    Field3 = "h",
                    Field4 = "1",
                    Items = new ObservableCollection<string> { "Thing 1", "Thing 2", "Thing 3", "Thing 4", "Thing 5", "Thing 6", "Thing 7", "Thing 8" }
                });
    
                ObservableCollection<Object2> myObjects2 = new ObservableCollection<Object2>
                {
                    new Object2{ Field6 = "Something 1" },
                    new Object2{ Field6 = "Something 2" },
                    new Object2{ Field6 = "Something 3" },
                    new Object2{ Field6 = "Something 4" },
                    new Object2{ Field6 = "Something 5" },
                    new Object2{ Field6 = "Something 6" },
                    new Object2{ Field6 = "Something 7" },
                    new Object2{ Field6 = "Something 8" },
                    
                };
    
                MyDataContext info = new MyDataContext
                {
                    MyObjects1 = myObjects1,
                    MyObjects2 = myObjects2
                };
    
                this.DataContext = info;
            }
    
        }

    Saturday, March 10, 2012 12:01 AM

Answers

  • Hi Webbert,

    If you want your first DockPanel could scroll, you have to wrap it into a ScrollViewer, and then you could sync two ScrollViewers, refer to below xaml code, I change it based on your code:

    <DockPanel LastChildFill="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" >
    
        <StackPanel DockPanel.Dock="Left" Margin="5">
            <TextBlock Text="Other things" />
            <TextBlock Text="Other things" />
        </StackPanel>
    
        <DockPanel LastChildFill="True">
            <ScrollViewer Width="{Binding ElementName=listbox, Path=ActualWidth}" Name="scrollviewer1" DockPanel.Dock="Top" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
            <DockPanel DockPanel.Dock="Top" HorizontalAlignment="Stretch" LastChildFill="False">
                <TextBox DockPanel.Dock="Left" Width="229" />
                <ListBox DockPanel.Dock="Left" ItemsSource="{Binding Path=MyObjects2}" ItemTemplate="{StaticResource TurnTemplate}" BorderThickness="0"  ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"></StackPanel>
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                </ListBox>
                <TextBox Width="45" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H1" TextAlignment="Right" Margin="0,0,0,1" />
                <TextBox Width="75" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H2" TextAlignment="Right" Margin="0,0,0,1" />
                <TextBox Width="35" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H3" Margin="0,0,0,1" />
                <TextBox Width="40" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H4" Margin="0,0,0,1" />
    
            </DockPanel>
            </ScrollViewer>
            <ListBox Name="listbox" ScrollViewer.ScrollChanged="ListBox_ScrollChanged" DockPanel.Dock="Bottom" ItemsSource="{Binding Path=MyObjects1}" ItemTemplate="{StaticResource custTemplate}" 
                        Margin="0,0,20,20"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        ScrollViewer.HorizontalScrollBarVisibility="Auto"
                    />
        </DockPanel>
    </DockPanel>

    And then you could simple sync it by ScrollChanged event handler:

    private void ListBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        Border scrollBorder = VisualTreeHelper.GetChild((ListBox)sender, 0) as Border;
        ScrollViewer scrollViewer = scrollBorder.Child as ScrollViewer;  
        scrollviewer1.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset);
    }

    additonal, there is another method could sync two scrollviewer in codeproject:
    http://www.codeproject.com/Articles/39244/Scroll-Synchronization

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Webbert Tuesday, March 13, 2012 3:09 PM
    Tuesday, March 13, 2012 5:06 AM

All replies

  • Hi Webbert,

    Could you upload a .zip file with me to repro your issue, above code is not included all, do following steps to upload your sample code:

    1) type "skydrive.live.com" in the IE address bar.
    2) sign in your account on the page, then click add files to upload your files(remember upload as "public").
    3) give me download link after upload successfully.

    Additional, could you explain more about what effect you want to achieve.

    best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, March 12, 2012 5:46 AM
  • Couldn't figure out how to get the direct link to the file, but it is in the Forum directory once you get here: https://skydrive.live.com/?cid=E02420377ABA0395&id=E02420377ABA0395%21144

    Once you start the app, if you scroll the list box, the headers don't move with it.  I want the headers to scroll horizontally with the data.

    Monday, March 12, 2012 11:30 AM
  • Hi Webbert,

    If you want your first DockPanel could scroll, you have to wrap it into a ScrollViewer, and then you could sync two ScrollViewers, refer to below xaml code, I change it based on your code:

    <DockPanel LastChildFill="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" >
    
        <StackPanel DockPanel.Dock="Left" Margin="5">
            <TextBlock Text="Other things" />
            <TextBlock Text="Other things" />
        </StackPanel>
    
        <DockPanel LastChildFill="True">
            <ScrollViewer Width="{Binding ElementName=listbox, Path=ActualWidth}" Name="scrollviewer1" DockPanel.Dock="Top" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
            <DockPanel DockPanel.Dock="Top" HorizontalAlignment="Stretch" LastChildFill="False">
                <TextBox DockPanel.Dock="Left" Width="229" />
                <ListBox DockPanel.Dock="Left" ItemsSource="{Binding Path=MyObjects2}" ItemTemplate="{StaticResource TurnTemplate}" BorderThickness="0"  ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"></StackPanel>
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                </ListBox>
                <TextBox Width="45" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H1" TextAlignment="Right" Margin="0,0,0,1" />
                <TextBox Width="75" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H2" TextAlignment="Right" Margin="0,0,0,1" />
                <TextBox Width="35" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H3" Margin="0,0,0,1" />
                <TextBox Width="40" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H4" Margin="0,0,0,1" />
    
            </DockPanel>
            </ScrollViewer>
            <ListBox Name="listbox" ScrollViewer.ScrollChanged="ListBox_ScrollChanged" DockPanel.Dock="Bottom" ItemsSource="{Binding Path=MyObjects1}" ItemTemplate="{StaticResource custTemplate}" 
                        Margin="0,0,20,20"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        ScrollViewer.HorizontalScrollBarVisibility="Auto"
                    />
        </DockPanel>
    </DockPanel>

    And then you could simple sync it by ScrollChanged event handler:

    private void ListBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        Border scrollBorder = VisualTreeHelper.GetChild((ListBox)sender, 0) as Border;
        ScrollViewer scrollViewer = scrollBorder.Child as ScrollViewer;  
        scrollviewer1.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset);
    }

    additonal, there is another method could sync two scrollviewer in codeproject:
    http://www.codeproject.com/Articles/39244/Scroll-Synchronization

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Webbert Tuesday, March 13, 2012 3:09 PM
    Tuesday, March 13, 2012 5:06 AM
  • Thanks.  That is what I was looking for.

    I made a couple of changes to get the scrolling correct, like hiding the scrollbar and adding hidden buttons to keep the width of the 2 list the same.  If I didn't then the scrolling got weird when scrolling all the way to the right.

    I'm including the changes for anyone that may be interested.

    Thanks for your help

    	<DockPanel LastChildFill="True" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" >
    		<StackPanel DockPanel.Dock="Left" Margin="5">
    			<TextBlock Text="Other things" />
    			<TextBlock Text="Other things" />
    		</StackPanel>
    		<DockPanel LastChildFill="True">
    			<ScrollViewer Width="{Binding ElementName=listbox, Path=ActualWidth}" Name="scrollviewer1" DockPanel.Dock="Top" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Hidden" Padding="0" Margin="-20,0,0,0">
    				<DockPanel DockPanel.Dock="Top" HorizontalAlignment="Stretch" LastChildFill="False" >
    					<TextBox DockPanel.Dock="Left" Width="229"/>
    					<ListBox DockPanel.Dock="Left" ItemsSource="{Binding Path=MyObjects2}" ItemTemplate="{StaticResource TurnTemplate}" BorderThickness="0"  ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    						<ListBox.ItemsPanel>
    							<ItemsPanelTemplate>
    								<StackPanel Orientation="Horizontal"></StackPanel>
    							</ItemsPanelTemplate>
    						</ListBox.ItemsPanel>
    					</ListBox>
    					<TextBox Width="45" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H1" TextAlignment="Right" Margin="0,0,0,1" />
    					<TextBox Width="75" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H2" TextAlignment="Right" Margin="0,0,0,1" />
    					<TextBox Width="35" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H3" Margin="0,0,0,1" />
    					<TextBox Width="40" DockPanel.Dock="Left" VerticalAlignment="Bottom" Text="H4" Margin="0,0,0,1" />
    					<Button Width="15" Height="22" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0,0,0,1" Visibility="Hidden">A</Button>
    					<Button Width="17" Height="22" DockPanel.Dock="Left" VerticalAlignment="Bottom" Margin="0,0,0,1" Visibility="Hidden">B</Button>
    				</DockPanel>
    			</ScrollViewer>
    			<ListBox Name="listbox" ScrollViewer.ScrollChanged="ListBox_ScrollChanged" DockPanel.Dock="Bottom" ItemsSource="{Binding Path=MyObjects1}" ItemTemplate="{StaticResource custTemplate}" 
                         Margin="0,0,20,20"
                         ScrollViewer.VerticalScrollBarVisibility="Auto"
                         ScrollViewer.HorizontalScrollBarVisibility="Auto"
                        />
    		</DockPanel>
    	</DockPanel>

    Tuesday, March 13, 2012 3:09 PM