How to center UserControl in a TabControl - wpf

My MainWindow is built with TabContol containing in each tab UserControl in xaml files. Opening specific UserControl is not a problem, but aligning it is. I was able to horizontally center content of tab but struggle to vertically do this same. I found out that the root problem is that UserControl don't take the whole free space (height) in the Tab. I tried to make main grid VerticalAlignment="Stretch" and "Center" but that didn't help. I could use margin with specific number or define row fixed hight but that will not work on every resolution and I don't want to write method in code behind but use the power of xaml. How can I force UserControl to take whole height in Tab and then vertically center it (it's important to do it for specific UserControl because others should have default position)?
ps. I'm using MetroWindow from MahApps.Metro.
MainWindow main Grid:
<Grid>
<StackPanel>
<TabControl ItemsSource="{Binding Tabs}"
SelectedIndex="0">
<TabControl.Resources>
<Style TargetType="{x:Type TabPanel}">
<Setter Property="HorizontalAlignment"
Value="Center" />
</Style>
<DataTemplate DataType="{x:Type VMod:LoginViewModel}">
<Pages:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:AdminViewModel}">
<Pages:AdminView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:ProductsViewModel}">
<Pages:ProductsView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:DistributionViewModel}">
<Pages:DistributionView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:SummaryViewModel}">
<Pages:SummaryView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:SettingsViewModel}">
<Pages:SettingsView />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type inter:ITab}">
<TextBlock>
<Run Text="{Binding TabName}" />
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</StackPanel>
</Grid>
UserControl main Grid:
<Grid Background="LightBlue"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Height="300"
Width="300"
Grid.Row="2"
BorderBrush="LightGray"
BorderThickness="1">
<StackPanel HorizontalAlignment="Center">
<iconPacks:PackIconRPGAwesome Kind="Honeycomb"
HorizontalAlignment="Center"
Width="60"
Height="60"
Margin="0, 0, 0, 0"/>
<TextBlock HorizontalAlignment="Center"
Text="DistributionTool"
FontSize="20"
FontWeight="Bold"
Margin="5" />
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Margin="5"
TextAlignment="Left"
FontSize="15"/>
<iconPacks:PackIconMaterial Grid.Column="1"
Kind="AccountTie"
Width="20"
Height="20"
VerticalAlignment="Center"/>
</Grid>
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<PasswordBox Grid.Column="0"
Margin="5"
HorizontalContentAlignment="Left"
FontSize="15"
Style="{StaticResource Win8MetroPasswordBox}" />
<iconPacks:PackIconMaterial Grid.Column="1"
Kind="Key"
Width="20"
Height="20"
VerticalAlignment="Center" />
</Grid>
<Button Content="LOGIN"
Width="80"
metro:ControlsHelper.ContentCharacterCasing="Normal"
Margin="5"
Style="{StaticResource AccentedSquareButtonStyle}" />
</StackPanel>
</Border>
</Grid>

From what I gather, what you could try would be:
Remove the StackPanel in your MainWindow Grid. Unless you intend to have more than 1 child inside the stack panel (Other than your TabControl), it is useless.
Add VerticalAlignement="Stretch" to your TabControl. This will allow it to take up all the space it can vertically.
Then you should be pretty much set to go.
The reason why you shouldn't use a StackPanel unless you intend to stack items inside, as in
<StackPanel>
<Child1/>
<Child2/>
</StackPanel>
is that the StackPanel.Orientation property affects how things will appear inside, including the Alignement of each child.
So Orientation="Vertical" (the default), affects the VerticalAlignement of its children. Same idea with Horizontal.

Related

How To Organize ItemsPanel Layout?

