Silverlight: Difficulty with ScrollViewer - silverlight

I'm making a wp7 Silverlight app. I have a ScrollViewer that contains a ListBox of ten elements. However, it only lets me scroll down a tiny bit. What could I be doing wrong?
<ScrollViewer>
<ListBox x:Name="StoryListBox"/>
</ScrollViewer>
The ListBox is filled with items of the following type:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="Thumbnail" Grid.Column="0" Width="89" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock x:Name="Headline" Grid.Column="1" Grid.Row="0" Style="{StaticResource PhoneTextAccentStyle}" TextWrapping="Wrap" HorizontalAlignment="Left" Width="299" FontSize="23.333" VerticalAlignment="Top" />
<TextBlock x:Name="Teaser" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Left" Style="{StaticResource PhoneTextSubtleStyle}" TextWrapping="Wrap" VerticalAlignment="Top" Width="384"/>
</Grid>
What could I be doing wrong here?

I do not see the reason why you would need the ListBox to be embedded within the ScrollViewer. If your ListBox has more items than it display in the viewable area, the ListBox items should scroll by themselves. If you edit the ListBox's style using Expression Blend, the style will look like this -
<Style x:Key="ListBoxListStyle" TargetType="ListBox">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer
x:Name="ScrollViewer"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}" >
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can see the ScrollViewer embedded in the ControlTemplate of the ListBox, it takes care of the scrolling of ListBoxItems.

It(Scrolling) is working perfectly after I set the Height of the list box.

The ScrollViewer could ONLY work when it is smaller than which it contains (either width or height). If you do not indict the width or height of the ScrollViewer, it will be stretched to fit its content, and as a result the effect would not be perfect.
So what you need to do is to set a fixed size for your ScrollViewer.
By the way, as indyfromoz said, a ListBox already has a ScrollViewer, so you could remove your outer ScrollViewer, and just set the ListBox's size.

Related

WPF: Negative margin gets clipped in ScrollViewer

I have a TabControl which is styled and templated in a way that the tab header and the tab content are separated by a line, but the current selected tab breaks this line (see picture 1).
This works like a charm, except for two things.
First problem: In certain zoom states (unfortunately in default state 100% as well) there is a very thin line separating the tab header from the content (see picture 2; the height of the separation line is 2px).
Where does it come from and how can I get rid of it?
Since the current selected tab might get a left, top and right border later on I can't just increase the negative margin, because the left and right border would be visible in the content.
Second problem: If I put the tab header inside a ScrollViewer to be able to horizontally scroll through the tabs (in case there are too many), the negative Margin gets clipped and the separation line is shown.
Of course the ScrollViewer is styled and templated in a way to not use the horizontal scrollbar below the content.
How can I use negative Margin inside a ScrollViewer?
I commented out the ScrollViewer in my code below. Please remove the comment indicators and please remove the Margin on the TabPanel inside the ScrollViewer to see the problem in action.
Here's my styles and templates and code, in case someone needs it:
<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Background" Value="LightGoldenrodYellow" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid SnapsToDevicePixels="True"
Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="Content"
Grid.Row="1" Grid.Column="0"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0,2,0,0"
Background="White">
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<!--<ScrollViewer Grid.Row="0" Grid.Column="0" Margin="5,5,5,0">-->
<TabPanel x:Name="PART_ScrollContentPresenter"
Grid.Row="0" Grid.Column="0"
Margin="5,5,5,0"
IsItemsHost="True" />
<!--</ScrollViewer>-->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="LightGray" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="BorderThickness" Value="1,1,1,0" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="MinWidth" Value="50" />
<Setter Property="Margin" Value="0,0,5,0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Margin}">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding Background}">
<TextBlock x:Name="TabTitle"
Margin="16,6"
Text="{TemplateBinding Header}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextWrapping="NoWrap"
TextTrimming="CharacterEllipsis" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="White" TargetName="Border" />
<Setter Property="BorderBrush" Value="Gray" TargetName="Border" />
<Setter Property="Margin" Value="0,0,2,-2" />
<Setter Property="Padding" Value="0,-2,0,0" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Gray" TargetName="Border" />
<Setter Property="BorderBrush" Value="Gray" TargetName="Border" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="MainWindow"
Height="350" Width="525">
<Window.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="1" />
</Window.LayoutTransform>
<TabControl Style="{StaticResource TabControlStyle}"
ItemContainerStyle="{StaticResource TabItemStyle}">
<TabItem Header="Tab 1" />
<TabItem Header="Tab Two" />
<TabItem Header="Tab III" />
</TabControl>
</Window>
Thank you for your time and help.
Regarding your first problem, you can increase your negative margins by separating the border from the content:
<Grid SnapsToDevicePixels="True" Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--SeparatorLine will be rendered on bottom-->
<Border x:Name="SeparatorLine"
Grid.Row="1" Grid.Column="0"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0,2,0,0">
</Border>
<!--Tabs will be rendered on top of the SeparatorLine-->
<TabPanel x:Name="PART_ScrollContentPresenter"
Grid.Row="0" Grid.Column="0"
Margin="5,5,5,0"
IsItemsHost="True" />
<!--Content will be rendered on top. Content.Margin should equal the SeparatorLine.BorderThickness-->
<Border x:Name="Content"
Grid.Row="1" Grid.Column="0"
Margin="0,2,0,0"
Background="White">
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</Grid>
Now you are free to use negative margin in TabItem, that are bigger than the content border size.
The second problem can also be solved with negative margins... as far as I know, you won't get the default Scrollviewer to allow any content overflow, no matter how bad you want it. See WPF clipping even when no clipping is desired - how to turn it off? for some discussion on the topic.
What you can do is increase the scrollviewers size with a negative margin, so the actual items will stay inside the supported area:
<ScrollViewer Grid.Row="0" Grid.Column="0" Margin="5,5,5,-3" VerticalScrollBarVisibility="Disabled">
<TabPanel x:Name="PART_ScrollContentPresenter"
Grid.Row="0" Grid.Column="0"
Margin="0,0,0,3"
IsItemsHost="True" />
</ScrollViewer>
This example would allow negative bottom margins on TabItem up to 3 px.

