SmallChange in a XAML ScrollViewer ControlTemplate has no effect - wpf

I've created a basic Windows Desktop WPF Application. In the MainWindow, I've added the following as the body of the window:
<ScrollViewer Template="{DynamicResource ScrollViewerControlTemplate1}">
<ScrollViewer.Resources>
<ControlTemplate x:Key="ScrollViewerControlTemplate1" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" SmallChange="40000"/>
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
</Grid>
</ControlTemplate>
</ScrollViewer.Resources>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="SteelBlue"/>
<Border Grid.Row="1" Background="Peru"/>
<Border Grid.Row="2" Background="Goldenrod"/>
<Border Grid.Row="3" Background="Tomato"/>
<Border Grid.Row="4" Background="IndianRed"/>
</Grid>
</ScrollViewer>
You'll notice that on PART_VerticalScrollbar, I've set the SmallChange="40000" (an arbitrarily large number). Yet when I click the up/down arrows on the scrollbar, it does the same very small change as it did before I set the SmallChange to anything.
I've read over the documentation a number of times, and can't figure out why this isn't having any effect on the amount that the ScrollViewer scrolls. Any ideas?
Note that I could change the ScrollBar template and change the command these button calls to Scrollbar.PageUpCommand rather than Scrollbar.LineUpCommand, but ultimately I'd like to have finer control over the scrolling than a full page.

The reason for this is a special logic implemented in the ScrollViewer and in the ScrollBar.
The ScrollBar.SmallChange property will be only considered for scrolling if the scroll bar is stand-alone. That means, when it is outside of a ScrollViewer.
If you look at the ScrollViewer.OnApplyTemplate method, you will notice the following:
public override void OnApplyTemplate()
{
// ...
ScrollBar scrollBar = GetTemplateChild(HorizontalScrollBarTemplateName) as ScrollBar;
if (scrollBar != null)
scrollBar.IsStandalone = false;
// Same for the vertical scroll bar
// ...
}
If a ScrollViewer finds scroll bars inside of it, it sets their IsStandalone (internal) properties to false, which disables the scroll command handing of the scroll bars. Instead, the ScrollViewer takes over the scroll command handing, but it ignores some ScrollBar's properties, including the SmallChange property.
TL;DR This won't work for the scroll bars inside of a ScrollViewer. You can change your template so use two "external" scroll bars. But then, you will need to connect those scroll bars with the ScrollViewer manually.

I suspect that your custom control template isn't being used because of the way you are referencing it. I would change the XAML to this:
<ScrollViewer>
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid ...>
...
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
... ScrollViewer content goes here
</ScrollViewer>
Basically, set the control's template directly, rather than using a dynamic resource.

Related

Issue with WPF WrapPanel inside a ScrollViewer

There are a number of similar questions on SO but so far I have not been able to resolve my problem using them.
I have a bunch of Controls inside a WrapPanel and the WrapPanel is inside a ScrollViewer. The ScrollViewer is inside a Grid.
I am trying to get all the <Border> controls in the WrapPanel to have an Orientation of 'Vertical' (so that they flow down and when there is no more space left vertically they wrap horizontally) with a HorizontalScrollBar that appears when there is no more space left Horizontally.
My code so far is as follows:
<Grid x:Name="configGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" Width="{Binding ElementName=configGrid, Path=ActualWidth}" Height="{Binding ElementName=configGrid, Path=ActualHeight}">
<WrapPanel HorizontalAlignment="Left" Orientation="Vertical" x:Name="ConfigWrapPanel" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ActualWidth}">
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="General">
// some controls here
</Expander>
</Border>
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="Another Block">
// some controls here
</Expander>
</Border>
// many more <border> blocks here....
</WrapPanel>
</ScrollViewer>
</Grid>
This almost works as expected, the various content flows vertically and when there is not enough room at the bottom it moves up and right and starts at the top again. But I never get any horizontal scrollbars and the controls just disappear off the right of the screen.
I'm sure this is something really simple I'm missing but I can't quite figure it out.
As a bit of further info, the various Border controls and sub elements are all of dynamic width and height (which is why I opted for a vertical orientation WrapPanel rather than Horizontal)
Any help would be greatly appreciated.
You have to remove the Width from your WrapPanel.
That width wants to stretch to infinity, which prevents the ScrollViewer from corrent measuring the boundaries of the WrapPanel resulting in never showing the ScrollBar.
Code below shows a working example:
<Grid x:Name="configGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" Height="{Binding ElementName=configGrid, Path=ActualHeight}">
<WrapPanel HorizontalAlignment="Left" Orientation="Vertical" x:Name="ConfigWrapPanel" >
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="General">
// some controls here
</Expander>
</Border>
<Border BorderBrush="White" BorderThickness="1,1,1,1" CornerRadius="2" Margin="10">
<Expander IsExpanded="True" BorderThickness="0" Header="Another Block">
// some controls here
</Expander>
</Border>
</WrapPanel>
</ScrollViewer>
</Grid>

