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>
Related
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.
I can add a ScrollBar style as global. But that will alse change all the ScrollBar except in ScrollViewer.
How can I just change the style of ScrollBar in ScrollViewer. Maybe add a style with a Key and specify it to somewhere like a ControlTemplate.
Here is my fully ScrollBar style:
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="rectangle"
SnapsToDevicePixels="True"
Background="{StaticResource WordBlueBrush}"
Opacity="0.3"
CornerRadius="4"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="rectangle" Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
<Setter Property="Width" Value="14"/>
<Setter Property="Margin" Value="-14 0 0 0" />
<Setter Property="MinWidth" Value="{Binding Height, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="Bg" SnapsToDevicePixels="true">
<Border Padding="0 4">
<Track x:Name="PART_Track"
IsDirectionReversed="true"
IsEnabled="{TemplateBinding IsMouseOver}"
Width="8"
HorizontalAlignment="Center"
>
<Track.DecreaseRepeatButton>
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageUpCommand}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageDownCommand}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb }" />
</Track.Thumb>
</Track>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Finally, I got my solution.
1st, add a key to the ScrollBar style:
<Style x:Key="ViewerScrollBar" TargetType="{x:Type ScrollBar}">
2nd, right click the ScrollViewer in Visual Studio XAML Designer. Chose 'Edit Template'->'Edit a Copy':
Add ControlTemplate for ScrollViewer
3rd, in the 'Create Style Resource' dialog, specify a key for the ScrollViewer style and click 'OK':
Create Style Resource Dialog
4th, Visual Studio will automatically create the ControlTemplate for my ScrollViewer. Now, add Style="{StaticResource ViewerScrollBar}" for the horizontal ScrollBar and verticalScrollBar:
<ControlTemplate x:Key="ScrollViewerControlTemplate" 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 Style="{StaticResource ViewerScrollBar}" 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}"/>
<ScrollBar Style="{StaticResource ViewerScrollBar}" 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>
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?
I am creating a button style. Paste this into a window to see the idea:
<Style x:Key="SelectionButton3"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ClipToBounds="False">
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="TheBorder"
BorderThickness="0,1.5,1.5,1.5"
CornerRadius="3"
Background="SteelBlue"
Height="35"
Grid.Column="1"
Grid.Row="0"
Margin="-31"
BorderBrush="DarkSlateBlue">
<Border.BitmapEffect>
<DropShadowBitmapEffect x:Name="BorderShadow"
ShadowDepth="0"/>
</Border.BitmapEffect>
</Border>
<Rectangle Fill="SteelBlue"
Stroke="DarkSlateBlue"
Grid.Row="0"
Grid.Column="0">
<Rectangle.LayoutTransform>
<RotateTransform Angle="-45" />
</Rectangle.LayoutTransform>
<Rectangle.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="5"/>
</Rectangle.BitmapEffect>
</Rectangle>
<ContentPresenter x:Name="ContentArea"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground"
Value="LightGray" />
<Setter Property="FontFamily"
Value="Segoe UI" />
</Style>
<Button Click="Button_Click"
Style="{StaticResource SelectionButton3}"
Width="185">
</Button>
What I would like to have happen is for the Borders drop shadow to appear when the mouse is over either the Border or the Rectangle. I know I need a trigger, but I'm not really sure where & how to do it.
Can someone point me in the right direction?
Thanks
<Style x:Key="SelectionButton3"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ClipToBounds="False">
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="TheBorder"
BorderThickness="0,1.5,1.5,1.5"
CornerRadius="3"
Background="SteelBlue"
Height="35"
Grid.Column="1"
Grid.Row="0"
Margin="-31"
BorderBrush="DarkSlateBlue">
</Border>
<Rectangle Name="rect" Fill="SteelBlue"
Stroke="DarkSlateBlue"
Grid.Row="0"
Grid.Column="0">
<Rectangle.LayoutTransform>
<RotateTransform Angle="-45" />
</Rectangle.LayoutTransform>
</Rectangle>
<ContentPresenter x:Name="ContentArea"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="TheBorder" Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect ShadowDepth="0" />
</Setter.Value>
</Setter>
<Setter TargetName="rect" Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect ShadowDepth="5"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground"
Value="LightGray" />
<Setter Property="FontFamily"
Value="Segoe UI" />
</Style>
Note that unless you're using .NET 3.0 or 3.5 pre SP1, you should use Effect instead of BitmapEffect. The BitmapEffect property has been deprecated in 3.5 SP1
I have this control template that I am writing:
<Style TargetType="{x:Type controls:InfoBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:InfoBar}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="{TemplateBinding FontFamily}" />
<Setter Property="FontSize" Value="{TemplateBinding FontSize}" />
<Setter Property="Foreground" Value="{TemplateBinding Foreground}" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{TemplateBinding LeftInfoBarTextBlockCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl Grid.Column="1" ItemsSource="{TemplateBinding MiddleInfoBarTextBlockCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl Grid.Column="2" ItemsSource="{TemplateBinding RightInfoBarTextBlockCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This section of xaml is throwing member is not valid because it does not contain a valid type name. exceptions for the template bindings on FontFamily, FontSize and Foreground.
<Grid.Resources>
If I change it to this:
<Grid.Resources>
It will build, but when I debug it, I get this XmlParseExeption:
Set property 'System.Windows.Setter.Value' threw an exception.
If I change controls:InfoBar to Control, which InfoBar inherits from, I get the same exception.
What am I doing wrong?
The problem is in this:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="{TemplateBinding FontFamily}" />
<Setter Property="FontSize" Value="{TemplateBinding FontSize}" />
<Setter Property="Foreground" Value="{TemplateBinding Foreground}" />
</Style>
You can use TemplateBinding only inside a control template.
and here you using it inside a style.
To answer your second question, Justin, you can create a style that applies a control template.
Notice that this style assigns a control template to the the Template property of a pushpin when the pushpin style is set to NumberPushpinStyle.
<Style x:Key="NumberPushpinStyle" TargetType="m:Pushpin">
<Setter Property="BorderBrush" Value="#FFF4F4F5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Fill="Black" Height="33" Stroke="White" StrokeThickness="2" Width="33" RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<CompositeTransform TranslateX="-16" TranslateY="16" />
</Ellipse.RenderTransform>
</Ellipse>
<TextBlock Foreground="White" Text="{TemplateBinding Content}" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0">
<TextBlock.RenderTransform>
<CompositeTransform TranslateX="-16" TranslateY="15" />
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>