Reproduce CSS property in StackPanels : Space Between - wpf

Is it possible in XAML to automatically separate elements in a StackPanel no matter what the size of the window is?
In CSS with the property : space between you can send elements to both sides of the parent. This is what I want to reproduce. Is it possible ?

A StackPanel does not work this way , see the documentation. It does not have a notion of the available space or remaining space to divide, it just stacks elements in one direction.
Arranges child elements into a single line that can be oriented horizontally or vertically.
What you can do instead is use a DockPanel. It allows stacking controls to either side of the panel and by default the last element added to it fills the remaining space. The Border here serves as a dummy element to take the remaining space. Usually, you would put a control there that is actually used. Please be aware that this example only reproduces what you explicitly asked for, the panel allows for much more complex layouts. See remarks from the documentation.
<DockPanel>
<Rectangle DockPanel.Dock="Left" Fill="Black" Width="100" Height="100"/>
<Rectangle DockPanel.Dock="Right" Fill="Black" Width="100" Height="100"/>
<Border/>
</DockPanel>
An alternative is to use a Grid with three columns, where the left and right columns use Auto as Width so the contained controls only take up as much space as they need, while the center column has its Width set to *, which will make it fit the remaining space.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Fill="Black" Width="100" Height="100"/>
<Rectangle Grid.Column="2" Fill="Black" Width="100" Height="100"/>
</Grid>
The relevant portion on size attributes for Grid:
Columns and rows that are defined within a Grid can take advantage of Star sizing to distribute remaining space proportionally. When Star is selected as the height or width of a row or column, that column or row receives a weighted proportion of the remaining available space. This is in contrast to Auto, which distributes space evenly based on the size of the content that is within a column or row.
Update for your list example. You can define a data template for the items.
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="25,60,25,25" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Padding="0,25" BorderThickness="0 0 0 1" BorderBrush="#3D3C44">
<Grid Width="378" Background="black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="hello" Foreground="white"/>
<Border Grid.Column="1" x:Name="PlaceholderForYourContentAndBindings" Background="Red"/>
<theme:OnOff Grid.Column="2" Height="21" Width="38" HorizontalAlignment="Right" Grid.Column="1" Toggle="{Binding status , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MouseLeftButtonDown="OnOff_MouseLeftButtonDown" />
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want two fixed columns in the ListView, use a UniformGrid instead of a WrapPanel and remove the fixed sizes. With an ItemContainerStyle the items will then scale with the window.
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding StringItems}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" Margin="25,60,25,25" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border Padding="0,25" BorderThickness="0 0 0 1" BorderBrush="#3D3C44">
<Grid Background="black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="hello" Foreground="white"/>
<Border Grid.Column="1" x:Name="PlaceholderForYourContentAndBindings" Background="Red"/>
<theme:OnOff Grid.Column="2" Height="21" Width="38" HorizontalAlignment="Right" Grid.Column="1" Toggle="{Binding status , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MouseLeftButtonDown="OnOff_MouseLeftButtonDown" />
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Related

How can I make align all the textboxes align in same vertical position in wpf?

