How to use left over space in wpf tab items row - wpf

Upper part of TabControl consists of TabItem controls. Is there a way to reuse remaining space there to put some WPF content?
I think I could use a "fake" TabItem with different styling and put my stuff in TabItem.Header but I was hoping there's a better way.
Solution
Based on the answer below, I got the desired behavior by wrapping TabPanel in the template below within e.g. StackPanel and adding my additional content after it.
<StackPanel Orientation="Horizontal">
<TabPanel
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
Background="Transparent" />
<TextBlock>Foo</TextBlock>
</StackPanel>

I tried a different way, which was to create another grid that occupies the same space as the TabControl, ie both are in Grid.Row=0. I have bound the grid height to the height of the first tab so if the tabs change height the other controls will remain centered. I set MinWidth on the window so the controls dont overlap the tabs.
Paste this code into a new WPF Window...
<Window x:Class="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"
mc:Ignorable="d"
Title="MainWindow" Height="306" Width="490" MinWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TabControl Grid.Row="0" x:Name="tabControl">
<TabItem x:Name="tabItem" Header="TabItem" Height="50">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
<Grid Grid.Row="0" Height="{Binding ActualHeight, ElementName=tabItem}"
VerticalAlignment="Top" Margin="0,2,0,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"
VerticalAlignment="Center" Margin="20,0">
<TextBlock VerticalAlignment="Center" Margin="10,0" FontSize="16"
Foreground="Red" FontFamily="Calibri">My Text</TextBlock>
<Button Content="My Button" />
</StackPanel>
</Grid>
</Grid>
</Window>
...and you will get this:

You can use a template and make it do whatever you want, that is the power of WPF. Here is a nice article on customizing the TabControl and the TabItem controls.
< EDIT Adding code for TabControl template from Switch On The Code article>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabPanel
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
Background="Transparent" />
<Border
Grid.Row="1"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="0, 12, 12, 12" >
<Border.Background>
<LinearGradientBrush>
<GradientStop Color="LightBlue" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<ContentPresenter ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
all you have to do is add your content to the Template, the part that holds the tab items is the <TabControl>

Related

How to resize button content in WPF with a viewbox while maintaining the Font size?

