I have a requirement to create a WPF TabControl control with tabs rotated to the left.
The resources I've found online suggest that this can be done by applying the following two things to the TabControl:
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270"/>
</Setter.Value>
</Setter>
and
<TabControl TabStripPlacement="Left" ...
All this works and the tabs are displayed as required but for some reason the Header text in the tabs appears blurry after the rotation, please suggest why this is happening and if there is anything I can do to fix this issue.
Complete XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl BorderBrush="Gray" BorderThickness="1" TabStripPlacement="Left">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border" BorderBrush="Gray" BorderThickness="1" CornerRadius="6,6,0,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center" HorizontalAlignment="Center"
ContentSource="Header" Margin="12,2,12,2"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<TabItem Header="Item 1" />
<TabItem Header="Item 2" />
<TabItem Header="Item 3" />
</TabControl>
</Grid>
</Window>
Add the following properties to the window declaration XAML
TextOptions.TextFormattingMode="Display"
TextOptions.TextRenderingMode="ClearType"
UseLayoutRounding="true"
You could check out the RenderOptions class which controls rendering behavior of Objects. For example see this (Image in WPF getting Blurry) where it was used to reduce blurring of images.
Try to add SnapsToDevicePixels="True" and UseLayoutRounding="True" to your Border or TabControl tag. These have solved my blurriness problems in most cases.
I suggest this code snippet :
<TabControl TabStripPlacement="Left" Margin="5" FontSize="13" FontFamily="Verdana" Grid.ColumnSpan="2" SnapsToDevicePixels="True" UseLayoutRounding="True" >
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Border x:Name="grid" Margin="0" >
<Border.LayoutTransform>
<RotateTransform Angle="270" ></RotateTransform>
</Border.LayoutTransform>
<ContentPresenter>
<ContentPresenter.Content>
<TextBlock FontFamily="Verdana" Margin="4" Text="{TemplateBinding Content}">
</TextBlock>
</ContentPresenter.Content>
</ContentPresenter>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Name="General" Header="YourHeader" > ..
Related
I just have a simple binding, it works well but there is an error popup.
The effects work but still an error.
And the error is
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:(no path); DataItem=null; target element is 'VisualBrush' (HashCode=23487194); target property is 'Visual' (type 'Visual')
I have tried x: Reference but there would be another error.
Appreciated a lot if any can help.
<Style TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border
x:Name="RoundMask"
CornerRadius="10"
Background="white"/>
<!-- The main content -->
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=RoundMask}" />
</Grid.OpacityMask>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Re templating a window like that is not really the way I'd go about this sort of thing.
I'd use something more like the approach in this sample:
https://gallery.technet.microsoft.com/ThWPFPolishing-Chrome-f41be7fe
Finished fancy window is
Window6, using WindowChrome styling which is in the resource dictionary Dictionary1.
Which has stuff lika a big round close button. But to give you the idea before downloading:
<Style x:Key="FinishedWindow" TargetType="{x:Type Window}">
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="Foreground" Value="{StaticResource DarkDark}"/>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="0"
CornerRadius="20"
GlassFrameThickness="0"
NonClientFrameEdges="None"
ResizeBorderThickness="5"
/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border Background="{StaticResource BrightMid}" BorderBrush="{StaticResource DarkLight}" BorderThickness="4,4,6,6"
CornerRadius="12">
<Border.Effect>
<BlurEffect KernelType="Gaussian" Radius="12" RenderingBias="Quality" />
</Border.Effect>
</Border>
<Border BorderBrush="{StaticResource DarkDark}" BorderThickness="2"
CornerRadius="12" ClipToBounds="True">
</Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}"
Foreground="{StaticResource DarkDark}"
Grid.Row="0"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontWeight="Bold"
FontSize="16"
/>
<Button Name="CloseButton"
Width="20" Height="20"
Grid.Row="0"
HorizontalAlignment="Right"
BorderThickness="0"
Margin="0,12,12,0"
Command="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CloseCommand}"
Style="{StaticResource CloseButton}"/>
<ContentPresenter Grid.Row="1" Margin="12"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
I gave your style a go.
Just using it implicitly had no effect at all.
I put it in app.xaml and gave it a key
<Application.Resources>
<Style TargetType="{x:Type Window}" x:Key="roundedWindowStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border
x:Name="RoundMask"
CornerRadius="10"
Background="white"/>
<!-- The main content -->
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=RoundMask}" />
</Grid.OpacityMask>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
And then I applied that to mainwindow
<Window
...
Title="MainWindow"
Style="{StaticResource roundedWindowStyle}"
Hit f5... and it kind of works. Well.
If you ignore that window chrome means it cannot work as you seem to intend.
You probably ought to be looking at using window chrome instead.
With what you have there.
At the absolute minimum, you need a Contentpresenter inside that Grid.
Because a window is a content control but it won't show any content at all if you have no contentpresenter in the template.
I've been developing some simple apps (just for educational purposes) with Windows Forms and now I'm starting to learn WPF, but it seems a bit more complex to me.
I would like to create a simple menu like the one you can see in the pictures. I've been trying to find it on Google and YouTube, but I only found tutorials about "Hamburger menu", which isn't what I'm looking for.
This is really an area where WPF shines: the layout you describe can be implemented with nothing more than a restyled tab control.
Start with an unstyled tab control with TabStripPlacement of Left:
Then edit the TabControl style to add gradient and margins behind the TabPanel:
Add the image and override the TabItem style to set the font and remove the background. I used a chevron to indicate selection rather than bolding the text, but that would have been handled the same way.
Here is the full XAML used for the screenshots above:
<Window x:Class="StackOverflowTabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackOverflowTabControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<Style x:Key="ItemContainerStyle" TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Margin="15,5">
<Path Width="10" Height="14" Margin="0,2,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Stretch="Fill" Fill="#FF000000" Data="F1 M 39.8307,37.6042L 36.6641,34.4375L 25.1849,23.3542L 35.4766,23.3542L 50.5182,37.6042L 35.4766,51.8542L 25.1849,51.8542L 36.6641,40.7708L 39.8307,37.6042 Z " Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter Content="{TemplateBinding Header}" Margin="20,5,10,5">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontWeight" Value="Light" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TabControlStyle1" TargetType="{x:Type TabControl}">
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="ItemContainerStyle" Value="{StaticResource ItemContainerStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Padding="5">
<Border.Background>
<LinearGradientBrush EndPoint="0,0" StartPoint="1,0">
<GradientStop Color="#FFC7C7C7" Offset="0"/>
<GradientStop Color="#FFECECEC" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<DockPanel>
<Image DockPanel.Dock="Bottom" HorizontalAlignment="Center" Margin="20" Source="pack://application:,,,/StackOverflowTabControl;component/twc.png" Width="200" />
<TabPanel x:Name="headerPanel" Background="Transparent" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
</DockPanel>
</Border>
<Border x:Name="contentPanel" Grid.Column="1" Margin="5,0,0,0">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Style="{DynamicResource TabControlStyle1}">
<TabItem Header="System Information">
<TextBlock Text="Windows 10 pro" />
</TabItem>
<TabItem Header="Something else">
<TextBlock Text="Other page" />
</TabItem>
</TabControl>
</Grid>
</Window>
I have a Viewbox:
<Viewbox x:Key="SampleViewbox" >
<Grid>
<Ellipse Stroke="#e2e2e0" StrokeThickness="6" Fill="#d5273e" Width="128" Height="128"/>
</Grid>
</Viewbox>
I then include this in a Style like:
<Style x:Key="SampleStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Transparent" >
<ContentPresenter Content="{StaticResource SampleViewbox}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I created many buttons with SampleStyle
<Grid>
<StackPanel>
<Button Style="{StaticResource SampleStyle}" Height="50" Width="50"></Button>
<Button Style="{StaticResource SampleStyle}" Height="80" Width="80"></Button>
<Button Style="{StaticResource SampleStyle}" Height="20" Width="20"></Button>
</StackPanel>
</Grid>
However, Only one button has the Ellipse (viewbox)
How can I make all the buttons have/show the ellipse??
Viewbox is FrameworkElement which cannot belong to multiple parents. Every time buttons request a resource {StaticResource SampleViewbox} they get the same instance.
to change that behavior add x:Shared="False" attribute
<Viewbox x:Key="SampleViewbox" x:Shared="False">
I think a good approach is to use DataTemplate.
So you will have
<DataTemplate x:Key="SampleViewbox">
<Viewbox>
<Grid>
<Ellipse
Width="128"
Height="128"
Fill="#d5273e"
Stroke="#e2e2e0"
StrokeThickness="6" />
</Grid>
</Viewbox>
</DataTemplate>
And for style
<Style x:Key="SampleStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Transparent">
<ContentPresenter ContentTemplate="{StaticResource SampleViewbox}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Setting the x:Shared attribute of the ViewBox to false as suggested by #ASh will indeed work but why don't you just include the ViewBox in the ControlTemplate like this?
<Style x:Key="SampleStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Transparent" >
<Viewbox>
<Grid>
<Ellipse Stroke="#e2e2e0" StrokeThickness="6" Fill="#d5273e" Width="128" Height="128"/>
</Grid>
</Viewbox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
A template is a template and a control instance is an instance.
I'm trying to create a custom tooltip. The problem is that I can not display the error text. This code works perfectly (a simple tooltip)
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Polygon Fill="Red"
Margin="0,2,2,0"
Points="10,10 10,0 0,0"
VerticalAlignment="Top"
HorizontalAlignment="Right"
ToolTip="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}">
</Polygon>
<AdornedElementPlaceholder x:Name="adorner" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
But this code, not work, not show the error
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Polygon Fill="Red"
Margin="0,2,2,0"
Points="10,10 10,0 0,0"
VerticalAlignment="Top"
HorizontalAlignment="Right">
<Polygon.ToolTip>
<ToolTip Content="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
BorderThickness="1"
Foreground="White"
Background="Red" />
</Polygon.ToolTip>
</Polygon>
<AdornedElementPlaceholder x:Name="adorner" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
The Tooltip itself and the AdornedElementPlaceholder reside in different namescopes so binding using an ElementName won't work.
But you can set the Tag property of the Polygon to the ErrorContent and bind the Content property of the Tooltip to the Tag property of its PlacementTarget (which is the Polygon). This works:
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Polygon Fill="Red"
Margin="0,2,2,0"
Points="10,10 10,0 0,0"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Tag="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}">
<Polygon.ToolTip>
<ToolTip Content="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"
BorderThickness="1"
Foreground="White"
Background="Red" />
</Polygon.ToolTip>
</Polygon>
<AdornedElementPlaceholder x:Name="adorner" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The call to (Validation.Errors)[0] will be troublesome as you are referencing a particular object from a list that will update and change therefore breaking the binding.
The first example where you use (Validation.Errors).CurrentItem. is more suitable as it matches your first implementation which works.
I ran into issues with this a few years back, basically never use an index in a binding unless you are absolutely 100% sure it will not change.
<Window.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Background="LightBlue"
BorderBrush="Black"
BorderThickness="1,1,1,1"
CornerRadius="6,6,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Height="181" VerticalAlignment="Top">
<TabItem Header="Cheese" />
<TabItem Header="Pepperoni" />
<TabItem Header="Mushrooms" />
</TabControl>
<TabControl Margin="0,201,0,60">
<TabItem Header="Cheese" />
<TabItem Header="Pepperoni" />
<TabItem Header="Mushrooms" />
</TabControl>
</Grid>
That code will create two TabControls with the same style. How can I make those two TabControls use different styles? Sample code would be helpful.
You have defined a single style targetting "TabItem" which will apply to the tab items of any tab control, thus both your tab controls will have the same style. You can use the x:Key attribute to name two different styles and then apply them to the TabControl or TabItem as per your requirements.
You can either do like in the code below,specifieing style for individual tabitems giving a key for the style resource
<Window.Resources>
<Style TargetType="{x:Type TabItem}" x:Key="TabItemStyle1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
------------
------------
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabItem}" x:Key="TabItemStyle2">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
------------
------------
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Height="181" VerticalAlignment="Top">
<TabItem Header="Cheese" Style="{StaticResource TabItemStyle1} />
<TabItem Header="Pepperoni" Style="{StaticResource TabItemStyle1} />
<TabItem Header="Mushrooms" Style="{StaticResource TabItemStyle1} />
</TabControl>
<TabControl Margin="0,201,0,60">
<TabItem Header="Cheese" Style="{StaticResource TabItemStyle2} />
<TabItem Header="Pepperoni" Style="{StaticResource TabItemStyle2} />
<TabItem Header="Mushrooms" Style="{StaticResource TabItemStyle2} />
</TabControl>
</Grid>
or you can use control level style resources
<Grid>
<TabControl Height="181" VerticalAlignment="Top">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
------------
------------
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Cheese" />
<TabItem Header="Pepperoni" />
<TabItem Header="Mushrooms" />
</TabControl>
<TabControl Margin="0,201,0,60">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
------------
------------
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Cheese" />
<TabItem Header="Pepperoni" />
<TabItem Header="Mushrooms" />
</TabControl>
</Grid>