DAVEWASLIN
DAVEWASLIN

Reputation: 159

Horizontally scroll all rows within column at the same time using one scrollbar

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

Answers (2)

pushpraj
pushpraj

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
  • Added ScrollHelper.IsSyncScrollEnabled & ScrollHelper.ScrollValue to ScrollViewer
  • added a 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

result

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

result

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

Sheridan
Sheridan

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

Related Questions