Change the color of a Border element in ListBox when item is selected

I have the following ItemTemplate defined for a ListBox.
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0 4 0 4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="grp1" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" SharedSizeGroup="grp2" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="0 0 0 1" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3">
<TextBlock Text="..." FontSize="16" />
</Border>
<TextBlock Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" Text="..." />
<StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal">
<TextBlock Grid.Column="0" Grid.Row="2" Text="..." />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
The thing is, when the item is selected, all the TextBlock's foreground colors change, but the Border's color remains black:
Unselected:
Selected:
How can I change the color of the Border to match the color of the TextBlocks?
The item selection colors are defined in the ControlTemplate for ListBoxItem. This is the container into which your ItemTemplate is injected so the colors are already set by the time your DataTemplate is being rendered. To override it you need to set the ListBox.ItemContainerStyle to a new Style with a ControlTemplate that does what you want. A simple example is below. To keep it as close as possible to what you're seeing now, use the option in Blend to "Edit a Copy" of the current template that is being rendered under your current theme. You can then edit just the parts you want and keep the rest of the template the same.
<Style x:Key="ListBoxItemBasicStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<!-- Change IsSelected SelectedBackgroundBrush to set the selection color for the items -->
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" Value="{DynamicResource SelectedBackgroundBrush}" TargetName="Border"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

ItemsControl is preventing item content from expanding to fill its container

I am creating a WPF custom control that inherits from ItemsControl. In the ControlTemplate of my control I have an ItemsPresenter. The problem is that I need to be able to get content in the items presenter to fill the entire content area. In the code below I have a simplified example of the broken code and also what I am trying to accomplish. BTW setting HorizontalAlignment and VerticalAlignment to Stretch causes the content expand horizontally but not vertically.
<Window x:Class="yada"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="768" Width="1024">
<Grid>
<Grid.Resources>
<Style TargetType="Border" x:Key="stepBorder">
<Setter Property="Background" Value="CadetBlue"></Setter>
<Setter Property="BorderBrush" Value="Green" ></Setter>
<Setter Property="BorderThickness" Value="2"></Setter>
<Setter Property="Padding" Value="25"></Setter>
</Style>
</Grid.Resources>
<TabControl Margin="0,20,0,0">
<TabItem Header="Broken">
<Border>
<ItemsControl>
<ItemsControl.Items>
<Control>
<Control.Template>
<ControlTemplate>
<Border Style="{StaticResource stepBorder}">
<TextBlock Text="Border fills small region near the top"></TextBlock>
</Border>
</ControlTemplate>
</Control.Template>
</Control>
</ItemsControl.Items>
</ItemsControl>
</Border>
</TabItem>
<TabItem Header="Works">
<Border>
<Control>
<Control.Template>
<ControlTemplate>
<Border Style="{StaticResource stepBorder}" >
<TextBlock Text="Border fills entire control"></TextBlock>
</Border>
</ControlTemplate>
</Control.Template>
</Control>
</Border>
</TabItem>
</TabControl>
</Grid>
</Window>
Here is some code from my control. I cant include it all, there is too much. I think this is the relevant part however:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Wizard}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" x:Name="ContentRow" />
<RowDefinition Height="{Binding ElementName= NavButtonPanel,Path=ActualHeight}"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" x:Name="ContentScrollViewer" MaxHeight="{Binding ElementName=ContentRowHeight, Path=ActualHeight}">
<ItemsPresenter
x:Name="Presenter"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
</ItemsPresenter>
</ScrollViewer>
<Border Style="{TemplateBinding NavButtonPanelStyle}" Grid.Row="1" x:Name="NavButtonPanel">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
</StackPanel>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate></DataTemplate>
</Setter.Value>
</Setter>
As usual, just posting on SO gets me halfway to my answer.
It turns out the ItemsControl is using an evil stackpanel as a container for items. Fortunately there is a top-secret property called ItemsPanel that lets you change this. Just set it to a grid as shown below and you are in business.
In my case I am writing a custom control that inherits from ItemsControl so in the Style I add this property setter:
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Grid></Grid>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>

