Metro Tab Control Style WPF - c#

I would like to achieve a tabcontrol Style like the one In this project (https://github.com/thielj/MetroFramework). Unfortunately the metroframework project only works for winforms.
Can someone help me to get this styled in xaml?
Would help me alot already, if I have the style for the blue/gray line separating tabitem and tabcontent.
Thanks in advance.

First create two ResourceDictionary to store new templates, one for TabControl and one for TabItem and name them TabControlTemplate.xaml and TabItemTemplate.xaml
Create a copy of default templates for controls mentioned above by right clicking on TabControl and TabItem and then choosing Edit Template > Edit a copy.... Then choose your style names to MetroLikeTabControl and MetroLikeTabItem and set target resource dictionaries for each template. Visual studio creates a copy of that template in selected files.
In TabControlTemplate.xaml add this setter tag to control styles:
<Setter Property="ItemContainerStyle" Value="{DynamicResource MetroLikeTabItem}" />
Then change this part of template:
<TabPanel Grid.Row="0" Grid.Column="0" x:Name="HeaderPanel"
Margin="2,2,2,0" Panel.ZIndex="1"
Background="Transparent" IsItemsHost="True" KeyboardNavigation.TabIndex="1" />
to this new one:
<Grid Grid.Row="0" Grid.Column="0">
<Border BorderThickness="0 0 0 2" BorderBrush="Gray" />
<TabPanel x:Name="HeaderPanel" Margin="2,2,2,0" Panel.ZIndex="1"
Background="Transparent" IsItemsHost="True" KeyboardNavigation.TabIndex="1" />
</Grid>
this adds a border with only bottom thickness and makes your TabPanel's border overlap with TabItem's border (Why only bottom border? because I'm implementing what you want when TabControl's TabStripPlacement property is set to Top. You can set triggers to implement all other states.
In TabItemTemlate.xaml set a BorderBrush = "0 0 0 2" for element with name innerBorder and remove Opacity = "0" property.
Then change styles of IsMouseOver = true, IsSelected = true and IsSelected = false (default style of a TabItem) as desired. This is my edited trigger for Selected state that changes content of TabItem and color of Border to Blue.
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true" />
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top" />
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1" />
<!-- commented line below, because we don't need Select Scale behaviour in metro style anymore -->
<!--<Setter Property="Margin" Value="-2,-2,-2,0" />-->
<Setter TargetName="innerBorder" Property="Opacity" Value="1" />
<Setter TargetName="innerBorder" Property="BorderThickness" Value="0,0,0,2" />
<Setter TargetName="innerBorder" Property="BorderBrush" Value="#0088cc" />
<Setter TargetName="contentPresenter" Property="TextBlock.Foreground" Value="#0088cc" />
<Setter TargetName="mainBorder" Property="BorderThickness" Value="0" />
</MultiDataTrigger>

Related

Change TextBox text and focus via CheckBox in XAML

When I check or uncheck a CheckBox, I want to clear the TextBox and set focus. I implemented it using code-behind.
My xmal:
<CheckBox x:Name="checkbox1" Checked="checkbox1_Checked" Unchecked="checkbox1_Checked" />
<TextBox x:Name="textbox1" Text="Hello" />
My C#:
private void checkbox1_Checked(object sender, RoutedEventArgs e)
{
textbox1.Text = "";
textbox1.Focus();
}
Is it possible to do it all in XAML?
Use data triggers. They work by creating a binding to a regular property, which is then
monitored for changes.
For instance, consider the following example:
<Window x:Class="WpfTutorialSamples.Styles.StyleDataTriggerSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="StyleDataTriggerSample" Height="200" Width="200">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<CheckBox Name="cbSample" Content="Hello, world?" />
<TextBlock HorizontalAlignment="Center" Margin="0,20,0,0" FontSize="48">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="No" />
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbSample, Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Yes!" />
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</Window>
In this example, we have a CheckBox and a TextBlock. Using a DataTrigger, we bind the TextBlock
to the IsChecked property of the CheckBox. We then supply a default style, where the text
is "No" and the foreground colour is red, and then, using a DataTrigger, we supply a style for
when the IsChecked property of the CheckBox is changed to True, in which case we make it
green with a text saying "Yes!" (as seen on the screen shot).
you can find more in here.

Conditional styling of an element in XAML

I am building a Windows phone 8 app that has a view model property of:
public bool IsReply {get; set;}
In my xaml code, I would like to distinguish two cases:
IsReply=True
<Grid Margin="0,0,0,0">
...
</Grid>
IsReply=False
<Grid Margin="40,0,0,0">
...
</Grid>
Basically, I would like to style the Grid element depending on the value of IsReply. I know that in WPF Style.Triggers exists, but apparently not in WP.
The solution I have right now is to have a duplicate copy of the entire grid code and set the visibility of each to a data converter. However, I feel this should be simpler to do.
The easiest way is to use a Style with Triggers:
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Margin" Value="40 0 0 0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsReply}" Value="True">
<Setter Property="Margin" Value="0 0 0 0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
You can bind the margin of the grid in your MVVM
<Grid Margin="{Binding margin}">
...
</Grid>
In your model
if(IsReply)
margin = new Thickness("0,0,0,0");
else
margin = new Thickness("40,0,0,0");
No need to create separate grids.
You can use DataTrigger, but you have to add these two references (right click on References in your project and AddReference/Assemblies/Extensions/ ... ).
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Grid
Margin="0">
<i:Interaction.Triggers>
<ei:DataTrigger
Binding="{Binding Path=IsReply}"
Value="True">
<ei:ChangePropertyAction
PropertyName="Margin"
Value="0" />
</ei:DataTrigger>
<ei:DataTrigger
Binding="{Binding Path=IsReply}"
Value="False">
<ei:ChangePropertyAction
PropertyName="Margin"
Value="40,0,0,0" />
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>

MouseOver trigger doesn`t work after programmatically set Foreground

I'm new to WPF, but searching internet for some days I couldn't figure out my problem.
After I programmatically change Foreground property, IsMouseOver trigger doesn't work. Please be tolerant and thank in advance :)
<Style x:Key="ZizaMenuItem" TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Margin" Value="5,0,5,0"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Label FontSize="14" Content="{TemplateBinding Content}" Name="ZizaMenuItemText" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ZizaMenuItemText" Property="Foreground" Value="#ff0000"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<StackPanel Height="30" Name="ZizaMenu" Orientation="Horizontal" Margin="0,12,0,0" VerticalAlignment="Top">
<Label Content="ZIZA" FontSize="11" FontWeight="Bold" Foreground="Black" Height="25" Margin="20,0,10,0" />
<Button Name="ZizaMenuInteresting" Click="ZizaMenuItemClicked" Content="ИНТЕРЕСНОЕ" Style="{StaticResource ZizaMenuItem}" />
<Button Name="ZizaMenuBest" Click="ZizaMenuItemClicked" Content="ЛУЧШЕЕ" Style="{StaticResource ZizaMenuItem}" />
<Button Name="ZizaMenuAuto" Click="ZizaMenuItemClicked" Content="АВТО" Style="{StaticResource ZizaMenuItem}" />
</StackPanel>
private void ZizaMenuItemClicked(object sender, RoutedEventArgs e)
{
// get label object from template
Button zizaMenuItem = (Button)sender;
Label zizaMenuItemText = (Label)zizaMenuItem.Template.FindName("ZizaMenuItemText", zizaMenuItem);
// set Foreground color for all buttons in menu
foreach (var item in ZizaMenu.Children)
if (item is Button)
((Label)(item as Button).Template.FindName("ZizaMenuItemText", (item as Button))).Foreground = Brushes.Black;
// set desired color to clicked button label
zizaMenuItemText.Foreground = new SolidColorBrush(Color.FromRgb(102, 206, 245));
}
That is horrible code, do not mess with controls inside control templates, ever. Template.FindName is something only the control that is being templated should call internally to get its parts, and only those, everything else should be considered uncertain.
If you need to change a property template bind it, and then bind or set said property on the instance. In terms of precedence you need to make sure not to create a local value which overrides the triggers (that is what you did). You can use a Style and Setter on the Label to bind the default Foreground.
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="{TemplateBinding Foreground}"/>
</Style>
</Label.Style>
Now you just need to set the Foreground of the Button itself, the Trigger should still internally have precedence over that Setter.
It has to do with dependency property value precedence. Local values have higher precedence than template triggers.
For more information read this: http://msdn.microsoft.com/en-us/library/ms743230.aspx

How to get the Binding element of a collection inside an item template in WPF?

I have a custom control RadCoverFlow that takes a collection of Image as an itemsSource.
<StackPanel Orientation="Vertical" Background="Black">
<telerik:RadCoverFlow x:Name="coverFlow"
ItemsSource="{Binding ViewImages, Mode=OneWay}"
ItemTemplate="{StaticResource ImageTemplate}"
</telerik:RadCoverFlow>
</StackPanel>
I want to define the widh, the height and a couple of other properties of the Images using a data template. My problem is that in the Data Template, I need to specify a source for each images, but that source is already specified in code.
<DataTemplate x:Key="ImageTemplate">
<Image Source="" Width="100" Height="100" Stretch="Uniform" telerik:RadCoverFlow.EnableLoadNotification="True" />
</DataTemplate>
How can I not re-specify the Source, or bind the source to the Source like {Binding ViewImages[i]}, what would be i in this case?
Thank You
Ideally, your business objects and your UI should be completely separate, so your ItemsSource should not have Image UI objects
But that said, try and use an implicit style to set your properties
<telerik:RadCoverFlow.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="Height" Value="100" />
<Setter Property="Width" Value="100" />
<Setter Property="Stretch" Value="Uniform" />
<Setter Property="telerik:RadCoverFlow.EnableLoadNotification" Value="True" />
</Style>
</telerik:RadCoverFlow.Resources>

WPF DevComponents TabNavigation inability to change SelectedIndex/Item in code-behind

I am using the DevComponents TabNavigation control for WPF, and am able to add a new TabItem to the TabNavigation at a specific index, call it i, in the code-behind. Now I want to make the new TabItem the SelectedItem, by doing:
private void textBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int i = createNewTabItem(0, "Foo");
TabNavigation tn = (((sender as TextBlock).Parent as Grid).Parent as TabItem).Parent as TabNavigation;
tn.SelectedItem = tn.Items[i];
}
private int createNewTabItem(int overflowSrcPageNum, String header)
{
TabItem ti = new TabItem();
ti.Header = header;
tabNavigation.Items.Insert(overflowSrcPageNum + 1, ti);
return overflowSrcPageNum + 1;
}
When I run this code, however, instead of the new TabItem being brought into view, it is brought into view and then the original tab I was on is quickly moved back into view.
If anyone has any ideas as to why this is happening, and how I can fix it please let me know. I have attached a sample of the XAML below:
<Grid >
<Grid.Resources>
<ResourceDictionary>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextDecorations" Value="Underline"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="11" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Text" Value="View More..." />
<Setter Property="Visibility" Value="Visible" />
<EventSetter Event="MouseLeftButtonDown" Handler="lblMoreCpartys_MouseLeftButtonDown" />
</Style>
</ResourceDictionary>
</Grid.Resources>
<my:TabNavigation Background="Black" HorizontalAlignment="Stretch" Margin="0" Name="tabNavigation"
VerticalAlignment="Stretch" MouseLeftButtonDown="tabNavigation_MouseLeftButtonDown"
FontSize="12" Foreground="SteelBlue" ForceCursor="True" MouseWheel="tabNavigation_MouseWheel"
TabStripPlacement="Bottom">
<TabItem Header="ITEM 1" Name="firstTabItem" FontSize="12" >
<TextBlock Name="firstTB" />
</TabItem>
<TabItem Header="ITEM 2" Name="secondTabItem" FontSize="12" >
<TextBlock Name="secondTB" />
</TabItem>
</my:TabNavigation>
</grid>
Thanks in advance.
Try setting e.Handled to True in textBlock_MouseLeftButtonDown.
I'm not familiar with that control, but if it works like TabControl then it has logic to bring a tab into view when it is clicked. That logic sees that the original tab was clicked, and brings it back into view after your change. Marking the EventArgs object as Handled will stop WPF from calling event handlers on parent elements, which would stop the tab from switching back.

Categories