I am creating an app and I need the content of a button to be resizable when the user minimises their screen. We know that the viewbox does this in WPF; it's the easiest way to resize text automatically.
This is the code for [the top part] of the button, I apologise about the formatting:
<Button Grid.Row="0"
Grid.Column="2"
Background="#3767B0"
Style="{DynamicResource IconStyle}"
BorderBrush="Transparent">
<Button.ContentTemplate>
<DataTemplate >
<Viewbox>
<TextBlock></TextBlock>
</Viewbox>
</DataTemplate>
</Button.ContentTemplate>
</Button>
What I want to get out of this is: to use the Viewbox in WPF to resize the content of this button while maintaining the font size of the content.
This resizes the content of the button, but the button's content is not maintained:
This is how it should look like with a viewbox, and should resize properly:
By maintaining I mean I want to use the viewbox whilst the content of the button (which is a child of the viewbox) looks exactly how it would've with a font size of 22. You can see how I want it to look like with a viewbox inside the content of the button in the image above. I've tried to be as clear as possible on this. Maybe I don't understand how the viewbox works?
Someone requested the icon style. Here is the icon style code:
<Style TargetType="Button"
x:Key="IconStyle"
BasedOn="{StaticResource ButtonStyle1}">
<Setter Property="FontFamily"
Value="Segoe MDL2 Assets" />
<Setter Property="Grid.RowSpan"
Value="2" />
<Setter Property="Padding"
Value="0,0,0,60" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="FontSize"
Value="80" />
<Setter Property="Background"
Value="#307A85" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="15"
ShadowDepth="0"
Opacity="0.4"
Color="Black" />
</Setter.Value>
</Setter>
If I understand correctly what you need to implement, then remove the ViewBox and set the explicit dimensions of the TextBlock.
I am showing an arbitrary example, but for your task, you yourself choose the required dimensions and alignment.
<Button Background="#3767B0"
Style="{DynamicResource IconStyle}"
BorderBrush="Transparent">
<Button.ContentTemplate>
<DataTemplate >
<TextBlock Width="300"
Height="300"
FontSize="200"
Text=""/>
</DataTemplate>
</Button.ContentTemplate>
</Button>
Hello my good friends on Stack OverFlow. I have found a solution to this problem, all I had to do was set the padding of the TextBlock:
<!--FEEDBACK BUTTON-->
<Button Grid.Row="3"
Grid.Column="2"
Grid.RowSpan="2"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
BorderThickness="3"
Name="Feedback_Button"
BorderBrush="White"
Click="Button_Click"
MouseEnter="Button_MouseEnter"
MouseLeave="Button_MouseLeave">
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Button.Template>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="218*" />
<RowDefinition Height="68*" />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Grid.Column="2"
Background="#3767B0"
FontFamily="Segoe MDL2 Assets"
Foreground="White"
Content="Feedback"
BorderBrush="Transparent">
<Button.ContentTemplate>
<DataTemplate>
<Viewbox>
<TextBlock Padding="15"></TextBlock>
</Viewbox>
</DataTemplate>
</Button.ContentTemplate>
</Button>
<Button Grid.Row="4"
Grid.Column="2"
Background="#FF2D5BA0"
FontFamily="Segoe UI Light"
Style="{StaticResource TextButton}"
Content="Feedback"
BorderBrush="Transparent">
<Button.ContentTemplate>
<DataTemplate>
<Viewbox>
<TextBlock Padding="15">Feedback</TextBlock>
</Viewbox>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</Grid>
</Button>
<!--FEEDBACK BUTTON-->
Thank you my friends for your help, such a simple solution I would've expected you guys to know this.
Cheers,
#Tom Joney , I do not quite clearly understand what you need to implement, so I cannot give an exact answer.
General answer for using different layout.
The ViewBox scales the content to the maximum allowed size provided by the outer container.
The Padding property of a container sets the size of the border between the container and its contents.
The element's Margin property sets the size of the border between the element and its containing container.
That is, these are very close properties.
In our case, the main content is Text.
Therefore, there is no difference how to set the size of the border: between the Text and the containing TextBlock (TextBlock.Padding) or between the TextBlock and its containing outer container (the Grid line).
Look at two examples in which I set the background colors for better clarity.
Example with Padding
<Window x:Class="ViewBoxScaleText.PaddingWind"
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:ViewBoxScaleText"
mc:Ignorable="d"
Title="PaddingWind" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Background="LightGreen">
<Viewbox>
<TextBlock Padding="15"
Background="LightBlue"
Text="{Binding Text, ElementName=tb}"/>
</Viewbox>
</Border>
<TextBox x:Name="tb" Grid.Row="1" Margin="10" Text="Feedback"/>
</Grid>
</Window>
Example with Margin
<Window x:Class="ViewBoxScaleText.MarginWind"
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:ViewBoxScaleText"
mc:Ignorable="d"
Title="MarginWind" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Background="LightGreen">
<Viewbox>
<TextBlock Margin="15"
Background="LightBlue"
Text="{Binding Text, ElementName=tb}"/>
</Viewbox>
</Border>
<TextBox x:Name="tb" Grid.Row="1" Margin="10" Text="Feedback"/>
</Grid>
</Window>
The color of the regions shows that the containers have different sizes, but this does not affect the location and size of the Text.
Try to enter text in the field at the bottom of the window to see how the amount of text affects its size and the size of the border.
Since the ViewBox scales the entire content, both the text size size and are the border scaled.
If in the examples above you change the size of the window, you will see that the size of the container occupied by the text also changes.
This happens due to the fact that the setting of the border occurs inside the ViewBox and thus we set the size of the border, in fact, in units relative to the size of the text.
Changing the size of the text will change the size of the face and therefore change the overall container for the text.
Here is an example with the ability to change the font size in the very bottom margin of the window.
See how this affects the size of the border.
<Window x:Class="ViewBoxScaleText.FontSizeWind"
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:ViewBoxScaleText"
mc:Ignorable="d"
Title="FontSizeWind" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Background="LightGreen">
<Viewbox>
<TextBlock Margin="15"
Background="LightBlue"
Text="{Binding Text, ElementName=tbText}" FontSize="{Binding Text, ElementName=tbFontSize}"/>
</Viewbox>
</Border>
<TextBox x:Name="tbText" Grid.Row="1" Margin="10" Text="Feedback"/>
<TextBox x:Name="tbFontSize" Grid.Row="2" Margin="10" Text="80"/>
</Grid>
</Window>
Here are some more layout options in one XAML so that you can launch, check their behavior and choose the one that suits you
<Window x:Class="ViewBoxScaleText.MultiVariantWind"
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:ViewBoxScaleText"
mc:Ignorable="d"
Title="MultiVariantWind" Height="750" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Background="LightGreen" Margin="5">
<Viewbox>
<TextBlock Margin="15"
Background="LightBlue"
Text="{Binding Text, ElementName=tbText}" FontSize="{Binding Text, ElementName=tbFontSize}"/>
</Viewbox>
</Border>
<Border Background="LightGreen" Margin="5" Grid.Row="1">
<Viewbox Margin="15">
<TextBlock
Background="LightBlue"
Text="{Binding Text, ElementName=tbText}" FontSize="{Binding Text, ElementName=tbFontSize}"/>
</Viewbox>
</Border>
<Border Background="LightGreen" Margin="5" Grid.Row="2">
<Viewbox Width="150" Height="150">
<TextBlock
Background="LightBlue"
Text="{Binding Text, ElementName=tbText}" FontSize="{Binding Text, ElementName=tbFontSize}"/>
</Viewbox>
</Border>
<Border Background="LightGreen" Margin="5" Grid.Row="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*"/>
<ColumnDefinition Width="70*"/>
<ColumnDefinition Width="15*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15*"/>
<RowDefinition Height="70*"/>
<RowDefinition Height="15*"/>
</Grid.RowDefinitions>
<Viewbox Grid.Row="1" Grid.Column="1">
<TextBlock
Background="LightBlue"
Text="{Binding Text, ElementName=tbText}" FontSize="{Binding Text, ElementName=tbFontSize}"/>
</Viewbox>
</Grid>
</Border>
<TextBox x:Name="tbText" Grid.Row="4" Margin="10" Text="Feedback"/>
<TextBox x:Name="tbFontSize" Grid.Row="5" Margin="10" Text="80"/>
</Grid>
</Window>

