WPF - dynamic width of group box in grids - wpf

I have a row on a xaml pages with three group boxes in it.
Here is the code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<GroupBox Grid.Column="0" Grid.Row="0">
<ScrollViewer>
<TextBlock TextWrapping="WrapWithOverflow" Text="one"/>
</ScrollViewer>
</GroupBox>
<GroupBox Grid.Column="1" Grid.Row="0">
<ScrollViewer>
<TextBlock TextWrapping="WrapWithOverflow" Text="two"/>
</ScrollViewer>
</GroupBox>
<GroupBox Grid.Column="2" Grid.Row="0">
<ScrollViewer>
<TextBlock TextWrapping="WrapWithOverflow" Text="three"/>
</ScrollViewer>
</GroupBox>
</Grid>
The three group boxes have long text that wraps and had a scroll bar. The issue is that not all of these groupboxes will be displayed at the same time. Sometimes it will be just one, two or all.
When put the columns to auto the first one just takes over, and the same happens when I use a dockpannel or a stackpannel.
My question is how can I have the width change to take up the full width of the page if the second or third groupboxes have collapsed visibility.
Thanks!

you can use UniformGrid with aa single row. it will adjust column width according to number of children
<UniformGrid Rows="1">
<GroupBox>
<ScrollViewer>
<TextBlock TextWrapping="WrapWithOverflow" Text="one"/>
</ScrollViewer>
</GroupBox>
<GroupBox>
<ScrollViewer>
<TextBlock TextWrapping="WrapWithOverflow" Text="two"/>
</ScrollViewer>
</GroupBox>
<GroupBox>
<ScrollViewer>
<TextBlock TextWrapping="WrapWithOverflow" Text="three"/>
</ScrollViewer>
</GroupBox>
</UniformGrid>

Related

WPF ListBox Header And Data Alignment

I am trying to align data with the headers in a WPF application. The headers a line up and spaced nicely. However, I cannot get the data items underneath to line up with the headers. Any suggestions?
I have been poking around a bit, but have not found a solution to my problem. I do have to stick with list box as it is part of the requirements. Also, I am new to WPF.
Here is what the output is:
And here is the xaml that I am using:
<Grid Grid.Row="2">
<ListBox ItemsSource="{Binding MyData}">
<ListBox.Template>
<ControlTemplate>
<StackPanel>
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" x:Name="ToteNumber"></ColumnDefinition>
<ColumnDefinition Width="*" x:Name="Desription"></ColumnDefinition>
<ColumnDefinition Width="*" x:Name="Time"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Tote Number" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Description" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="Time" HorizontalAlignment="Center"/>
</Grid>
<ItemsPresenter></ItemsPresenter>
</StackPanel>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Border BorderThickness="1" BorderBrush="Black">
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Property1}" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="1" Text="{Binding Property2}" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="2" Text="{Binding Property3}" HorizontalAlignment="Stretch"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You need to set Grid.IsSharedSizeScope="True" on a parent panel and you need to identify the SharedSizeGroup on each column so it knows which column in your listbox template grid corresponds to which one in your itemtemplate.
Note that I think * width is treated as Auto when you apply Sharedsizegroup.
I use width auto and minwidth instead when I've done something similar.
You may find you need to bind width of each column to some parent measuring grid or calculate minwidth using a converter based on ViewportWidth of the scrollviewer in your listbox.
<Grid Grid.Row="2" Grid.IsSharedSizeScope="True">
<ListBox ItemsSource="{Binding MyData}">
<ListBox.Template>
<ControlTemplate>
<StackPanel>
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="A" x:Name="ToteNumber"></ColumnDefinition>
<ColumnDefinition Width="*" SharedSizeGroup="B" x:Name="Desription"></ColumnDefinition>
<ColumnDefinition Width="*" SharedSizeGroup="C" x:Name="Time"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Tote Number" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Description" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="Time" HorizontalAlignment="Center"/>
</Grid>
<ItemsPresenter></ItemsPresenter>
</StackPanel>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Border BorderThickness="1" BorderBrush="Black">
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="A"></ColumnDefinition>
<ColumnDefinition Width="*" SharedSizeGroup="B"></ColumnDefinition>
<ColumnDefinition Width="*" SharedSizeGroup="C"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Property1}" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="1" Text="{Binding Property2}" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="2" Text="{Binding Property3}" HorizontalAlignment="Stretch"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As I mentioned, I use a similar technique in some of my own markup. For that I put the headers grid and then the listbox with an itemtemplate in a stackpanel.
<StackPanel>
<Grid>
Headers
<ListBox>
ItemTemplate
</StackPanel>
Sounds like that doesn't suit whatever is driving your requirements though.

How to center/stretch textbox or stackpanel inside dockpanel?

