Change the width of the scrollbars in a scrollviewer on MousOver - wpf

I try to style a ScrollViewer so that the width of the ScrollBar element is changed on a MouseOver. I can style the ScrollViewer and give it an initial width, and it is also possible to change properties on a MouseOver, like the Opacity or Visibility. But I can't change the Width, that property is not changed.
I use this code as an example:
<Style x:Key="myScrollBarStyle" TargetType="{x:Type ScrollBar}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.9" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Opacity" Value="0.4" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="myStyle" TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.ColumnSpan="2" Grid.RowSpan="2"/>
<ScrollBar
Style="{StaticResource myScrollBarStyle}"
Name="PART_VerticalScrollBar"
Grid.Column="1"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
<ScrollBar
Style="{StaticResource myScrollBarStyle}"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Opacity is changed on a MouseOver but if I add a Width, it is ignored. If I look at ControlTemplate.Triggers then I can change the width on MouseOver but that is the width of the complete ScrollViewer, not the ScrollBar element. Even if I point to the element with the targetname PART_VerticalScrollBar.
Searching the internet only results in solutions to change the visibility or opacity, not the width. I could not find any solution.
Is there a solution to do this in a style?

Related

How to reduce size of scrollbar?

I tried this without luck.
<ListBox ItemsSource="{Binding Path=Items}" ScrollViewer.CanContentScroll="False">
<ListBox.Resources>
<Style TargetType="ScrollBar">
<Setter Property="Width" Value="4"/>
<Setter Property="Template" Value="{DynamicResource MyScrollBar}"/>
</Style>
</ListBox.Resources>
</ListBox>
Scroll bar template:
<ControlTemplate x:Key="MyScrollBar" TargetType="{x:Type ScrollBar}">
<Track x:Name="PART_Track" Width="4" IsDirectionReversed="True" IsEnabled="{TemplateBinding IsMouseOver}">
<Track.Thumb>
<Thumb>
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid>
<Border x:Name="thumb" BorderThickness="0" Background="Gray" Height="{TemplateBinding Height}" Width="4"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
</Track.Thumb>
</Track>
</ControlTemplate>
The thumb has changed its width. But ListBox still leaves much more space for ScrollBar than I want. How can I decrease the space for ScrollBar?
The ListBox uses a ScrollViewer internally in its control template. The default ScrollViewer template will use the scroll bar width defined in the SystemParameters. A simple way to adapt it is to override the key in the ListBox resources. Please note that there are different keys for the horizontal an vertical scroll bars. You might want to adapt both in the long run.
<ListBox ItemsSource="{Binding Path=Items}" ScrollViewer.CanContentScroll="False">
<ListBox.Resources>
<system:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">4</system:Double>
<Style TargetType="ScrollBar">
<Setter Property="Width" Value="4"/>
<Setter Property="Template" Value="{DynamicResource MyScrollBar}"/>
</Style>
</ListBox.Resources>
</ListBox>
Another way is to create a custom control template for the ScrollViewer. You can refer to the official styles and template documentation for ScrollViewer to get an idea how to build this template. There is already an example, where you can adapt the scroll bar width.
<Style x:Key="MyScrollViewer"
TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="1"
BorderThickness="0,1,1,1">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<ScrollContentPresenter CanContentScroll="{TemplateBinding CanContentScroll}" />
</Border>
<ScrollBar x:Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Width="4"/>
<ScrollBar x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can either make this style implicit by omitting the x:Key, so it will be applied to all ScrollViewers in scope or you can create a custom ListBox control template, where you sepcify this template for the internal ScrollViewer in order to apply it only there.

DataGridRow border on top of DataGrid border