Changing viewport of datagrid

I need to change viewport of datagrid to maximum so all rows are propagated in constructor. So I used Scrollview with row height set to auto.
<ScrollViewer>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<DataGrid>
</DataGrid>
</Grid>
</ScrollViewer>
This propagates all controls but now when I use scrollbar it also moves header row. I need that the scrollbar will effect only datagrid.
I dont understand this statement
... need to change viewport of datagrid to maximum so all rows are
propagated in constructor. So I used Scrollview with row height set to
auto...
... where is the Viewport setting in the code? Also what is this constructor? Your terminologies are confusing....
However I can assume what you want is the datagrid to fully render its rows (without extra space below rows) and occupy that entriely over the grid panel. But because you have used a scroll viewer abone grid, it will scroll datagrid headers as well.
If you use "Snoop" you will find that wpf datagrid has a scroll viewer as its visual child and it has a Grid panel in its own template, there the ScrollContentPresenter is under a Grid.Row which has RowDefinition that has Height as asterick (*).
Using visual children extraction method given below, accesss that Grid descendent and change its height to auto.
(GetVisualChild<Grid>(myDataGrid)).RowDefintions[1].Height="Auto"
Method to snoop through all children of a parent...
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null) child = GetVisualChild<T>(v);
if (child != null) break;
}
return child;
}
The XAML way
Override the dataGrid Control template...
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Padding="{TemplateBinding Padding}">
<ScrollViewer Focusable="false"
Name="DG_ScrollViewer">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <!--This changed to Auto from '*'-->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!--Left Column Header Corner -->
<Button Command="{x:Static dg:DataGrid.SelectAllCommand}"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=CellsPanelHorizontalOffset}"
Template="{StaticResource SelectAllButtonTemplate}"
Focusable="false"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=HeadersVisibility, Converter={x:Static dg:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static dg:DataGridHeadersVisibility.All}}" />
<!--Column Headers-->
<dgp:DataGridColumnHeadersPresenter Grid.Column="1"
Name="PART_ColumnHeadersPresenter"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=HeadersVisibility, Converter={x:Static dg:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static dg:DataGridHeadersVisibility.Column}}"/>
<!--DataGrid content-->
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />
<ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar"
Orientation="Vertical"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar Grid.Column="1"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

want to make scrollable tabs for a tabcontrol

