WPF: Negative margin gets clipped in ScrollViewer - wpf

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.

Related

How to draw a separator line below DataGrid headers in WPF

Is there a way to draw a separator line below the headers in a DataGrid? I have set GridLinesVisibility to None as I don't want any gridlines except the one below the headers. I'm struggling to find a way to do this and any help would be greatly appreciated.
This is what I want to achieve.
You can modify the DataGridColumnHeader's ControlTemplate.
I used the original DataGrid's template and replaced the default border and fill with a Rectangle with a height of 1.
<DataGrid>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Style.Resources>
<!-- This style is required for the column resize thumbs -->
<Style x:Key="ColumnHeaderGripperStyle" TargetType="{x:Type Thumb}">
<Setter Property="Width" Value="8" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Cursor" Value="SizeWE" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="1"/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<Thumb Grid.Row="0" x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" />
<Thumb Grid.Row="0" x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" />
<Rectangle Grid.Row="1" Height="1" HorizontalAlignment="Stretch" Stroke="Black"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>

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>

How can I move TabItem of TabControl?

<Border Background="#FF260F54">
<TabControl Name="MyTabCtrl" SelectionChanged="MyTabCtrl_SelectionChanged" >
<TabItem Name="TItem01" Header="01">
<TextBlock>TItem01</TextBlock>
</TabItem>
<TabItem Name="TItem02" Header="02">
<TextBlock>TItem02</TextBlock>
</TabItem>
</TabControl>
</Border>
I want to make TItem01 move to the left 200 pixels, then show TItem02.
What should I do? Please help me. Thank you very much!
Don't know if this is the simplest way, but I would modify the default TabControl Style:
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<Style TargetType="{x:Type TabControl}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabPanel
Name="HeaderPanel"
Grid.Row="0"
Panel.ZIndex="1"
Margin="200,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border
Name="Border"
Grid.Row="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2" >
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Notice the Margin property of the TabPanel element in the ContolTemplate? That margin determines where the tabs start. By default it's 0,0,4,-1 and I modified it to 200,0,4,-1 to match your requirement.
If you are wondering how I produced the style, there are a couple of ways. The easiest would be using Expression Blend. It has an option to "break" your control and expose all it's default parts and styles. Another way is just search MSDN for ControlTemplates because they are public. (I used the second method)

WPF toggle buttons with custom template not accepting click in correct area

I'm a relative beginner in the WPF space so I'm sure this will be the first of many questions!
I have a series of toggle buttons all with a custom template designed to show an image with a transparent background that then becomes highlighted when the user toggles the button.
I wanted to add padding around the content so that the highlighted area would extent around the content.
This is working, but the user still has to click in the inner area to activate the button, which is not what I want.
I'm assuming it's because I'm using the Margin property of the ContentPresenter to bind to the Padding of the button and this is classed as outside of the content, but not sure of the best way to fix this.
It does work when de-selecting the button though.
Below is some XAML showing the problem which should be able to be copied and pasted straight into XamlPad.
<Page.Resources>
<Style x:Key="ValidationToggleButton" TargetType="ToggleButton">
<Setter Property="Padding" Value="5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate >
<Grid Name="MainGrid">
<Viewbox>
<ContentPresenter Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Property=Button.Content}" />
</Viewbox>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="MainGrid" Property="Background" Value="#88FFFF55" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<GroupBox Grid.Column="0" Header="Validation" BorderBrush="#55BBE6" Margin="2" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0" Style="{StaticResource ValidationToggleButton}">
CLICK
</ToggleButton>
</Grid>
</GroupBox>
</Grid>
Anyone have any idea how I might correct this?
Welcome to WPF world :). That happens because of the... background brush. If you don't set it it's null. And that means it's not visible for hit testing mechanism. Quick fix to this set Background="Transparent" to the MainGrid. But more appropriate way to set it via styles:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style x:Key="ValidationToggleButton" TargetType="ToggleButton">
<Setter Property="Padding" Value="5" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate >
<Grid Name="MainGrid" Background="{TemplateBinding Background}">
<Viewbox>
<ContentPresenter Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Property=Button.Content}" />
</Viewbox>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="MainGrid" Property="Background" Value="#88FFFF55" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<GroupBox Grid.Column="0" Header="Validation" BorderBrush="#55BBE6" Margin="2" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0" Style="{StaticResource ValidationToggleButton}">
CLICK
</ToggleButton>
</Grid>
</GroupBox>
</Grid>
</Page>

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