Button after ListBox

I need to show ListBox with dynamic content (so I do not know its height) and Button below it. So user should be able to scroll ListBox to end and see button.
In Android I would use RelativeLayout and below property for button or some other solution, but in WP I do not know how to do it.
What I had tryed:
1) Put all in StackPanel
<StackPanel>
<ListBox />
<Button />
</StackPanel>
This is not working, because StackPanel is blocking ListBox scrolling.
2) Ok, lets put ListBox in Grid
<StackPanel>
<Grid>
<ListBox />
</Grid>
<Button />
</StackPanel>
Nothing happens.
3) Put all in Grid and use Grid.Row not worked.
4) Lets put all in Grid and set margin for Button dynamically
<Grid>
<ListBox />
<Button />
</Grid>
Ok, this is working, but it is not good solution , because I need to handle ListBoxpopulating each time and reset margin for button. Bad bad bad.
P.S. Also, I can put button as ListBox item (not bad, but not good :)
Help me please...
If I understood you correctly, you need a simple Grid defined like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox />
<Button Grid.Row="1" Height="80"/>
</Grid>
By making height of the row zero to "star" and the first row to Auto, the ListBox will fill the remaining space, and the Button only 80 (you can change this to whatever you like). ListBox is automatically put to row zero because that's the default if it is not explicitly set.
If you don't want the button fixed on the page but to scroll with the ListBox, you could edit the ListBox template like this:
<Style x:Key="ListBoxWithButtonStyle" TargetType="ListBox">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsPresenter/>
<Button Content="Touch me" Height="80" Grid.Row="1"/>
</Grid>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And then apply to ListBox:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Style="{StaticResource ListBoxWithButtonStyle}">
</ListBox>
</Grid>

Scrolling Content Inside A Border With CornerRadius