Say I have a tab control, and I have over 50 tabs, where there is no enough space to hold so many tabs, how make these tabs scrollable?
Rick's answer actually breaks the vertical stretching of content inside the tabcontrol. It can be improved to retain vertical stretching by using a two row grid instead of a StackPanel.
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden" >
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent" Grid.Row="1"/>
</Grid>
</ControlTemplate>
</TabControl.Template>
Override the TabControl ControlTemplate and add a ScrollViewer around the TabPanel like this sample:
<Grid>
<TabControl>
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"/>
</StackPanel>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="TabItem1">TabItem1 Content</TabItem>
<TabItem Header="TabItem2">TabItem2 Content</TabItem>
<TabItem Header="TabItem3">TabItem3 Content</TabItem>
<TabItem Header="TabItem4">TabItem4 Content</TabItem>
<TabItem Header="TabItem5">TabItem5 Content</TabItem>
<TabItem Header="TabItem6">TabItem6 Content</TabItem>
<TabItem Header="TabItem7">TabItem7 Content</TabItem>
<TabItem Header="TabItem8">TabItem8 Content</TabItem>
<TabItem Header="TabItem9">TabItem9 Content</TabItem>
<TabItem Header="TabItem10">TabItem10 Content</TabItem>
</TabControl>
</Grid>
which gives this result:
Recently I've implemented such control. It contains two buttons (to scroll left and right) which switch their IsEnabled and Visibility states when it is necessary. Also it works perfectly with item selection: if you select a half-visible item, it will scroll to display it fully.
It looks so:
It isn't so much different from the default control, the scrolling is appeared automatically:
<tab:ScrollableTabControl ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
IsAddItemEnabled="False"
.../>
I've written the article about this ScrollableTabControl class in my blog here.
Source code you can find here: WpfScrollableTabControl.zip
The above solution is great for tab items with the tab control's "TabStripPlacement" property set to "Top". But if you are looking to have your tab items, say to the left side, then you will need to change a few things.
Here is a sample of how to get the scrollviewer to work with the TabStripPlacement to the Left:
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ScrollViewer
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
FlowDirection="RightToLeft">
<TabPanel
x:Name="HeaderPanel"
Panel.ZIndex ="0"
KeyboardNavigation.TabIndex="1"
IsItemsHost="true"
/>
</ScrollViewer>
<ContentPresenter
x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
ContentSource="SelectedContent" Grid.Column="1"
/>
</Grid>
</ControlTemplate>
Note that in the ScrollViewer I set FlowDirection="RightToLeft" so that the scroll bar would snap to the left of the tab items. If you are placing your tab items to the right the you will need to remove the FlowDirection property so that it defaults to the right side.
And here is the result:
For thoose who want to know how to make the scrollviewer scroll to the selected tab item.
Add this event SelectionChanged="TabControl_SelectionChanged" to your TabControl.
Then give a name like TabControlScroller to the ScrollViewer inside the template. You should end with something like this
<TabControl SelectionChanged="TabControl_SelectionChanged">
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ScrollViewer x:Name="TabControlScroller" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" >
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent" Grid.Row="1"/>
</Grid>
</ControlTemplate>
</TabControl.Template>
<!-- Your Tabitems-->
</TabControl>
Then in code behind you just have to add this method :
private void TabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
TabControl tabControl = (TabControl)sender;
ScrollViewer scroller = (ScrollViewer)tabControl.Template.FindName("TabControlScroller", tabControl);
if (scroller != null)
{
double index = (double)(tabControl.SelectedIndex );
double offset = index * (scroller.ScrollableWidth / (double)(tabControl.Items.Count));
scroller.ScrollToHorizontalOffset(offset);
}
}
Place it inside a ScrollViewer.
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
<TabControl ...>
...
</TabControl>
</ScrollViewer>

what the name for the item in the green circle

what's the element name in the green circle? Whta's the event corresponding to click this element?
Interestingly, this part of the DataGrid is no-man's-land - it doesn't even have an official name, let alone a behaviour or a template. In order to handle events related to this area, see here or here.
I call it the 'select all' button. However, it doesn't have an official name. It also does not have any style properties exposed. For a simple method to style it via attached properties, see the following:
http://www.scottlogic.co.uk/blog/colin/2009/02/styling-hard-to-reach-elements-in-control-templates-with-attached-behaviours/
Colin E.
As others have stated, it's the "Select All" Button. Clicking it will select everything in the DataGrid if SelectionMode is set to Extended.
The "Select All" Button is part of the DataGrid Template. Or actually, it's part of the ScrollViewer Template that is part of the DataGrid Template so it's nested pretty deep. The ControlTemplate looks like this. From here you can do anything you want with the SelectAll Button, disable it, change the Background etc.
<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>
<!-- Here it is -->
<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 x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="1"/>
<ScrollBar 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>
If you want to disable the selectall button you can also do like this:
//Disable select all button
datagrid.CommandBindings.Add(new CommandBinding(DataGrid.SelectAllCommand, delegate { }, (s, a) => { if (a.OriginalSource is Button) ((Button)a.OriginalSource).IsEnabled = false; }));
as a workaround.
This can also be used to wire other things up.
The "problem" with re-templating is that you have to take all the themes into account... tedious :)
In at least one example I found, clicking the gray triangle in the corner causes all rows to be selected.
But this is not a built-in feature of this control as far as I know. It is part of a custom template.
See this example:
http://www.dotnetspark.com/kb/2941-styling-wpf-datagrid.aspx
If you download the sample, and search the Resources/DataGridCustomization.xaml file for "SelectAll", you'll find the part of the template that contains this feature.

