SOLVED: Please see my own answer below with the XAML code, a screenshot and an explanation.
It was a little tough to title this one, so let me explain what my issue is. I have a datagrid that has a defined height, so the scrollbar appears. I would like to contain the vertical scrollbar to the area that excludes the header. While it only scrolls the data rows and not the header, visually it covers the entire datagrid area to the right. The problem with that is that two boxes appear (one above and below) at the scrollbar area. I'm not sure how to get rid of them or how to contain the scrollbar to the body of the datagrid.
The only thing I've been able to figure out (and I don't like the look of it) is to set the Background of the DataGrid to Transparent. Here's the outcome:
As you see, the scrollbar is sticking out annoyingly. Then, there's also the problem of a gap between the horizontal scrollbar and the last row if the background is transparent:
There's also a solution that gets rid of the background color of the datagrid for those two boxes, making them stand out less:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/9fc4252b-38b1-4369-8d76-b6c5ae1e4df5/how-to-remove-the-blank-space-above-the-verticalscroolbar-of-datagrid-in-wpf?forum=wpf
Similar solution can be found here: Annoying Square Where Scrollbars Meet
However, it doesn't get rid of the problem that the scrollbar is awkwardly sticking out on the side.
Something I tested was to separate header from the body and place the body into a vertical ScrollViewer, then place the header and the body into a horizontal ScrollViewer so that they can be both scrolled horizontally. But, as you imagine, this doesn't work out well, because you have to scroll to the right to see the verticall scrollbar. I'm sure there's a way to make it so that it stays frozen on the right, but I haven't figured it out. Another issue is the fact the header width has to match the largest possible width of any cell in that column, or everything will be off. Here's the outcome shown when scrolled all the way to the right:
I'm very new to control templates, so I can't figure out if this could work, because I can't find the right components:
If I give the vertical scrollbar a negative margin to the left (say -6,0,0,0) and a similarly sized padding to the right of the cell block (0,0,6,0), the vertical scrollbar technically should move to the left. I will continue experimenting and try to figure this out, unless someone has the answer for me (which would be awesome).
EDIT #1:
Well, I made some progress and was able to set the margin of the scrollbar to (-17,0,0,0). 17 appears to be the width of the scrollbar. There appears to be a specific key for its width:
http://msdn.microsoft.com/en-us/library/system.windows.systemparameters.verticalscrollbarwidthkey(v=vs.110).aspx
but I can't figured out a way to insert that as the negative offset value in the margin within XAML. It wouldn't be hard to do it in code-behind.... but I'd rather keep it all XAML. Anyways, here's the screenshot of the progress and the XAML code portion:
Here's XAML code portion:
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Command="{x:Static DataGrid.SelectAllCommand}" Focusable="false" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<ScrollContentPresenter Margin="0,0,17,0" x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="1"/>
<ScrollBar Margin="-17,0,0,0" x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
<Grid Grid.Column="1" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The important part that makes the change possible is halfway through that code snippet where I set Margin="-17,0,0,0" for ScrollBar.
My current problem is that I can't figure out which component to offset to the left by 17 (by adding either margin or padding). I've been messing with all of them and no luck thus far. I'll update as soon as I get it, unless someone figures it out before me. As it stands now, scrollbar will overlay anything in that last column until I fix the offset.
EDIT #2:
Please refer to the updated XAML code above. What I did was add Margin="0,0,17,0" to ScrollContentPresenter.
One side effect effect, which I can completely live with, is the fact that offsets the header as well, but you don't see that until you scroll all the way to the right. It effects only the header of the last column, because the ScrollContentPresenter offsets it as well... strangely, since there is a DataGridColumnHeadersPresenter, but it works independently... so, I'll keep on working on it. ScrollContentPresenter, unfortunately, doesn't have padding, which would work like a charm. So, now I have to figure out how I can pad it, rather than setting a margin, or figure out a different method.
A similar method is to set the margin of the horizontal ScrollBar (that's the one in the second grid) to 0,0,-17,0. It'll move it 17 units to the right. Then, set the margin of DataGridColumnHeaderPresenter to 0,0,-17,0. It'll move it 17 units to the right.
EDIT #3:
Here's another method for anyone that's interested:
Set the margin of vertical ScrollBar to 0,-22,0,-17. -22 is the height of my headers, so adjust it to whatever yours are. This method stretches the scrollbar to cover the two white boxes.
EDIT #4:
I figured out the solution. Please see my answer for the XAML code, a screenshot and an explanation.