Hi i created lable's and text boxs in wpf but text boxes are started different position How can I make align all the textboxes align in same vertical position?
<DockPanel LastChildFill="True"
HorizontalAlignment="Stretch">
<Label DockPanel.Dock="Left"
Style="{DynamicResource EditorHLabelNoIdentStyle}" Content="{x:Static r:Resources.LABEL_POSITION}"/>
<TextBox ToolTip="{x:Static r:Resources.LABEL_POSITION_TIP}"
Style="{DynamicResource EditorTextBoxStyle}" Text="{Binding XPath=nameofthedataset, Mode=TwoWay}"/>
</DockPanel>
<DockPanel LastChildFill="True"
HorizontalAlignment="Stretch">
<Label DockPanel.Dock="Left"
Style="{DynamicResource EditorHLabelNoIdentStyle}" Content="{x:Static r:Resources.LABEL_POSITION}"/>
<TextBox ToolTip="{x:Static r:Resources.LABEL_POSITION_TIP}"
Style="{DynamicResource EditorTextBoxStyle}" Text="{Binding XPath=keywords, Mode=TwoWay}"/>
</DockPanel>
Use a Grid with two ColumnDefinitions and a RowDefinition for each row and then set the Grid.Row and Grid.Column attached properties
of each element to specify its position in the Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label DockPanel.Dock="Left" Style="{DynamicResource EditorHLabelNoIdentStyle}" Content="{x:Static r:Resources.LABEL_POSITION}"/>
<TextBox Grid.Column="1" ToolTip="{x:Static r:Resources.LABEL_POSITION_TIP}" Style="{DynamicResource EditorTextBoxStyle}"
Text="{Binding XPath=nameofthedataset, Mode=TwoWay}"/>
<Label Grid.Row="1" DockPanel.Dock="Left" Style="{DynamicResource EditorHLabelNoIdentStyle}" Content="{x:Static r:Resources.LABEL_POSITION}"/>
<TextBox Grid.Row="1" Grid.Column="1" ToolTip="{x:Static r:Resources.LABEL_POSITION_TIP}"
Style="{DynamicResource EditorTextBoxStyle}" Text="{Binding XPath=keywords, Mode=TwoWay}"/>
</Grid>
With a number of rows, setting labels or texblocks and textboxes to row and column quickly gets a bit tedious.
You could instead use standard template to line things up in a stackpanel.
A headeredcontentcontrol has a template with a stackpanel to put a header above it's content. This xaml re-templates to use a grid with two columns. The size of the first is shared across the scope of it's containing stackpanel.
You can also avoid repeating all your standard styling and whatnot by applying it in the template.
My markup uses simplified example controls for clarity.
Your textboxes ( or whichever control you wanted to label ) complete with binding, tooltip etc go in the content of a headeredcontentcontrol. The label gets it's value from the header property.
The shared size scope and auto size on the column means all the instances end up with the maximum necessary width a label in that stackpanel requests. So the right column of controls (textboxes) lines up.
<StackPanel Grid.IsSharedSizeScope="True">
<StackPanel.Resources>
<Style TargetType="HeaderedContentControl">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="L" Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{TemplateBinding Header}"
Margin="2,2,4,2"
/>
<ContentControl Content="{TemplateBinding Content}"
Grid.Column="1"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<HeaderedContentControl Header="Label 1:">
<TextBox Text="Some textbox"/>
</HeaderedContentControl>
<HeaderedContentControl Header="A much longer label:">
<TextBox Text="A second textbox"/>
</HeaderedContentControl>
</StackPanel>

Why my WPF Rectangle control is not filling all the empty space of StackPanel?