WPF ViewBox inside of a StackPanel

I am trying to display something like a score board, with team name in top 25% of a square and score taking up lower 75% (with font ratios as appropriate). I also want these controls to grow/shrink as the window gets resized.
To do this I have created a grid and split it into * and 3* rows. I have added one TextBlock to the top row and another TextBlock spanning bottom 4 rows. Then I have wrapped each TextBox in a ViewBox
This causes an application to go into a "break mode".
This shows the issue:
<Window x:Class="WpfRandoms.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:WpfRandoms"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Teams}" HorizontalAlignment="Center" Grid.Row="0" BorderBrush="{x:Null}" Background="{x:Null}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border Margin="10" Padding="10" CornerRadius="5" Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0">
<TextBlock Text="{Binding Name}" TextAlignment="Center" />
</Viewbox>
<Viewbox Grid.Row="1">
<TextBlock Text="{Binding Score}" FontSize="40" TextAlignment="Center" />
</Viewbox>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
With code-behind:
using System.Collections.Generic;
using System.Windows;
namespace WpfRandoms
{
public partial class MainWindow : Window
{
public class Team
{
public string Name { get; set; }
public int Score { get; set; }
}
public IList<Team> Teams { get; private set; }
public MainWindow()
{
InitializeComponent();
Teams = new List<Team>();
Teams.Add(new Team() { Name = "Team A", Score = 78 });
Teams.Add(new Team() { Name = "Team B", Score = 1 });
Teams.Add(new Team() { Name = "Team C", Score = 101 });
DataContext = this;
}
}
}
This seems to be caused by the StackPanel used as ItemsPanelTemplate of a ListView (without this StackPanel everything works fine but the layout is not as desired). However, I am not aware of another way of making ListView horizontal, so I am stuck with the StackPanel.
After some experimenting and reading about how ViewBox works, and how StackPanels behave I arrived at some solution (possibly not the best one).
I am wrapping my ListView in a Border, and then binding the height of a Border within the ListView's DataTemplate to the height of that outside Border.
Because of that there is a small limitation - mainly top/bottom margins cause the elements of the ListView to be trimmed. To avoid this trimming I have added padding to the Border within the DataTemplate, and then added a smaller Border within that has a background etc.
I had to also hide the vertical scrollbar of the ListView.
Here is what I have:
<Window x:Class="WpfRandoms.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<Border x:Name="MyBorder" Margin="10" Grid.Row="0">
<ListView ItemsSource="{Binding Teams}" HorizontalAlignment="Center" VerticalAlignment="Stretch" BorderBrush="{x:Null}" Background="{x:Null}" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border Margin="10, 0" Padding="10" Height="{Binding ActualHeight, ElementName=MyBorder}" Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}">
<Border Background="AliceBlue" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0">
<TextBlock Text="{Binding Name}" TextAlignment="Center"/>
</Viewbox>
<Viewbox Grid.Row="1">
<TextBlock Text="{Binding Score}" FontSize="40" TextAlignment="Center"/>
</Viewbox>
</Grid>
</Border>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Grid>
</Window>
If there is a better solution, I would greatly appreciate it :)
I'm not sure if I understand your problem exactly. If you are looking to keep the ratios between the name and score the same, you can use the "*" for the name (25%) and "3*" for the score (75%). You can also put each TextBlock in a Viewbox rather than the entire thing. To keep it simple, I replaced the converter and bindings with hard coded strings.
<Window x:Class="WpfApplication1.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>
<Border Margin="10" Padding="10" CornerRadius="5" Background="Coral">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0">
<TextBlock Text="Home" TextAlignment="Center" />
</Viewbox>
<Viewbox Grid.Row="1" >
<TextBlock Grid.Row="1" Text="25" TextAlignment="Center" />
</Viewbox>
</Grid>
</Border>
</Grid>
I think you may have a problem with the converter you are referencing on the boarder control.
I tested your XAML without the bindings and the view box works as expected.
<Border Margin="10" Padding="10" CornerRadius="5" Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}">
<Viewbox>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Team Name" TextAlignment="Center" Grid.Row="0" />
<TextBlock Text="100" FontSize="100" TextAlignment="Center" Grid.Row="1" Grid.RowSpan="3"/>
</Grid>
</Viewbox>
</Border>
Hopefully this helps!

