I have to develop a customized tab control and decided to create it with WPF/XAML, because I planned to learn it anyway. It should look like this when it's finished:
I made good progress so far, but there are two issues left:
Only the first/last tab item should have a rounded upper-left/bottom-left corner. Is it possible to modify the style of these items, similar to the way I did with the selected tab item?
The selected tab item should not have a border on its right side. I tried to accomplish this with z-index and overlapping, but the results were rather disappointing. Is there any other way to do this?
XAML:
<Window x:Class="MyProject.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="350" Width="500" Margin="5" Background="LightGray">
<Window.Resources>
<LinearGradientBrush x:Key="SelectedBorderBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="Gray" Offset="0.965"/>
<GradientStop Color="WhiteSmoke" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<DockPanel>
<Border
Panel.ZIndex="50"
Margin="0,100,-1,0"
Background="#FFAAAAAA"
BorderBrush="Gray"
CornerRadius="7,0,0,7"
BorderThickness="1">
<TabPanel
Margin="0,0,0,0"
IsItemsHost="True" />
</Border>
<Border
Background="WhiteSmoke"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="7,7,7,0" >
<ContentPresenter
ContentSource="SelectedContent" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border"
Background="#FFAAAAAA"
CornerRadius="7,0,0,0"
BorderBrush="Gray"
BorderThickness="0,0,0,1"
Panel.ZIndex="50"
Margin="0,0,0,0"
>
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Left"
ContentSource="Header"
Margin="10,10,10,10"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter Property="Margin" Value="0,0,-2,0" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="{StaticResource SelectedBorderBrush}"/>
<Setter TargetName="Border"
Property="Background"
Value="WhiteSmoke" />
<Setter TargetName="Border"
Property="CornerRadius"
Value="0,0,0,0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Name="_menuTabControl" TabStripPlacement="Left" Margin="5">
<TabItem Name="_tabItem1" Header="First Tab Item" ></TabItem>
<TabItem Name="_tabItem2" Header="Second Tab Item" >
<Grid />
</TabItem>
<TabItem Name="_tabItem3" Header="Third Tab Item" >
<Grid />
</TabItem>
</TabControl>
</Grid>
Edit: Thanks to Vlad, I could fix the second problem with a gradient border brush. See updates XAML for the solution.
Edit: Vlad fixed the first problem.
For the second problem, you should perhaps try to remove the clipping? Beware however of the possible issues.
For the first problem, you should try style trigger on property IsSelected. (Edit: I see, you are doing it exactly this way.) Have a look how this is implemented at the default template at MSDN. Note that they are using ZIndex, too.
Edit:
I found a workaround for your first/last tab problem. You need to use attached properties to designate the first/last tab:
In your TestWindow class you define attached property:
public static bool GetIsFirstTab(DependencyObject obj)
{
return (bool)obj.GetValue(IsFirstTabProperty);
}
public static void SetIsFirstTab(DependencyObject obj, bool value)
{
obj.SetValue(IsFirstTabProperty, value);
}
public static readonly DependencyProperty IsFirstTabProperty =
DependencyProperty.RegisterAttached("IsFirstTab", typeof(bool),
typeof(TestWindow), new UIPropertyMetadata(false));
Then, in your first tab you set this property:
<Window x:Class="MyProject.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject"
...
/>
...
<TabItem Name="_tabItem1" Header="First Tab Item"
local:TestWindow.IsFirstTab="true">
</TabItem>
Then, you should define a trigger for it:
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border"
Property="Background"
Value="WhiteSmoke" />
</Trigger>
<Trigger Property="local:Window1.IsFirstTab" Value="True">
<Setter TargetName="Border"
Property="Background"
Value="Red" />
</Trigger>
This must help.
The same trick would work with last tab. Or you can have a number instead of bool as attached property.
Related
I'm using Prism for UI composition and would need a way to switch the active view by a RadioButton. I want a group of RadioButtons that would change the desired view when checked. I thought the TabControl would be perfect for this. I thought I could just use a style to change the template of the TabItem to a RadioButton, but it is not switching tabs with the RadioButton is selected. Here's the template I have for the TabItem
<ControlTemplate TargetType="{x:Type TabItem}">
<RadioButton IsChecked="{TemplateBinding IsSelected}"
Content="{TemplateBinding Header}" />
</ControlTemplate>
I thought that should make the tab selected when the RadioButton is checked, but that doesn't appear to happen. What am I doing wrong or is there another way to achieve the same result?
Also is there a way to make the first view of the TabControl active? I tried SelectedIndex="0" on the tab control, but that doesn't seem to set the IActiveAware.IsActive on the view.
Here is the exact code that I'm using for styling the TabControl and TabItem
<Style TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{StaticResource mainRegionControlForegroundBrush}"></Setter>
<Setter Property="Header" Value="{Binding Content.DataContext.Title, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<RadioButton IsChecked="{TemplateBinding IsSelected}" Content="{TemplateBinding Header}" Foreground="{TemplateBinding Foreground}" Margin="{TemplateBinding Margin}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local" ClipToBounds="True" SnapsToDevicePixels="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel IsItemsHost="True" Orientation="Horizontal" Margin="10,0"/>
<ContentPresenter Grid.Row="1" Name="PART_SelectedContentHost" Content="{TemplateBinding TabControl.SelectedContent}" ContentSource="SelectedContent" ContentTemplate="{TemplateBinding TabControl.SelectedContentTemplate}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"></ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I figured it out. TemplateBinding wasn't updating the parent property, so IsSelected was never set on the TabItem. I changed the binding to
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
and it worked.
You simply need to define a new ControlTemplate for the RadioButton that uses a Border element:
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<ControlTemplate x:Key="TabTemplate" TargetType="{x:Type RadioButton}">
<Border BorderBrush="Black" Background="{TemplateBinding Background}"
BorderThickness="1,1,1,0" CornerRadius="5,5,0,0" Padding="5">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template" Value="{StaticResource TabTemplate}" />
<Setter Property="Height" Value="26" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="LightGray" />
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<RadioButton Content="Tab 1" />
<RadioButton Content="Tab 2" />
<RadioButton Content="Tab 3" />
</StackPanel>
I also added a basic Trigger so that you can see how to Style the selected item differently from the other tabs.
UPDATE >>>
Sorry, I clearly didn't read your question properly. To do it the other way around, it's a lot more work and unfortunately, you are going to have to do your bit because there is just too much code. So to start with, your example code didn't work because you were trying to define a new ControlTemplate for the TabControl, whereas you actually just need to define one for the TabItem.
The first step to do this is to actually define a new ControlTemplate for the whole TabControl which includes the ControlTemplate for the TabItem. Your example didn't work because you failed to replicate a lot of parts of the default ControlTemplate, so we need to do that. How? Well we can find the default ControlTemplate in the TabControl Styles and Templates page on MSDN.
So after looking at that, you'll understand why I can't put all that code here. At first, you basically need to use that exact XAML to reproduce the default ControlTemplate... when it is all working as normal, then you can start to tweak it to your requirements. If you look down the linked page, you'll see this:
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
...
This is where the default ControlTemplate for the TabItem starts. Looking further down, underneath the VisualStateManager.VisualStateGroups, you should see this:
<Border x:Name="Border"
Margin="0,0,-4,0"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True" />
</Border>
This is what defines what a TabItem should look like and is where you need to add your RadioButton (instead of this Border and it's contents). You'll probably also need to remove or adjust anything that references the old controls, eg. in the VisualStateManager.VisualStateGroups section.
I'm trying to make template for TabControl
visual design should be following:
TabControl content area should have small shadow over TabItems
but shadow should not be applied to active tab item
problem is that I can only write these templates as separated styles
meaning that if I use grid they'll end up in different grids and I can't change Zindex to make only one TabItem pop out over shadow
I'm using Border to apply shadow
<Border BorderThickness="0" >
<Border.Effect>
<DropShadowEffect Direction="90" ShadowDepth="1" Color="#b6b6b6" />
</Border.Effect>
</Border>
I came up with this, using the Kaxaml template for a TabControl, it incorporates Adrian's idea of using a LinearGradientBrush to simulate the shadow, I'm using a ControlTemplate:
<TabControl>
<TabControl.Resources>
<LinearGradientBrush x:Key="myBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#ffffff" Offset="0.0"/>
<GradientStop Color="#eeeeee" Offset="0.46"/>
<GradientStop Color="#787878" Offset="1"/>
</LinearGradientBrush>
<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="0,0,0,-1" IsItemsHost="True"
KeyboardNavigation.TabIndex="1" Background="{StaticResource myBrush}" />
<Border Name="Border" Grid.Row="1" Background="#FFFFFF" BorderBrush="#888888" BorderThickness="1"
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="#888888" />
<Setter TargetName="Border" Property="BorderBrush" Value="#AAAAAA" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- SimpleStyles: TabItem -->
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border" Margin="0,0,-4,0" Background="#E0E0E0" BorderBrush="#888888" BorderThickness="1,1,1,1" >
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center"
ContentSource="Header" Margin="12,2,12,2" RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter TargetName="Border" Property="Background" Value="{StaticResource myBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#EEEEEE" />
<Setter TargetName="Border" Property="BorderBrush" Value="#AAAAAA" />
<Setter Property="Foreground" Value="#888888" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Test" />
<TabItem Header="Test2" />
<TabItem Header="Test3" />
<TabItem Header="Test4" />
</TabControl>
By way of an explanation.
The LinearGradientBrush myBrush will be used in two places, the first is in the TabControl, as the Background:
<TabPanel Name="HeaderPanel" Grid.Row="0" Panel.ZIndex="1" Margin="0,0,0,-1" IsItemsHost="True"
KeyboardNavigation.TabIndex="1" Background="{StaticResource myBrush}" />
The Second is within the TabItem template as the background for all items that aren't selected *using a trigger); you could add additional selected styles if required:
<Setter TargetName="Border" Property="Background" Value="{StaticResource myBrush}" />
Note: I've changed the background of the TabControl so it's no longer transparent (so it has the shadow. This might fall flat on it's face depending on how you want to use the control (i.e. if you want to show content underneath).
Before this version, I had a different version that used a DropShadow and a clipping grid, which will retain the underlying transparency if you need it. It does rely on you manually matching the gradient brush with the DropShadow. Let me know if you need that other version and I'll post it.
Good luck.
You could get something similar by changing the background color to a gradient on the TabItem:
<Style TargetType="TabItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#eeeeee" Offset="0.0"/>
<GradientStop Color="#eeeeee" Offset="0.6"/>
<GradientStop Color="#b6b6b6" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
I have created a template of a button, which contains an Image and a TextBlock. Since I would like to change the appearance of both, I think they need to be in the Template. But, of course, not every instance of this template should present the same text and image.
Until now, i found a promising property called "Use a custom path expression" at the "Text" / "Source"-value filed of the TextBlock / Image at:
Data Binding... > Element Property > Use a custom path expression
I would now like to set this value at the instances of the button. I already tried to manually insert a myText="Lorem Ipsum" in the XAML of the Button, but that does not seem to work.
Thank you in advance for your help!
Update: This is, how the XAML looks like:
<TextBlock [...] Text="{Binding myText, ElementName={x:Null}, FallbackValue=Lorem}"/>
How do I access this or modify this, so it can be accessed?
Update 2: There already exist bindings for the TextBlock and the Image. At the moment, the XAML of the Button looks like that:
<s:SurfaceButton Command="{Binding SearchCustomCommand}" Style="{DynamicResource BasicButton}">
<StackPanel Orientation="Vertical" Height="60" Width="48" IsHitTestVisible="False">
<Image Source="{StaticResource ImageSourceToolboxSearchIcon}"
[...]
Stretch="Uniform" />
<TextBlock Text="{lex:LocText ToolboxButtonSearchesCustom}"
FontFamily="{DynamicResource button.font}"
[...]
FontSize="{DynamicResource button.size}"
Foreground="{DynamicResource button.color.default}"/>
</StackPanel>
</s:SurfaceButton>
I would now like to extract the Image and Textbox to the template (which also already exists), so I could refrence the Button in a way like this (whith all the Information about sizes and colors etc in the template and only the reference to the resource in the actual instance of the button - to be able to change the image/text for echt button seperately):
<s:SurfaceButton
Command="{Binding SearchPopularCommand}"
Style="{DynamicResource ToolboxButtonBig}"
ImageSource="{StaticResource ImageSourceToolboxSearchIcon}"
TextBlockSource="{lex:LocText ToolboxButtonSearchesCustom}"/>
I already copied the whole XAML for the StackPanel and the included TextBlock and Image to the Template. Now those are shown on every Button (which is fine), but i can't change the contents of them.
I'm sorry if the description is quite poor - I'm rather new to WPF...
Update 3: After some further research, I found questions similar to mine - which obviously describe the problem better than I could:
Button template with image and text in wpf
Creating an image+text button with a control template?
it is not necessary to edit button's template to insert image and text, you can set Button.Content property like this:
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="../Images/image.png"/>
<TextBlock Text="Lorem Ipsum"/>
</StackPanel>
</Button.Content>
</Button>
and it will work well. example above can be simplified but I inserted it like this for better understanding what is going on.
EDIT:
here are examples how it can be done in two different ways:
overwriting template:
<Button Content="Lorem Ipsum">
<Button.Template>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal">
<Image x:Name="ButtonImage" Source="../Images/mouseNotOverImage.png"/>
<ContentPresenter Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonImage" Property="Source" Value="../Images/mouseOverImage.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
complete button template definition you can find here
modifying style:
<Button>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content">
<Setter.Value>
<StackPanel Orientation="Horizontal">
<Image Source="../Images/mouseOverImage.png"/>
<TextBlock Text="Lorem Ipsum"/>
</StackPanel>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Content">
<Setter.Value>
<StackPanel Orientation="Horizontal">
<Image Source="../Images/mouseNotOverImage.png"/>
<TextBlock Text="Lorem Ipsum"/>
</StackPanel>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Your first option would be to define a style (or styles), e.g.
<Window.Resources>
<Style x:Key="MyButton" TargetType="{x:Type Button}" >
<Setter Property="Content">
<Setter.Value>
<Grid>
<Image .../>
<TextBlock Text="Test"/>
</Grid>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource MyButton}" />
</Grid>
Your second option would be to use Blend to make a copy of the default button style and edit that
<Window.Resources>
<Style x:Key="MyButton" TargetType="{x:Type Button}" >
<Setter Property="Content">
<Setter.Value>
<Grid>
<TextBlock Text="Test"/>
</Grid>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#F3F3F3" Offset="0"/>
<GradientStop Color="#EBEBEB" Offset="0.5"/>
<GradientStop Color="#DDDDDD" Offset="0.5"/>
<GradientStop Color="#CDCDCD" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/>
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding IsDefaulted}" SnapsToDevicePixels="true">
<!-- put your text and image here -->
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Themes:ButtonChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="RenderDefaulted" TargetName="Chrome" Value="true"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter Property="RenderPressed" TargetName="Chrome" Value="true"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#ADADAD"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="167,151,0,0" VerticalAlignment="Top" Width="75" Style="{DynamicResource MyButtonStyle}"/>
</Grid>
and a third option would be to create a custom control based on the default button style. You could then create dependency properties and use template bindings.
I have few buttons as Menu in my WPF App.
This scenario is something like menu in a website.
When I click one of the button, I want that button style to be different from others, and when I select another, the previous one should be normal and a selectedstyle should apply on this selected button.
Can you tell me how can I achieve this through ControlTemplate or do I have to maintain a IsSelected property that let us know which button is selected?
Thanks,
VJ
You can try with the RadionButton. The below sample will create a Flat button look for the RadioButton.
<Window x:Class="WpfApplication8.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="472">
<Window.Resources>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Focusable"
Value="False" />
<Setter Property="GroupName"
Value="filter" />
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<ControlTemplate.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="HorizontalAlignment"
Value="Center" />
</Style>
</ControlTemplate.Resources>
<Border x:Name="PART_border"
CornerRadius="2"
Margin="2"
Background="Transparent"
BorderThickness="1"
BorderBrush="{x:Static SystemColors.ControlDarkBrush}"
SnapsToDevicePixels="True">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter x:Name="PART_content" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter TargetName="PART_content"
Property="TextBlock.FontWeight"
Value="Bold" />
<Setter TargetName="PART_border"
Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Color="Black"
Offset="0" />
<GradientStop Color="white"
Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal" >
<RadioButton Height="30" Width="100" Content="First"></RadioButton>
<RadioButton Height="30"
Width="100"
Content="Second"></RadioButton>
<RadioButton Height="30"
Width="100"
Content="First"></RadioButton>
</StackPanel>
</Grid>
for RadioButton with Image just have look at Matt's blog
http://madprops.org/blog/wpf-killed-the-radiobutton-star/
You should use the built in visual state handling and create the states / styles in XAML.
In your concrete case it seems that what you're after is a group of RadioButtons, so that you don't have to write any code to deal with switching between states.
You better Use a Custom RadioButton List for this purpose.Check out the below post.
Do not allow unselect/deselect in ListBox
You can easily customize the selected and deselected styles to match your requirement
I have made a custom control in wpf however I am having an issue with the button that forms a part of the item control (the x in each item element in the attached picture is the button) basically the button is disabled, but I am not disabling it!
If I just place one of the inner items (MultiSelectionItem) into a grid by itself then the button works fine, so it must have something to do with my usage of the ItemsControl element in the Template for the outer control (MultiSelectionBox)
Image:
<Style TargetType="{x:Type local:MultipleSelectionBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MultipleSelectionBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsControl ItemsSource="{Binding multipleSelectionItems}">
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel IsItemsHost="True"/>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:MultipleSelectionItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MultipleSelectionItem}">
<Border BorderBrush="#FFC0CBD9" BorderThickness="1" Margin="0,0,2,2" CornerRadius="0">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFDFE9F5" Offset="0" />
<GradientStop Color="#FFEEF3FC" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Command="{x:Static local:MultipleSelectionItemCommands.RemoveCommand}" IsEnabled="True">
<Button.Template>
<ControlTemplate>
<Image Source="/CustomFormResearch;component/Images/x_no_hover.jpg" Margin="2,0,0,0" Height="11" Width="11">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="/CustomFormResearch;component/Images/x_no_hover.jpg" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/CustomFormResearch;component/Images/x_with_hover.jpg" />
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Button.Template>
</Button>
<TextBlock Text="{Binding DisplayData}" VerticalAlignment="Stretch" Margin="5,0,5,0" Grid.Column="1" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My first guess is that since your button is bound to a Command, the CanExecute of the command should return false.
This is the principle and benefit of a Command: When the can execute returns false it automaticaly disable the associated button.
Check this links for more about commands and MVVM :
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
http://msdn.microsoft.com/fr-fr/magazine/cc785480.aspx#id0190094