Scrolling Content Inside A Border With CornerRadius - wpf

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>

Related

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.

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.

How to style a chart to omit the excess chart area surround the chart

I am using Silverlight Toolkit from http://silverlight.codeplex.com/ and am having an issue with the styling. Based off microsoft's documents I see the chart is made up of 4 basic components.
ChartAreaStyle ==> Grid
LegendStyle ==> Legend (a control similar to an ItemsControl with a Title)
PlotAreaStyle ==> Grid
TitleStyle ==> Title (a ContentControl)
ref http://msdn.microsoft.com/en-us/expression/dd433476.aspx
My Question Is
How can I fill the chart container with the chart itself instead of having an empty surrounding area and if possible omit the legend?
I am still trying to wrap my head around xaml and control templates etc. I know it probably can be done using that, I just don't know how.
Here is an example of what I am trying to achieve:
Here is what it looks like now:
So I copied the style of the Chart control from the Toolkit source code and removed redundant elements from the template.
The styles definitions look so:
<UserControl.Resources>
<Style x:Key="ChartWithoutPaddings" TargetType="chart:Chart">
<Setter Property="Padding" Value="0" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ChartAreaStyle">
<Setter.Value>
<Style TargetType="Panel">
<Setter Property="MinWidth" Value="100" />
<Setter Property="MinHeight" Value="20" />
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chart:Chart">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<chartingprimitives:EdgePanel x:Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}">
<Grid Canvas.ZIndex="-1" Style="{TemplateBinding PlotAreaStyle}" />
</chartingprimitives:EdgePanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="EmptyDataPoint" TargetType="Control">
<Setter Property="Background" Value="Black" />
<Setter Property="Template" Value="{x:Null}" />
</Style>
<Style x:Key="OnePixelLine" TargetType="Polyline">
<Setter Property="StrokeThickness" Value="1" />
</Style>
</UserControl.Resources>
You should also hide axes and specify height and width of the chart:
<chart:Chart Style="{StaticResource ChartWithoutPaddings}"
VerticalAlignment="Center" HorizontalAlignment="Center"
Width="200" Height="30">
<chart:LineSeries ItemsSource="{Binding Items}" IndependentValuePath="Title" DependentValuePath="Value"
DataPointStyle="{StaticResource EmptyDataPoint}" PolylineStyle="{StaticResource OnePixelLine}" />
<chart:Chart.Axes>
<chart:CategoryAxis Orientation="X" Height="0" Opacity="0" />
<chart:LinearAxis Orientation="Y" Width="0" Opacity="0" />
</chart:Chart.Axes>
</chart:Chart>

How to change part of the datatemplate background

I have a ListBox, its DateTemplate like this:
<Grid Margin="30,0,0,0" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Source="{Binding Path=IconSource}"/>
<TextBlock Grid.Column="1" Background="{x:Null}" Text="{Binding Path=DisplayText, Mode=Default}" Foreground="Black"/>
</Grid>
and this is the ItemContainerStyle:
<Style x:Key="Test" TargetType="ListBoxItem">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" SnapsToDevicePixels="True">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="Blue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When one ListBoxItem is selected, is it possible not to set the image's background?
Like in Intellisense of VS, the icon of the API doens't have background.
The simplest thing is to put your image inside another Grid, and set the Grid's Background to the background color you would like the image to have - then when you change the Background of the parent Border, the image's parent grid will protect it from the background color change.
I am not completely sure if this is what you meant, but I would recommend to use no data template but instead do all the necessary data binding with the elements of the item container control template. This is possible as the item container and the data template actually share the same data context. In case of a ListBoxItem, this would look like this:
<ControlTemplate x:Key="ListBoxItemTemplate" TargetType="ListBoxItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="{Binding IconSource}" Height="50" Margin="0,0,5,0"/>
<Border x:Name="Border" VerticalAlignment="Center" Grid.Column="1">
<TextBlock Text="{Binding Name}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="Blue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
You would reference this template in the ItemsContainerStyle that is set on your ListBox.
In summary: do not use a data template but incorporate it in the ItemContainerStyle. Thus you can leave the background of the Image element untouched.
Hope this helps.

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>

Resources