I have the code below which I want to display a series of buttons horizontally with an image and text label, and below that row of buttons one label for the entire button group. What I currently have displays the buttons in a row, but the group label displays in the same row to the left of the buttons. I'm probably doing this wrong, but I'm impressed that I got this far. I've tried putting the Child ItemsPanel in a StackPanel, but then it shows no buttons - it seems to loose contact with the collection from the parent window. Is there an easy way to connect this collection and organize the label below the buttons? I'm thinking my next try is to collect the data from the parent in the child's code-behind and build an ItemsSource which I will then connect to the ItemTemplate, but I don't really know how to do that either.
Parent window snippet:
<User_Controls:ToolbarGroup GroupLabel="Group 1">
<User_Controls:ImageButton ButtonText="Test 1"/>
<User_Controls:ImageButton ButtonText="Test 2"/>
<User_Controls:ImageButton ButtonText="Test 3"/>
</User_Controls:ToolbarGroup>
Child window code:
<ItemsControl x:Class="Fiducia_WPF.User_Controls.ToolbarGroup"
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:User_Controls="clr-namespace:Fiducia_WPF.User_Controls"
mc:Ignorable="d"
Loaded="ItemsControl_Loaded"
d:DesignHeight="170" d:DesignWidth="320">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="170" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<User_Controls:ImageButton ButtonText="Temp"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<Label Name="lblGroupTitle" Grid.Row="1" Grid.Column="0" Content="Group X" FontSize="16" FontWeight="Bold" Margin="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Visible" />
</ItemsControl>
You should create a ControlTemplate for your ItemsControl:
<ItemsControl x:Class="Fiducia_WPF.User_Controls.ToolbarGroup" ...>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<StackPanel>
<ItemsPresenter/>
<Label x:Name="lblGroupTitle" Content="Group X" ... />
</StackPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="170" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<User_Controls:ImageButton ButtonText="Temp"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Setting the Content of the lblGroupTitle Label may probably better be done by binding it to a property of the ToolbarGroup control:
<Label Content="{Binding GroupTitle,
RelativeSource={RelativeSource AncestorType=User_Controls:ToolbarGroup}}" ... />
I'd just use a straight up Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" ... />
<Button Grid.Row="0" Grid.Column="1" ... />
<Button Grid.Row="0" Grid.Column="2" ... />
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" />
</Grid>

How to arrange stack panels in a stack panel?

I am facing issue while arranging Stack panel as mention in the screenshots.
I am new to the WPF and I need to design a layout that look something like as mention in the image.
Zahorak is correct. There are a number of ways to create this layout, the best being a grid or a DockPanel. I prefer a DockPanel. A DockPanel allows you to position child controls around the edge of the DockPanel, filling the rest of the DockPanel (if you don't specify otherwise) with the last child control. For example, to achieve the result you are after, the xaml would be
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<TextBlock Text="Stack Panel 2" />
</StackPanel>
<StackPanel DockPanel.Dock="Left">
<TextBlock Text="Stack Panel 3" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom">
<TextBlock Text="Stack Panel 5" />
</StackPanel>
<StackPanel>
<TextBlock Text="Stack Panel 4" />
</StackPanel>
</DockPanel>
A Grid layout requires a little more work.
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.ColumnSpan="2">
<TextBlock Text="Stack Panel 2" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.RowSpan="2">
<TextBlock Text="Stack Panel 3" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<TextBlock Text="Stack Panel 4" />
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="1">
<TextBlock Text="Stack Panel 5" />
</StackPanel>
</Grid>
Note that a missing Grid.Row or Grid.Column means a value of 0 (the first row or first column).
I hope this helps.
Here is a way with pure stackpanels. You would have to set a width for the tall, narrow one (Stackpanel 3) in the second row
<StackPanel Orientation="Vertical" Name="StackPanel1">
<StackPanel Orientation="Horizontal" Name="StackPanel2">
<StackPanel>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical" Name="StackPanel3">
<StackPanel>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Name="StackPanel4">
<StackPanel>
<StackPanel Orientation="Horizontal" Name="StackPanel5">
<StackPanel>
<StackPanel>
<StackPanel>
<StackPanel>

WPF Statusbar, stretch textblock to take as much space as possible

I created my own simple statusbarcontrol with 3 TextBlocks. Now I would like that the first Textblock takes as much space as it has available. That I don't seem to get done.. Now it only takes the space needed to display the text.
XAML:
<StatusBar Background="{StaticResource GradientBrush}">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem HorizontalAlignment="Left"
HorizontalContentAlignment="Right">
<Border BorderThickness="1"
BorderBrush="Black"
Padding="5 0 5 0"
Background="White">
<TextBlock Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding TextColorMessage}"
Background="White"
/>
</Border>
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2"
HorizontalAlignment="Right">
<TextBlock Text="{Binding Path=DatabaseName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StatusBarItem>
<Separator Grid.Column="3" />
<StatusBarItem Grid.Column="4"
HorizontalAlignment="Right">
<TextBlock Text="{Binding Path=ComputerName}" />
</StatusBarItem>
Well this is pretty straight forward:
you had set the StatusBarItem HorizontalAlignment="Left" when it should be "strech", same for the HorizontalContentAlignment.
Also would suggest setting margin=0 on the border.
this is what i did so it will work for me:
<StatusBarItem HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<Border BorderThickness="1"
BorderBrush="Black"
Margin="0"
Padding="5 0 5 0"
Background="White">
<TextBlock Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding TextColorMessage}"
Background="White"/>
</Border>
</StatusBarItem>
if this is helpful please mark as answer
Try setting the StatusBarItem's HorizontalAlignment and HorizontalContentAlignment to Stretch.
Set the StatusBarItem's HorizontalContentAlignment to Stretch and the TextBlock's TextAlignment property to Right. (This second setting is only needed for the first StatusBarItem as it seems that you try to align the text to the right in it.)
StatusBar has only one HorizontalAlignment=Right item to effect, so you can get only the last item in wanted place. You can set statusBar's FlowDirection="RightToLeft" and add item in reversed order. Details: right placed items needn't StatusBarItem surrounded, but the left one need to be streched. Following code:
<StatusBar VerticalAlignment="Bottom" FlowDirection="RightToLeft"><!--flow right to left cause items right aligned-->
<!--make item's flow back left to right, avoid display disorder-->
<TextBlock x:Name="textBlock_status_R1" Text="111.147.168.20" Grid.Column="2" Margin="10,0" FlowDirection="LeftToRight"/>
<Separator/>
<TextBlock x:Name="textBlock_status_R2" Text="Working" Margin="10,0" FlowDirection="LeftToRight"/>
<Separator/>
<!--To fill rest space,need StatusBarItem packing and default HorizontalAlignment="Stretch"-->
<StatusBarItem FlowDirection="LeftToRight">
<TextBlock x:Name="textBlock_status_L1" Text="Information here."/>
</StatusBarItem>
</StatusBar>