I'm looking for help with a WPF problem I've been wrestling with for a while. I've styled a tab view, moving the tabs onto the left and displaying them vertically. These tabs sit inside a border with rounded corners at the top and bottom left.
Normal Tab http://gallery.me.com/theplatz/100006/TabGood.png?derivative=medium&source=web.png&type=medium&ver=12464623560001
I'm running into a problem when the tabs are scrolled. Instead of the rounded corners clipping the scrolled content, the content actually rides up on top of the corners, as seen here:
Overlapping Tab http://gallery.me.com/theplatz/100006/TabBad/web.png?ver=12464623500001
Here's the XAML:
<Style x:Key="SidebarTabControl" TargetType="TabControl">
<Setter Property="Background" Value="#FFC6D3DE" />
<Setter Property="Padding" Value="0,20,0,0" />
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" MinWidth="150" MaxWidth="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border
CornerRadius="10,0,0,10"
Background="{TemplateBinding Background}">
<ScrollViewer Grid.Column="0"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
ClipToBounds="True">
<Border Padding="{TemplateBinding Padding}">
<TabPanel
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent">
</TabPanel>
</Border>
</ScrollViewer>
</Border>
<ContentPresenter
Grid.Column="1"
Margin="0"
ContentSource="SelectedContent" />
<GridSplitter Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Background="{StaticResource SplitterBrush}"
ShowsPreview="True"
Width="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SidebarTab" TargetType="TabItem">
<Setter Property="Padding" Value="10,12,2,12" />
<Setter Property="BorderThickness" Value="0,1,0,1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border Padding="{TemplateBinding Padding}"
Name="tab"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{StaticResource SidebarTabBorderBrush}">
<ContentPresenter Style="{StaticResource SidebarTabForegroundStyle}" Name="content" ContentSource="Header" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="tab" Property="Background" Value="{StaticResource SidebarTabBackgroundBrushSelected}" />
<Setter TargetName="tab" Property="BorderBrush" Value="{StaticResource SidebarTabBorderBrushSelected}" />
<Setter TargetName="content" Property="Style" Value="{StaticResource SidebarTabForegroundStyleSelected}" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="tab" Property="Background" Value="{StaticResource SidebarTabBackgroundBrush}" />
<Setter TargetName="tab" Property="BorderBrush" Value="{StaticResource SidebarTabBorderBrush}" />
<Setter TargetName="content" Property="Style" Value="{StaticResource SidebarTabForegroundStyle}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any ideas on a way I could accomplish what I'm looking for? I've tried some ZIndex tricks, but that didn't seem to work.
To accomplish what I was looking for, I used the solution found here, and modified it to fit my needs. Here's what I ended up with:
<Style x:Key="SidebarTabControl" TargetType="TabControl">
<Setter Property="Background" Value="#FFC6D3DE" />
<Setter Property="Padding" Value="0,20,0,20" />
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" MinWidth="150" MaxWidth="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Background of the sidebar and our clipping bounds -->
<Border Grid.Column="0"
CornerRadius="10,0,0,10"
Background="{TemplateBinding Background}"
Name="mask" />
<!-- Border necessary so that the top tab does not get clipped prematurely -->
<Border Grid.Column="0" Background="Transparent">
<!-- Add opacity mask to clip contents as they're scrolled -->
<Border.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}"/>
</Border.OpacityMask>
<ScrollViewer
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Disabled">
<Border Padding="{TemplateBinding Padding}">
<TabPanel
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent">
</TabPanel>
</Border>
</ScrollViewer>
</Border>
<ContentPresenter
Grid.Column="1"
Margin="0"
ContentSource="SelectedContent" />
<GridSplitter Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Background="{StaticResource SplitterBrush}"
ShowsPreview="True"
Width="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit: I found the solution to the clipping problem with the first TabItem. Nesting the ScrollView inside a second border and applying the OpacityMask to this border, and not the ScrollView fixed the problem. Additionally, I had to explicitly set Background="Transparent" to the Border where the OpacityMask was being applied in order for the clip to not happen prematurely.
You could set a Clip on the rounded border with a geometry matching the border's outline.
<Border>
<Border.Clip>
<RectangleGeometry Rect="..." RadiusX="..." RadiusY="..."/>
</Border.Clip>
</Border>
Note that - as you've probably found - ClipToBounds on the Border won't work because the area between the corner and the rounded edge is in the bounds of the Border, so won't be clipped.
I had a similar issue, just assumed the border would clip it's content naturally. What good is it to implement a corner radius if content is just going to poke through anyway.
I ended up creating a custom control that inherits Border and set the clip for the border in it. Long story short I overrode ArrangeOverride to get the final size of the border and created a RectangleGeometry to use as the clip.
public class NodeBorder : Border
{
static NodeBorder()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeBorder), new FrameworkPropertyMetadata(typeof(NodeBorder)));
}
protected override Size ArrangeOverride(Size finalSize)
{
Rect rect= new Rect(0, 0,finalSize.Width,finalSize.Height);
RectangleGeometry geometry = new RectangleGeometry(rect,this.CornerRadius.TopLeft,this.CornerRadius.TopRight);
this.Clip = geometry;
return base.ArrangeOverride(finalSize);
}
}
When you create a custom control in VS it will create a style for the control in Generic.xaml. Just delete the style, it's not needed.
Then in your xaml, instead of Border, you can simply use yournamespace:NodeBorder.
You get all of the Border goodness, but with clipping built in. No extra markup, and it will size perfectly to your border layout.
EDIT:
Just occurred to me that clipping inside the border may be useful.
The above can be modified to include a dependency property ContentClip...
public static readonly DependencyProperty ContentClipProperty =
DependencyProperty.Register("ContentClip", typeof(Geometry), typeof(NodeBorder), new PropertyMetadata(null));
protected override Size ArrangeOverride(Size finalSize)
{
Rect rect= new Rect(0, 0,finalSize.Width-(this.BorderThickness.Left+ this.BorderThickness.Right),finalSize.Height-(this.BorderThickness.Top+ this.BorderThickness.Bottom));
RectangleGeometry geometry = new RectangleGeometry(rect,this.CornerRadius.TopLeft-1,this.CornerRadius.TopRight-1);
this.ContentClip = geometry;
return base.ArrangeOverride(finalSize);
}
... and used as such:
<Controls:NodeBorder
x:Name="xxx"
Canvas.Top="300"
Canvas.Left="10"
CornerRadius="20"
BorderBrush="Red"
BorderThickness="2"
Panel.ZIndex="10"
>
<Rectangle Width="60" Height="60"
Fill="Blue"
Clip="{Binding ElementName=xxx, Path=ContentClip}"
/>
</Controls:NodeBorder>

Resources