Learning basic concepts of WPF before moving to UWP. Following XAML in my WPF project is showing the windows as below.
I'm trying to display the Rectangle and Button on the right side of the StackPanel and need the Rectangle (not the Button) control to auto fill the StackPanel.
I tried the HorizontalAlignment="Stretch" with no Width attribute but without Width attribute the entire rectangle shrinks to 0 width. Don't want to hard code the width value (if possible) so that window of the app adjust itself depending on the device it's on (screen resolution). But if that scenario is still possible with hard coded width value as well please let me know that approach as well.
Window:
XAML:
Remark: I don't think ListBox is playing any role (related to this post). Only controls inside the ListItemsControl on above ListBox probably need proper adjustment. but I may be wrong.
<Window x:Class="WPFProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="376"
Width="337">
<Grid>
<ItemsControl>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Height="10">
<Rectangle x:Name="myRectangle" Fill="#FFF4F4F5" HorizontalAlignment="Right" Height="9" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.533,0.6"/>
<Button Content="" HorizontalAlignment="Right" Height="10" VerticalAlignment="Top" FontSize="5" FontWeight="Bold"/>
</StackPanel>
</ItemsControl>
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,11,0,81" ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="myList" SelectionChanged="myList_ContextMenuClosing">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="{Binding FirstName}" ToolTip="{Binding FullName}" Width="20" Height="20" Stroke="#FF211E1E" OpacityMask="Black" StrokeThickness="1" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="btnTest" Content="Test" HorizontalAlignment="Left" Margin="250,298,0,0" VerticalAlignment="Top" Width="75" Click="BtnTest_Click"/>
</Grid>
</Window>
Two things here:
When you use Stackpanel with horizontal orientation, horizontalalingment="stretch" can't be used. That is because all of the elements are being Stacked with their designed width.
You are specifying a fixed width of 100 for your rectangle. If you do that it will not stretch anymore even if you use stretch for alignment. Also the horizontalalingment="stretch" needs to be placed on the element you are expecting to stretch, not the Panel.
For things like this use DockPanelor a Grid instead.
Read more about WPF panels here:
https://wpf-tutorial.com/panels/introduction-to-wpf-panels/
Here is an example for Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="myRectangle" Fill="#FFF4F4F5" HorizontalAlignment="Stretch" Height="9" Margin="0,0,0,0"
Stroke="Black" VerticalAlignment="Top" RenderTransformOrigin="0.533,0.6" Grid.Row="0"/>
<Button Content="" HorizontalAlignment="Right" Height="10" VerticalAlignment="Top" FontSize="5" FontWeight="Bold" Grid.Column="1"/>
</Grid>
Notice the width="*" attribute means the cell will use all the remaining space. If you have multiple rows/columsn defined with * the space will be divided between them.
Stack Panel acts like a stack of things placed one after another. It can be horizontal or vertical. Unlike Grid you cannot access particular place in a stack panel, every next element will be placed after one another in a sequence.For your requirement a StackPanel is not suitable unless you need to have horizontal scrolling. You should try a DockPanel or Grid instead like
<Grid Height="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<!--first column of grid-->
<Rectangle Grid.Column="0" x:Name="myRectangle" Fill="#FFF4F4F5" Height="9" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Top" RenderTransformOrigin="0.533,0.6"/>
<!--second column of grid-->
<Button Grid.Column="1" Content="" HorizontalAlignment="Right" Height="10" VerticalAlignment="Top" FontSize="5" FontWeight="Bold"/>
</Grid>

Why are letters scaling to different sizes in comparable view boxes within WPF?

I am attempting to create an onscreen keyboard using a Grid for key layout. Each key consists of a Border with a TextBlock containing a letter. To make the letters scale I have wrapped each TextBlock in a ViewBox, for example;
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<TextBlock Text="a" />
</Viewbox>
</Border>
</Grid>
<Grid Grid.Row="0" Grid.Column="1">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<TextBlock Text="b" />
</Viewbox>
</Border>
</Grid>
<Grid Grid.Row="0" Grid.Column="2">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<TextBlock Text="c" />
</Viewbox>
</Border>
</Grid>
</Grid>
The problem is when you shrink the control by resizing the window horizontally (i.e. squash the borders together horizontally). As each letter has a slightly different width and height, the amount of zooming/scaling applied by each viewbox is not exactly the same. This results in the letters being rendered at different vertical heights, i.e. the "b" will be on a horizontal plane above the "a" and "c", which looks a little wrong.
The only work around I can think of (which works) relies on fixing the widths of each textblock, e.g. setting Width="10". This, however, feels unsatisfactory as it requires knowledge of the font which will be used to display each letter and an assumption about the maximum width. A middle ground would be to achieve this automatically the largest possible letter/glyph in each viewbox by including a hidden letter in each textblock;
<Grid Grid.Row="0" Grid.Column="0">
<Border BorderThickness="1" BorderBrush="Gray">
<Viewbox>
<Grid>
<TextBlock Text="a" />
<TextBlock Text="X" Visibility="Hidden" />
</Grid>
</Viewbox>
</Border>
</Grid>
I don't like that solution though and would love a reliable way to ensure all textblocks are the same size and so scale acceptably, without hard coding values or assumptions about the font.
Any ideas?
Thanks.
Using a uniform grid and binding to a primary textblock you get a pretty decent scaling:
<UniformGrid Rows="3" Columns="10">
<Viewbox>
<Border>
<TextBlock TextAlignment="Center"
Width="{Binding ActualWidth, ElementName=textBlock, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=textBlock, Mode=OneWay}">
Q
</TextBlock>
</Border>
</Viewbox>
<Viewbox>
<Border>
<TextBlock x:Name="textBlock" TextAlignment="Center">
W
</TextBlock>
</Border>
</Viewbox>
<Viewbox>
<Border>
<TextBlock Width="{Binding ActualWidth, ElementName=textBlock, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=textBlock, Mode=OneWay}"
TextAlignment="Center">
E
</TextBlock>
</Border>
</Viewbox>
<Viewbox>
<Border>
<TextBlock Width="{Binding ActualWidth, ElementName=textBlock, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=textBlock, Mode=OneWay}"
TextAlignment="Center">
R
</TextBlock>
</Border>
</Viewbox>
</UniformGrid>
You'll see each control is bound to the W key - assuming that is the biggest. If you are unsure, you can add a different element as hidden and bind to that - as you implied in your question. The important thing is that the grid sets the size of the main control element.
This MSDN question is answered correctly; http://social.msdn.microsoft.com/Forums/vstudio/en-US/c052fa89-4788-4d85-b266-fdd5c637a0ff/sharing-viewbox-zoom-level-between-items?forum=wpf
The solution relies on leveraging the SharedSizeGroup behaviour on a grid to ensure that the viewbox of every key is the same size as every other viewbox, like so;
<Viewbox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="col"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="row"/>
</Grid.RowDefinitions>
<TextBlock Text="a" />
</Grid>
</Viewbox>
Other solutions involving hard coding the width/height of the viewbox, or binding to a common viewbox and filling it with the largest possible glyph work, but are not perfect solutions. The above solution makes no assumptions and relies on built in WPF measure/arrange logic to produce the desired outcome.