I'm already tired. I've tried all the ways. I need my text to be in the middle.
Before that, I centered the text by creating a separate DockPanel for it. Now I can’t do this because my interface structure is collapsing
I need center Text
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="1" Grid.Column="0">
*** MY OTHER CODE ****
</DockPanel>
<DockPanel Grid.Column="0" Background="#FF79A6A6" Name="mainMenuName">
*** MY OTHER CODE ****
</DockPanel>
<DockPanel LastChildFill="True" Grid.Column="1" Name="topMenu">
<TextBlock Text="SomeText" HorizontalAlignment="Center"/>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal">
<TextBox x:Name="Search" />
<Button Width="50" Name="SearchBtn" Content="Search" HorizontalAlignment="Center"></Button>
</StackPanel>
</DockPanel>
<DockPanel Name="mainContent" Background="Black" Grid.Row="1" Grid.Column="1">
*** OTHER CODE ***
</DockPanel>
</Grid>
emoacht
There is no point to use DockPanel if you don't set DockPanel.Dock attached property. Move the TextBlock below of the StackPanel and then set DockPanel.Dock="Right" at that StackPanel.

Two groupboxes in dockpanel, how to set resizing (strech & fixed) correctly?

I have a very basic layout, but still not able to get the behaviour I want. Stupid me...
My grid has two columns, dynamic sized column left and fixed sized column right. This is working. Inside the right column I have stackpanel containing two buttons, they follow the window resizing correctly.
Inside the left column I have dockpanel containing two groupboxes, the lower has fixed height and is docked to the bottom. This groupbox follows the window resizing correctly, just like I want.
But I'm not able to get the upper groupbox to fill the upper section of the dockpanel. I can only set its height as fixed or when setting it "Auto" it gets strange height of 23...? I want it to fill the area and follow window resizing. I tried using stackpanel also in this column, but no success.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="220"/>
</Grid.ColumnDefinitions>
<DockPanel x:Name="GroupPanel" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<GroupBox x:Name="AlarmGroup" Header="Alarms" Margin="10" DockPanel.Dock="Top" />
<GroupBox x:Name="LogGroup" Header="Log" Height="188" Margin="10" VerticalAlignment="Bottom" />
</DockPanel>
<StackPanel x:Name="ButtonPanel" Width="190" Grid.Column="1">
<Button x:Name="StartButton" DockPanel.Dock="Right" Width="150" Height="40" VerticalAlignment="Top" Margin="0,20,10,0">Start</Button>
<Button x:Name="StopButton" DockPanel.Dock="Right" Width="150" Height="40" VerticalAlignment="Top" Margin="0,10,10,0">Stop</Button>
</StackPanel>
</Grid>
By default, DockPanel fills its remaining space with its last child.
You've set the AlarmGroup GroupBox as the first child, so it takes up only the space it needs; it's default. The second child has a fixed height, so it does not take up the remainder of the space.
To achieve the layout you are looking for, move LogGroup to be the first child of GroupPanel and set its DockPanel.Dock property to Bottom.
Example
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="220"/>
</Grid.ColumnDefinitions>
<DockPanel x:Name="GroupPanel">
<GroupBox x:Name="LogGroup" Header="Log"
DockPanel.Dock="Bottom"
Height="188" Margin="10"/>
<GroupBox x:Name="AlarmGroup" Header="Alarms"
DockPanel.Dock="Top"
Margin="10"/>
</DockPanel>
<StackPanel x:Name="ButtonPanel"
Width="190"
Grid.Column="1">
<Button x:Name="StartButton"
Width="150" Height="40"
VerticalAlignment="Top"
Margin="0,20,10,0">Start</Button>
<Button x:Name="StopButton"
Width="150" Height="40"
VerticalAlignment="Top"
Margin="0,10,10,0">Stop</Button>
</StackPanel>
</Grid>
Result
Is this working for you ?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="220"/>
</Grid.ColumnDefinitions>
<DockPanel x:Name="GroupPanel" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<GroupBox x:Name="LogGroup" Header="Log" Height="188" Margin="10" DockPanel.Dock="Bottom"/>
<GroupBox x:Name="AlarmGroup" Header="Alarms" Margin="10" DockPanel.Dock="Top" />
</DockPanel>
<StackPanel x:Name="ButtonPanel" Width="190" Grid.Column="1">
<Button x:Name="StartButton" DockPanel.Dock="Right" Width="150" Height="40" VerticalAlignment="Top" Margin="0,20,10,0">Start</Button>
<Button x:Name="StopButton" DockPanel.Dock="Right" Width="150" Height="40" VerticalAlignment="Top" Margin="0,10,10,0">Stop</Button>
</StackPanel>
</Grid>
In a DockPanel there is a property LastChildFill set to true by default which means that the last coltrol you put will take all the space. I also changed VerticalAligment="Bottom" to DockPanel.Dock="Bottom"
Depending on the screen size, Log and Alarm screens get smaller equally. I tried to do it by dividing it into partitions in a Grid. Is it enough for you ?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="220"/>
</Grid.ColumnDefinitions>
<Grid x:Name="GroupPanel" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="10"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<GroupBox x:Name="LogGroup" Header="Log" Grid.Column="0" Grid.Row="0" Margin="10"/>
<GroupBox x:Name="AlarmGroup" Header="Alarms" Grid.Column="0" Grid.Row="2" Margin="10"/>
</Grid>
<StackPanel x:Name="ButtonPanel"
Width="190"
Grid.Column="1">
<Button x:Name="StartButton"
Width="150" Height="40"
VerticalAlignment="Top"
Margin="0,20,10,0">Start</Button>
<Button x:Name="StopButton"
Width="150" Height="40"
VerticalAlignment="Top"
Margin="0,10,10,0">Stop</Button>
</StackPanel>
</Grid>

