WPF add header to ListBox so it scrolls like DataGrid
I'm trying to create a layout that uses a ListBox
and my custom header that looks like a ruler, but for dates (with explicit start and end dates). The goal is to have an appearance and feel similar to a DataGrid
, except that the column header row would be replaced by my DateTape
object. When the user scrolls horizontally, the DateTape
and ListBox
both scroll, but when the user scrolls vertically, only the ListBox
scrolls and the DateTape
stays at the top (like the column header row in the DataGrid
).
So far, the best I've been able to do is as follows:
<Window x:Class="ProjectNS.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:ProjectNS"
Title="MainWindow" Height="350" Width="600">
<Window.Resources>
<DataTemplate x:Key="itemTemplate">
<my:CustomRectangle HorizontalAlignment="Left" VerticalAlignment="Top" />
</DataTemplate>
</Window.Resources>
开发者_运维技巧 <DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File" />
</Menu>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<DockPanel>
<my:DateTape DockPanel.Dock="Top" VerticalAlignment="Top">
<my:DateTape.Dates>
<CalendarDateRange Start="10/4/2011" End="11/4/2011" />
</my:DateTape.Dates>
</my:DateTape>
<ListBox ItemTemplate="{StaticResource itemTemplate}" />
</DockPanel>
</ScrollViewer>
</DockPanel>
</Window>
The only problem I have with this solution is that the vertical scrollbar for the ListBox
is at the extreme right of the control which means the user has to scroll horizontally to make the scrollbar appear. I need the scrollbar visible at all times.
I tried placing the DateTape
and ListBox
into a ScrollViewer
, but then the DateTape
scrolls out of view when scrolling vertically.
FYI - My CustomRectangle
object is a UserControl
that allows the user to adjust the horizontal position and width real-time to position it as desired in line with the DateTape
.
I ended up having to restructure a bit. The ListBox
is now an ItemsControl
nested within a ScrollViewer
with the vertical scroll bar hidden (not disabled). I also have an independent ScrollBar
docked to the right side that is tied to the vertical scroll bar in the ScrollViewer
in the code-behind. This handles the vertical scrolling. Finally, a secondary ScrollViewer
contains the DateTape
and the ItemsControl
set to handle the horizontal scrolling.
XAML
<DockPanel x:Name="dockPanel">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File" />
</Menu>
<ScrollBar x:Name="verticalScrollBar"
DockPanel.Dock="Right"
SmallChange="1"
LargeChange="3"
Scroll="verticalScrollBar_Scroll"
SizeChanged="verticalScrollBar_SizeChanged"
Style="{StaticResource scrollBarHiderStyle}"
Maximum="{Binding ElementName=listScroller, Path=ScrollableHeight}" />
<ScrollViewer x:Name="dateScroller"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Auto"
DockPanel.Dock="Top">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<my:DateTape x:Name="dateTape"
DockPanel.Dock="Top"
VerticalAlignment="Top"
Dates="{Binding Source={StaticResource dateRange}}" />
<ScrollViewer x:Name="listScroller" VerticalScrollBarVisibility="Hidden" Grid.Row="1" Foreground="{x:Null}" Panel.ZIndex="1">
<ItemsControl x:Name="itemsList"
ItemTemplate="{StaticResource itemTemplate}"/>
</ScrollViewer>
</Grid>
</ScrollViewer>
</DockPanel>
C#
// this function merely sets the scroll bar thumb size
private void verticalScrollBar_SizeChanged(object sender, SizeChangedEventArgs e)
{
verticalScrollBar.Track.ViewportSize = itemsList.ActualHeight / 2;
}
// this function links the scroll bar to the scrollviewer
private void verticalScrollBar_Scroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e)
{
listScroller.ScrollToVerticalOffset(e.NewValue);
}
I tried to tie the independent ScrollBar
to the scroll bar in the ItemsControl
through use of a ItemsPanelTemplate
, but I couldn't get that to work.
精彩评论