I'm confused with Grid layout in ListBoxItem of WPF

I'm newer in wpf,recently i have a requirement need to create an listbox and when i practice in DataTemplate ,the issue comed.my layout code like below
<ListBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Left">
<ListBoxItem HorizontalAlignment="Stretch">
<Grid ShowGridLines="True" Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.ColumnSpan="2"
Width="200" Height="40" Fill="Gray"/>
</Grid>
</ListBoxItem>
</ListBox>
the begining,i set the width 100 on the first column,because the Rectange's width become to 200,so the first column width become more greater than 100,what happend when the grid to measue the child element?i am confused about it ,because i put the code in an commen state it run normally.any one tell me ,thanks

Radiobutton Even Horizontal Alignment

Is there a way to evenly layout radiobuttons including the radiobutton text? I have tried StackPanel with Orientation=Horizontal, DockPanel and UniformGrid but I have not achieved the exact look I am going for which is an even amount of white space between the controls without having to wrap or truncate the text.
<GroupBox Name="grpLegend" Header="{x:Static res:Strings.ChartOptionsDisplayControlView_GroupBox_Legend}">
<ItemsControl
ItemsSource="{Binding IsAsync=True, Path=AvailablePitchbookLegendPosition}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
Content="{Binding IsAsync=True, Path=DisplayName}"
IsChecked="{Binding IsAsync=True, Path=IsSelected}"
GroupName="LegendPosition"
Margin="2,3.5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</GroupBox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" Content="Left"/>
<RadioButton Grid.Column="1" HorizontalAlignment="Center" Content="Center"/>
<RadioButton Grid.Column="2" Content="Right"/>
</Grid>
If this Grid was part of a list's ItemTemplate and you wanted to synchronize the widths of the grid's columns you should use the SharedSizeGroup property.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c1"/>
<ColumnDefinition SharedSizeGroup="c2"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="c3"/>
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" Content="Left"/>
<RadioButton Grid.Column="1" HorizontalAlignment="Center" Content="Center"/>
<RadioButton Grid.Column="2" Content="Right"/>
</Grid>
and then on a suitable parent container use the attached property Grid.IsSharedSizeScope="true"
<ListBox Grid.IsSharedSizeScope="True" ItemTemplate={StaticResource RadioButtonTemplate}/>

Resources