Reputation: 159
How can i horizontally scroll a listview within single column in a DataGrid, using a scrollbar that is external to the DataGrid, ensuring that each row scrolls at the same time?
(I would also like the starting position of the scrollbar to be all the way to the right, so that the last item in the listview is visible when the data is loaded)
I cannot use and third-party libraries or dlls.
Any help would be greatly appreciated.
Many thanks
EDIT
First column sample code
<DataGridTemplateColumn Width="325" Header="Header Text" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding TrendList}"
BorderThickness="0"
Background="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
>
<ListBox.Resources>
<Style TargetType="ScrollViewer">
<Setter Property="resex:ScrollSynchronizer.ScrollGroup" Value="Group1" />
<Setter Property="resex:ScrollSynchronizer.AlwaysScrollToEnd" Value="True" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Margin="2,2,2,2" Width="25" Height="25"
HorizontalAlignment="Center" VerticalAlignment="Center"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Content="{Binding ArisingList.Count}"
FontWeight="Bold
>
<interact:Interaction.Triggers>
<interact:EventTrigger EventName="MouseDoubleClick">
<interact:InvokeCommandAction Command="{Binding OpenPCICommand}"
CommandParameter="{Binding ArisingList}" />
</interact:EventTrigger>
</interact:Interaction.Triggers>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Background" Value="Green"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ArisingList.Count}" Value="0">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
<Label.ToolTip>
<TextBlock x:Name="pciID" Text="{Binding PciID, StringFormat='PCI Number : {0}'}" />
</Label.ToolTip>
</Label>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</DataTemplate>
This is what i have so far. The scrollbars are all linked internally but they arent linked to any external scrollbar
Upvotes: 1
Views: 1241
Reputation: 13679
I managed to create a synchronous scroll behavior using Attached properties
may this help you achieve your goal in some way
for this I have made some changes to your template as well
ListBox
to ItemsControl
Wrapped
ItemsControl in a ScrollViewer
ScrollHelper.IsSyncScrollEnabled
& ScrollHelper.ScrollValue
to ScrollViewer
Name
to main DataGrid
sample xaml
<Grid xmlns:l="clr-namespace:CSharpWPF">
<DataGrid x:Name="dGrid" ItemsSource="{Binding MyDataSource}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="325"
Header="Header Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Hidden"
l:ScrollHelper.IsSyncScrollEnabled="true"
l:ScrollHelper.ScrollValue="{Binding (l:ScrollHelper.ScrollValue),ElementName=dGrid,Mode=TwoWay}">
<ItemsControl ItemsSource="{Binding TrendList}">
<ItemsControl.ItemTemplate>
... your item template here
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
... other columns etc
</DataGrid.Columns>
</DataGrid>
</Grid>
ScrollHelper class
namespace CSharpWPF
{
class ScrollHelper : DependencyObject
{
public static bool GetIsSyncScrollEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsSyncScrollEnabledProperty);
}
public static void SetIsSyncScrollEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsSyncScrollEnabledProperty, value);
}
// Using a DependencyProperty as the backing store for IsSyncScrollEnabled. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSyncScrollEnabledProperty =
DependencyProperty.RegisterAttached("IsSyncScrollEnabled", typeof(bool), typeof(ScrollHelper), new PropertyMetadata(false, OnIsSyncScrollEnabledChanged));
private static void OnIsSyncScrollEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer sv = d as ScrollViewer;
if ((bool)e.NewValue)
sv.ScrollChanged += sv_ScrollChanged;
else
sv.ScrollChanged -= sv_ScrollChanged;
}
static void sv_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.HorizontalChange != 0)
{
ScrollViewer sv = sender as ScrollViewer;
SetScrollValue(sv, sv.HorizontalOffset);
}
}
public static double GetScrollValue(DependencyObject obj)
{
return (double)obj.GetValue(ScrollValueProperty);
}
public static void SetScrollValue(DependencyObject obj, double value)
{
obj.SetValue(ScrollValueProperty, value);
}
// Using a DependencyProperty as the backing store for ScrollValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ScrollValueProperty =
DependencyProperty.RegisterAttached("ScrollValue", typeof(double), typeof(ScrollHelper), new PropertyMetadata(0.0, OnScrollValueChange));
private static void OnScrollValueChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer sv = d as ScrollViewer;
if (sv != null)
{
double offset = (double)e.NewValue;
if (sv.HorizontalOffset != offset)
sv.ScrollToHorizontalOffset(offset);
}
}
}
}
result is synchronous scrolling between all of the scrollviewers
Extra
if you do not want scroll bar to appear on all rows you can make use of triggers
eg
<Grid xmlns:l="clr-namespace:CSharpWPF">
<DataGrid x:Name="dGrid">
<DataGrid.Columns>
<DataGridTemplateColumn Width="325"
Header="Header Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
x:Name="scroll"
l:ScrollHelper.IsSyncScrollEnabled="true"
l:ScrollHelper.ScrollValue="{Binding (l:ScrollHelper.ScrollValue),ElementName=dGrid,Mode=TwoWay}">
<ItemsControl ItemsSource="{Binding TrendList}">
<ItemsControl.ItemTemplate>
... your template here
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="scroll"
Property="HorizontalScrollBarVisibility"
Value="auto" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
result is a single scroll bar scrolls all the rows data
EDIT
to get the default scroll position to show the last item in the itemscontrol, change the method sv_ScrollChanged
as follows
static void sv_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = sender as ScrollViewer;
if (e.HorizontalChange != 0)
{
SetScrollValue(sv, sv.HorizontalOffset);
}
if (e.ExtentWidthChange != 0)
{
SetScrollValue(sv, e.ExtentWidth);
}
}
Upvotes: 1
Reputation: 69985
If I understand you correctly, you want to be able to scroll across the Width
of your DataGrid
, but not using the DataGrid ScrollBar
s. As long as there is nothing restricting the Width
of the DataGrid
, then you can do this easily with a ScrollViewer
. Try this:
<ScrollViewer>
<DataGrid ItemsSource="{Binding YourCollectionProperty}" />
</ScrollViewer>
Upvotes: 0