How to make a WPF window resizable if a Grid row contains a collapsed element

I have a problem with the design of a resizable WPF window.
The result shall be that the window elements are resized automatically while changing the width and height of the window by dragging with the mouse.
The base panel is a Dockpanel on which the first element is a ToolBar which is positioned at the top.
Then comes a Grid with two rows.
In each of the rows is a DockPanel with inner elements.
Some of these elements can be hidden by Visibilty.Collapsed (which would be made by properties in the final version).
For testing purposes i have two DockPanels after ToolBar and i have set the visibility of the first in the XAML file to Collapsed.
When i start the application the window seems to be correct.
But as i click the bottom of the window for changing the height by dragging, the second Dockpanel gets smaller and the place where the collapsed Dockpanel would have been gets visible.
What have i done wrong?
Here is my XAML:
<Window x:Class="WPFResizable.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:WPFResizable"
mc:Ignorable="d"
Title="MainWindow"
SizeToContent="Height"
Width="525">
<DockPanel VerticalAlignment="Stretch" Background="AliceBlue">
<Button DockPanel.Dock="Top" Height="40" Content="ToolBar"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0" VerticalAlignment="Stretch" Visibility="Collapsed">
<GroupBox Margin="5"
Header="Group 1">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TextBox Text="Test 1"
MinWidth="400"
MinHeight="100"
Margin="5"/>
</ScrollViewer>
</GroupBox>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="0" VerticalAlignment="Stretch">
<GroupBox Margin="5"
Header="Group 2">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TextBox Text="Test 2"
MinWidth="400"
MinHeight="100"
Margin="5"/>
</ScrollViewer>
</GroupBox>
</DockPanel>
</Grid>
</DockPanel>
</Window>
I think this will do what you want, if I correctly understand what you're trying to do.
VerticalAlignment="Stretch" is redundant on a Grid child, and if there's only one column, you don't need to specify Grid.Column on the children.
Your first grid row had Height="*", which resulted in it taking up half the height of the grid whether or not its content was visible. I've written a Style with a Trigger that changes that row's height to Auto when its content is not visible. This depends on there being exactly one element in the row, but since you've got a DockPanel there, you seem to be thinking along those lines already.
<Grid DockPanel.Dock="Bottom">
<Grid.RowDefinitions>
<RowDefinition>
<RowDefinition.Style>
<Style TargetType="RowDefinition">
<!--
Must set the default in a Setter instead of an attribute, or
dependency property precedence will override the trigger
below.
-->
<Setter Property="Height" Value="*" />
<Style.Triggers>
<DataTrigger
Binding="{Binding Visibility, ElementName=TopChildPanel}"
Value="Collapsed"
>
<Setter Property="Height" Value="Auto" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel x:Name="TopChildPanel" Grid.Row="0" Visibility="Visible">
<GroupBox Margin="5"
Header="Group 1">
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<TextBox Text="Test 1"
MinWidth="400"
MinHeight="100"
Margin="5"/>
</ScrollViewer>
</GroupBox>
</DockPanel>
<DockPanel Grid.Row="1">
<GroupBox
Margin="5"
Header="Group 2">
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
>
<TextBox
Text="Test 2"
MinWidth="400"
MinHeight="100"
Margin="5"/>
</ScrollViewer>
</GroupBox>
</DockPanel>
</Grid>