How to refer MaxWidth="??" of TextBlock to Owner ActualWidth?

How to refer MaxWidth="??" of TextBlock to stpMessage ActualWidth?
<StackPanel Name="stpMessage" Orientation="Horizontal" Margin="0,5,0,0">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,0,0,0" Foreground="Blue" TextWrapping="Wrap">#ToUserName</TextBlock>
<StackPanel Grid.Column="1">
<TextBlock Margin="5,0,0,0" Text="{Binding Path=Text}" MinHeight="20" MinWidth="200" HorizontalAlignment="Stretch" MaxWidth="1000"
VerticalAlignment="Stretch" TextWrapping="WrapWithOverflow">START skf skdjf skdj hfskdjf ksdjhf ksjdhf ksjhf kjsf kjshf kjshkjfhsdf kjsfdkj hskdfj hskdjf hskdjf skjhfksjfks END</TextBlock>
</StackPanel>
</Grid>
</StackPanel>
The problem with this is that StackPanels do not limit the size of their children, so will grow as much as their children need
Change your StackPanel to a control that limits the size of it's children, like a Grid (or wrap it in another control) and then use an ElementName binding to bind to the ActualWidth property of that control
<Grid Name="stpMessage" ... />
...
<TextBlock MaxWidth="{Binding ElementName=stpMessage, Path=ActualWidth}" ... />
...
</Grid>

Setting a relative column width in XAML. '0.5*' string cannot be converted to Length

Here's the XAML:
<ListBox Grid.Row="1" x:Name="lstGames" Background="#343434" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="0.5*"/> //<-----------HERE!
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="{Binding ImageUrl}" Stretch="UniformToFill"/>
<StackPanel Grid.Row="0" Grid.Column="1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Title:" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Release Date:" />
<TextBlock Text="{Binding ReleaseDate}" />
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
All I want is for the first column to at most be around 35% of the total width of the window. I was under the impression that this notation was allowed, but something is wrong and I'm getting that exception fired.
Any suggestions?
If I change it to only Width="0.5*" then it compiles and runs, but still doesn't give the pictures (the columns) a uniform width:
ColumnDefinition.MaxWidth is a double, not a GridLength. So it can't be a relative value. However, since it's a dependency property, you might be able to bind it to the total width of the window, using a converter with a parameter.

Resources