I'm trying to display DataGridRow border on top of DataGrid border. Is that even possible with a style or I have to use a custom adorner?
I want to achieve something like that:
I tried using Panel.ZIndex and ClipToBounds="False" but it doesn't seem to work in this case.
<Style x:Key="MyDataGridRowStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate>
<TextBlock
Margin="2,0,0,0"
VerticalAlignment="Center"
Foreground="Red"
Text="!" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRow}">
<Border
x:Name="DGR_Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ClipToBounds="False"
SnapsToDevicePixels="True">
<SelectiveScrollingGrid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGridCellsPresenter
Grid.Column="1"
ItemsPanel="{TemplateBinding ItemsPanel}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<DataGridDetailsPresenter
Grid.Row="1"
Grid.Column="1"
SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=AreRowDetailsFrozen, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}}"
Visibility="{TemplateBinding DetailsVisibility}" />
<DataGridRowHeader
Grid.RowSpan="2"
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Row}}" />
</SelectiveScrollingGrid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsNewItem" Value="True">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NewItemMargin}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="Margin" Value="-1,-1,-1,-1" />
<Setter Property="Panel.ZIndex" Value="123" />
</Trigger>
</Style.Triggers>
</Style>
Because of the stiffness of WPF it's not possible to use Panel.ZIndex that way as it can be used only on the same level. Few solutions to this problem:
Custom adorner that draws selection rectangle for selected rows (must use adorner layer above/on the same level of DataGrid border) custom template can be used to add AdornerDecorator just above the border.
Add Canvas and <Rectangle x:Name="PART_SelectionRectangle" /> to DataGrid template, subclass DataGrid and change position of the Rectangle in Canvas based on the current selection in your subclassed DataGrid.
I'm not sure if there's any way to make this with style alone without subclassing DataGrid/adding event handlers to it, maybe with a custom converter that calculates position and size of selection rectangle and binding Rectangle properties using that converter.

Globally changing width of scroll bars in my application

I have a WPF application that runs on a touch screen computer. I'd like to change all of the scroll bars in the app to be much wider. Is there a way to do that globally?
Yo have to override the default template of scrollViewer to increase the width of vertical scrollbar. To apply the template across all your scrollbars put the override style in your App resources -
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="1"/>
<ScrollBar Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Width="40"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<ScrollBar Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can set width of 'PART_VerticalScrollBar' to your desired width (say 40 as in example above).Placing this style under Application Resources (App.xaml) makes it applied across complete application.
You need to create an Style inside of the Resources indicating the TargetType.
This style will be applied to all ScrollBars in your xaml file.
<Window.Resources>
<Style TargetType="{x:Type ScrollBar}">
....
</Style>
I know this is old but I applied Kent's answer from this post-https://stackoverflow.com/questions/1321247/how-to-increase-scrollbar-width-in-wpf-scrollviewer but put it in a resource dictionary.
<ResourceDictionary xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">10</sys:Double>
<sys:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">10</sys:Double>
//a bunch of styles
</ResourceDictionary>

Style TreeView but keep it Virtualized

The Virtualization on my TreeView works when I have only styled the TreeViewItem with this bit of xaml in the style:
<Style.Triggers>
<Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
But then if I try and give the TreeView itself a style with Style={Resource} the virtualization breaks, i.e:
<Style x:Key="FontTreeViewStyle" TargetType="{x:Type TreeView}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeView">
<Border Name="Border" CornerRadius="1" BorderThickness="1" BorderBrush="{DynamicResource BorderMediumColor}" Background="{DynamicResource ControlLightColor}">
<ScrollViewer Focusable="False" CanContentScroll="False" Padding="4"><!-- Style="{StaticResource MyScrollViewer}"-->
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks in advance!
By specifying CanContentScroll="False" on the ScrollViewer you will always stop it virtualizing
Take a look at why setting ScrollViewer.CanContentScroll to false disable virtualization
If you still have problems double check the visual tree that's created in something like Snoop or WPF Inspector. For virtualization to work the IScrollInfo object (ie the panel) must be the direct child of the ScrollViewer.
http://msdn.microsoft.com/en-us/library/ms750665.aspx
Hope that helps,
Mark
MarkDaniel alredy answered (thanks!) but here is a full example style:
<Style x:Key="ScrollViewerStyle" TargetType="{x:Type ScrollViewer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter CanContentScroll="True" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.VirtualizationMode="Recycling"/>
<ScrollBar Name="PART_VerticalScrollBar"
Grid.Column="1"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Style="{DynamicResource ScrollBarStyle}"/>
<ScrollBar Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Style="{DynamicResource ScrollBarStyle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

WPF Binding Issues

I'm trying to customize the TreeView control. When a user selects an item in the TreeView, I need the ActualWidth of the SelectedItem to be stored in the item's Tag:
<Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
<!-- ... -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle x:Name="rect" />
<ContentPresenter x:Name="PART_Header" ContentSource="Header" Margin="5" />
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1" />
<!-- ... -->
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Tag" Value="{Binding ElementName=rect, Path=ActualWidth}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Later, I listen to the SelectedItemChanged event of the TreeView:
private void views_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeViewItem item = (TreeViewItem)e.NewValue;
double i = (double)item.Tag;
}
Now the problem is that item.Tag is always null. Is this a problem with my binding? Or should things be done in a different way?
Try that :
<Setter Property="Tag" Value="{Binding Path=ActualWidth, RelativeSource={x:Static RelativeSource.Self}}" />

Resources