WPF - How to skip a label in tab navigation?

I have a stackpanel inside a controltemplate inside a itemscontrol inside a stackpanel inside a grid inside a usercontrol (se the xaml below). In the inner stackpanel there is a label (Name="NoComponentChosen") and another stackpanel (Name="ComponentChosen"). The label's visibility is initially collapsed.
The controltemplate has a datatrigger with a binding. When the binding reference has a specific value the label (NoComponentChosen) becomes visible and the stackpanel collapses.
I would like to skip the label/stackpanel when I am tabbing trough the user interface. I would also like to still be able tab through the rest of the things in my ChangeRequestView.xaml (listbox and buttons).
Right now - when I tab - the label/stackpanel eventually becomes chosen and is surrounded by a dotted rectangle. That is what I would like to avoid.
This is my ChangeRequestView.xaml:
<UserControl x:Class="MyProgram.View.ChangeRequestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:ListBoxBehavior="clr-namespace:MyProgram.Utility"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="35"/>
<RowDefinition Height="45*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<!--The title of the view-->
<Label Grid.Row="0" Content="Change Requests" FontWeight="Bold" Margin="5,5,5,5" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<!--The component chosen-->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5,0,0,0" IsHitTestVisible="False" Focusable="False">
<ItemsControl>
<ItemsControl.Template>
<ControlTemplate>
<StackPanel>
<StackPanel Name="ComponentChosen" Orientation="Horizontal">
<Label Content="Reference des.: " />
<Label Name="referenceDesignation" Content="{Binding Path=ReferenceDesignation, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<Label Name="NoComponentChosen" Content="Choose a component" Visibility="Collapsed"/>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ReferenceDesignation}" Value="">
<Setter TargetName="ComponentChosen" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="NoComponentChosen" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</StackPanel>
<ListBox Grid.Row="2" ... />
<Button Grid.Row="3" ... />
<Button Grid.Row="4" ... />
</Grid>
</UserControl>
The whole UserControl/ChangeRequestView.xaml is part of my MainWindow - I don't know if that has anything to say. I tab through all of the different views in my MainWindow, and when I get to the ChangeRequestView I would like to skip the label/stackpanel according to which of them isn't collapsed.
This is my MainWindow.xaml:
<Window x:Class="MyProgram.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:MyProgram.View"
xmlns:vm="clr-namespace:MyProgram.ViewModel"
...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="309*"/>
<RowDefinition Height="187*"/>
<RowDefinition Height="120*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209*"/>
<ColumnDefinition Width="558*"/>
<ColumnDefinition Width="250*"/>
</Grid.ColumnDefinitions>
<Menu IsMainMenu="True" Name="Menu1" VerticalAlignment="Top" Grid.Row="0" Grid.ColumnSpan="3">
<MenuItem Header="_File">
<MenuItem Header="_Save changed change requests for current reference designation"
InputGestureText="Ctrl+S" Command="{Binding Path=HotKeysSaveChangeRequestUpdateCmd}"/>
<MenuItem Header="_Upload change requests for current project"
InputGestureText="Ctrl+U" Command="{Binding Path=HotKeysUploadCmd}"/>
<MenuItem Header="_Exit" ToolTip="Exit this program"
InputGestureText="CTRL+X" Command="{Binding Path=ShutDownCmd}"/>
</MenuItem>
</Menu>
<!--Search Region-->
<Border Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" Margin="3,1,1,3" BorderThickness="2,2,2,2" CornerRadius="4,4,4,4" BorderBrush="LightGray">
<view:SearchView x:Name="SearchView"/>
</Border>
<!-- Project Region -->
<Border Grid.Row="1" Grid.Column="0" Margin="3,3,1,1" BorderThickness="2,2,2,2" CornerRadius="4,4,4,4" BorderBrush="LightGray">
<view:ProjectView x:Name="ProjectView" />
</Border>
<!-- Components Region -->
<Border Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Margin="1,3,1,1" BorderThickness="2,2,2,2" CornerRadius="4,4,4,4" BorderBrush="LightGray">
<view:ComponentView x:Name="ComponentView" />
</Border>
<!-- Change Request Region -->
<Border Grid.Row="1" Grid.Column="2" Grid.RowSpan="2" Margin="1,3,3,1" BorderThickness="2,2,2,2" CornerRadius="4,4,4,4" BorderBrush="LightGray">
<view:ChangeRequestView x:Name="ChangeRequestView" />
</Border>
<!-- Log Region -->
<Border Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="1,1,3,3" BorderThickness="2,2,2,2" CornerRadius="4,4,4,4" BorderBrush="LightGray">
<view:LogView x:Name="LogView" />
</Border>
</Grid>
<Window.InputBindings>
<KeyBinding Gesture="CTRL+U" Command="{Binding Path=HotKeysUploadCmd}"/>
<KeyBinding Gesture="CTRL+S" Command="{Binding Path=HotKeysSaveChangeRequestUpdateCmd}"/>
<KeyBinding Gesture="CTRL+X" Command="{Binding Path=ShutDownCmd}"/>
</Window.InputBindings>
</Window> </br>
I apologize for the long xaml-files, but I'm thinking the more information I provide to you the easier it will be for you to help me solve this tabbing issue.
Do you have any ideas of how to skip the label "NoComponentChosen"/the stackpanel "ComponentChosen" when I tab through the user interface?
Try KeyboardNavigation.TabNavigation Attached Property with KeyboardNavigationMode.None on the container with the elements you want to skip.
KeyboardNavigation.TabNavigation="None"