How to align to a control in the same screen?

In XAML, we typically have <Grid> layout which contains different elements. How do I align a control in one cell of grid to a control in a different cell like below?
(This used to be rather common in traditional applications where controls maybe in different group boxes etc but we still want to align them horizontally in one plane)
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column ="2">
<RadioButton Content="Option1"/>
</StackPanel>
</Grid>
The result is below which is ugly:
In this case, I just want to option1 to aligned centered with the TextBox (which does have custom height).
I can use margins to bring it to the desired position but that's kind of hard coded and not too WPFish.
Should I use binding to tie them directly? Is there a better way? Another way I can think of is to keep making grids within grids but seems like that will over complicate for this simple thing?
Try putting them both in another Grid or a horizontal StackPanel, and put that in one of the parent Grid cells.
The following did the trick, basically wrap the <RadioButton> around in <Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<Border BorderBrush="{x:Null}" Height="50">
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</Grid>
This answer helped.
Now the result is:
Three variants.
The first - I will supplement #zar: I use size binding instead of explicitly assigning a value to the size.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<Border BorderBrush="{x:Null}" Height="{Binding ActualHeight, ElementName=name, Mode=OneWay}">
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</Grid>
Second - I am implementing #Mark Feldman proposal: Delete StaskPanel and add lines to the grid.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="name" Height="50"/>
<Label Content="John"
Grid.Row="1"/>
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center"
Grid.Column="1"/>
</Grid>
The third - analogous to the first, but without the Border.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0">
<TextBox x:Name="name" Height="50"/>
<Label Content="John"/>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<RadioButton Content="Option1" Margin="10,0,0,0" VerticalAlignment="Center" VerticalContentAlignment="Center"
Height="{Binding ActualHeight, ElementName=name, Mode=OneWay}"/>
</StackPanel>
</Grid>

TextBlock Wrapping Property Doesn't Work

In the following XAML, I am trying to wrap the TextBlock which binds to "PortfolioCodes" and "CommentaryText" but it seems that "Wrapping" doesn't work for TextBlock. I tried every possible suggestion I could find on this web site but all in vain. Can someone please help.
<Grid>
<ListBox ItemsSource="{Binding Path=Summaries}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" BorderBrush="LightGray" BorderThickness="1" Padding="4" Margin="4">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="15"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">No Of Security</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding Path=PortfolioSecurityCount}"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1">Portfolio Code(s)</TextBlock>
<Grid Grid.Column="2" Grid.Row="1" >
<TextBlock TextWrapping="Wrap" Text="{Binding Path=PortfolioCodes}"></TextBlock>
</Grid>
<TextBlock Grid.Column="0" Grid.Row="2">Commentary Text</TextBlock>
<Grid Grid.Column="2" Grid.Row="2" >
<TextBlock Grid.Column="2" Grid.Row="2" TextWrapping="Wrap" Text="{Binding Path=CommentaryText}"></TextBlock>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Based on Guge response, I have changed xaml as below and now its working.
<Grid x:Name="LayoutRoot">
<ListBox x:Name="SummaryListBox" ItemsSource="{Binding Path=Summaries}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" BorderBrush="LightGray" BorderThickness="1" Padding="4" Margin="4">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="15"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">No Of Security</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding Path=PortfolioSecurityCount}"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1">Portfolio Code(s)</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=PortfolioCodes}" ></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="2">Commentary Text</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="2" TextWrapping="Wrap" Text="{Binding Path=CommentaryText}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Change the width of your third ColumnDefinition from "Auto" to "*", that way it only takes up whatever is left of your horizontal space.
To try to explain this:
Screen area in WPF is distributed in a two pass algorithm. First each visual element asks each child how much space it needs, with an indication of how much is available. These children do the same for their children.
Then each visual element tells each child how much they are actually going to get. These children, again, do the same for their children.
Your code failed to do what you wanted because the Grid in the DataTemplate told its third column children they could have all the horizontal space they wanted ("auto") in the first run. Those textboxes then thought that they wouldn't have to wrap. So they just reported back their desired width, and one line worth of height.
In the second run the Grid found that "auto" turned out to be a little less than what those children wanted. And the Grid still only gave them one line worth of height, so wrapping was out of the question. The children then had no other option left to them but to truncate the text.
When the third column width is set to "*", the grid will tell the children in the that column exactly how many horizontal pixels are left after the first column got their "auto" and the second column got their 15. Now the textboxes can figure out that they may want to wrap, and they report back "Ok, Dad, I'll make do with those measly horizontal pixels, but at least give me what I want in verticals". There is no limit to the vertical space, so they get what they need to present all their glorious content.
Try to give width to your TextBlock, by default TextBlock takes all available space and doesn't wrap text

Resources