Disabling text selection in DocumentViewer

Simple question. How do you disable the text selection of DocumentViewer in WPF? This is the feature where an XPS document is displayed by the viewer and then text can be highlighted via mouse. The highlighted text can also be copied but I have already disabled this. I just don't know how to disable the highlighting.
Thanks!
you may use IsFocusable=false. But search box will be disabled too...
We have solved this by overriding the ControlTemplate of the ScrollViewer embedded in the DocumentViewer control. Insert the Style below in "Window.Resources":
<Style TargetType="{x:Type ScrollViewer}" x:Key="CustomScrollPresenter">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid Background="{TemplateBinding Panel.Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle Grid.Column="1" Grid.Row="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
<ScrollContentPresenter
PreviewMouseLeftButtonDown="ScrollContentPresenter_PreviewMouseLeftButtonDown"
Grid.Column="0"
Grid.Row="0"
Margin="{TemplateBinding Control.Padding}"
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}" />
<ScrollBar
x:Name="PART_VerticalScrollBar"
Grid.Column="1"
Grid.Row="0"
Minimum="0"
Maximum="{TemplateBinding ScrollViewer.ScrollableHeight}"
ViewportSize="{TemplateBinding ScrollViewer.ViewportHeight}"
Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Visibility="{TemplateBinding ScrollViewer.ComputedVerticalScrollBarVisibility}"
Cursor="Arrow" AutomationProperties.AutomationId="VerticalScrollBar" />
<ScrollBar
x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal" Grid.Column="0" Grid.Row="1" Minimum="0"
Maximum="{TemplateBinding ScrollViewer.ScrollableWidth}" ViewportSize="{TemplateBinding ScrollViewer.ViewportWidth}" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" Visibility="{TemplateBinding ScrollViewer.ComputedHorizontalScrollBarVisibility}" Cursor="Arrow" AutomationProperties.AutomationId="HorizontalScrollBar" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then override the Style of ScrollViewer with it in the ControlTemplate for DocumentViewer:
<Style
x:Key="MyDVStyleExtend"
BasedOn="{StaticResource {x:Type DocumentViewer}}"
TargetType="{x:Type DocumentViewer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DocumentViewer">
<Border BorderThickness="2,2,2,2"
BorderBrush="SlateBlue" Focusable="False">
<Grid Background="{StaticResource GridBackground}"
KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width ="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Style="{StaticResource CustomScrollPresenter}" Grid.Column ="0"
CanContentScroll="True"
HorizontalScrollBarVisibility="Auto"
x:Name="PART_ContentHost"
IsTabStop="True"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then create a function for the "PreviewMouseLeftButtonDown="ScrollContentPresenter_PreviewMouseLeftButtonDown"" attribute stated in the CustomScrollPresenter style.
private void ScrollContentPresenter_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
Another way would be to add e.g. a dockpanel:
<DockPanel Name="pnlTouchTaker"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Background="Transparent">
</DockPanel>
lying "above" the documentviewer and set it´s width and height to the actual width and height of the scrollviewer content in e.g. page loading event.
You might have to add additional logic if using zoom options and the horizontal toolbar becomes visible.
Implement the following code in xaml.cs part (DocumentViewerInstance x:Name of your DocumentViewer in your xaml.)
DocumentViewerInstance.GetType().GetProperty("IsSelectionEnabled", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(DocumentViewerInstance, false, null);
You can use the IsFocusable=false or IsHitTestVisible = false or handle any preview event (for example in accepted answer) for disable selection but the hyperlinks won't work! If you set IsSelectionEnabled = false, the selection will be disabled but hyperlinks will work too. (Warning! IsSelectionEnabled can be changed for true value after you set false, so you should check the value often.)

Resources