WPF tabitem positioning

What is the proper way of positioning for example three tabitems at the very top left corner and one at the very top right corner of a tab control using WPF?
I have tried to move the fourth tabitem to the right by changing its margin but this doesn't produce a good result; first of all it is cut short and second of all it does not display correctly when selected.
The problem is that the TabPanel, which is used internally by the TabControl to lay out the tabs, does not seem to support what you want. A quick workaround would be to replace the TabPanel by something else, for example, a DockPanel:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<TabControl>
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border BorderThickness="0,0,1,1" BorderBrush="#D0CEBF" Grid.Row="1">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter ContentSource="SelectedContent"/>
</Border>
</Border>
</Border>
<DockPanel IsItemsHost="True" LastChildFill="False" Margin="2,2,2,0" />
</Grid>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="Item 1" />
<TabItem Header="Item 2" />
<TabItem Header="Item 3" />
<TabItem Header="Item 4" DockPanel.Dock="Right" />
</TabControl>
</Window>
(Reference: This is a modified version of an MSDN example for styling a TabControl.)
The simple DockPanel doesn't work as smooth as the TabPanel -- the tabs "jump" a bit when switching between them, but this might get you started. Maybe subclassing the TabPanel and overriding the relevant parts would give you a more accurate result; I guess it depends on how much effort you want to put into this.
I found that by inserting an "invisible" tab I could adjust the spacing, (i.e. move the tabs down from the top)
For example:
TabItem Height="100" Visibility="Hidden" <br>
TabItem..... <br>
TabItem.... <br>
You would need to swap out the TabPanel within the TabControl to something custom which provided the desired behavior. None of the default panels are going to provide your desired behavior out of the box.
This will most likely need to involve overriding MeasureOverride and ArrangeOverride to provide the custom placement within the panel that is desired based on the number of items it contains.
This will involve a custom ControlTemplate for the TabControl. I tried an example using a DockPanel as the items host rather than the default TabPanel.
<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>
<DockPanel Name="HeaderPanel"
LastChildFill="False"
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border Name="Border"
Grid.Row="1"
Background="WhiteSmoke"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2">
<ContentPresenter Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that I don't know of a way of exposing the DockPanel.Dock property to the TabItems outside of the ControlTemplate E.G.
<TabControl Margin="10">
<TabItem Header="Tab One" DockPanel.Dock="Left"/>
<TabItem Header="Tab Two" DocKPanel.Dock="Left"/>
<TabItem Header="Tab Three" DocKPanel.Dock="Left"/>
<TabItem Header="Tab Four" DocKPanel.Dock="Right"/>
</TabControl>
// Note: This does not work!!
I guess you will need to write your own Panel to host the TabItems; Note that this will be quite a lot of work as you will need to handle things like overflow behaviour which is built into the TabPanel.
Even if you did try this I think you would have to write a custom TabControl if you wanted to expose this functionality outside of the ControlTemplate.
If you want to go down this road then see my answer in this post

Resources