I have a window split up by grids (left center right), where the center control I want to have multiple controls that will need to fill their heights as tall as possible while splitting it evenly amongst the other controls.
One way I was able to achieve this is through a grid like so:
<!-- Center -->
<Grid Grid.Row="0" Grid.Column="2" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WebBrowser Grid.Row="0" Name="browsc" />
<WebBrowser Grid.Row="1" Name="browsa" />
<WebBrowser Grid.Row="2" Name="browsb" />
</Grid>
This works, however I need to add and remove rows dynamically, where it's possible a row in the middle will need to be removed (causing the need to reorder the rows each control rests on which I can't imagine a simple solution for).
If there isn't a better way to split the controls than this, how would I add and remove rows via code (C#)?
If there's a better way to do it, how can I do this where all I have to worry about are adding and removing the controls themselves and not mess with row properties?
Thanks!
This is an example on how to add new RowDefinition to Grid programmatically and set control to a specific Grid Row :
//following line equal to XAML : <RowDefinition Height="*" />
var newrow = new RowDefinition {Height = new GridLength(1, GridUnitType.Star)};
//add new rowdefinition to grid
myGridName.RowDefinitions.Add(newrow);
//set webbrowser control to newly added row, or any row number you wish
Grid.SetRow(browsx, myGridName.RowDefinitions.Count-1);
You can access any RowDefinition from myGridName.RowDefinitions property to delete it later. But a better idea is to set RowDefinition's Height to zero instead of delete it. With that you don't have to rearrange other controls, moving control in row 3 to row 2 for example because the previous row 2 has been deleted.
This seems like a good case for UniformGrid:
<UniformGrid Grid.Row="0" Grid.Column="2" Columns="1">
<WebBrowser Name="browsc" />
<WebBrowser Name="browsa" />
<WebBrowser Name="browsb" />
</UniformGrid>
UniformGrid ensures that all items have the same size, both width and height. In your case you only want to constrain height, but since there is only one column, all items must have the same width anyway, so this is okay.
Instead of setting up rows, we can just set Columns="1" on the UniformGrid and it will auto-arrange each item on a new row. If you add or remove items, or toggle their Visibility between Visible and Collapsed, all the sizes will be adjusted to fit the space.
Another possibility still using a grid is to bind the height to a converter that uses the visibility of the control it contains to decide on the layout.
Here's an example:
In the XAML file:
<Grid Grid.Row="0" Grid.Column="2" Height="Auto" xmlns:local="clr-namespace:WpfApplication2">
<Grid.Resources>
<local:RowHeightConverter x:Key="RowHeightConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding ElementName=browsc, Path=IsVisible, Converter={StaticResource RowHeightConverter}}" />
<RowDefinition Height="{Binding ElementName=browsa, Path=IsVisible, Converter={StaticResource RowHeightConverter}}" />
<RowDefinition Height="{Binding ElementName=browsb, Path=IsVisible, Converter={StaticResource RowHeightConverter}}" />
</Grid.RowDefinitions>
<WebBrowser Grid.Row="0" Name="browsc"></WebBrowser>
<WebBrowser Grid.Row="1" Name="browsa" Visibility="Collapsed"></WebBrowser>
<WebBrowser Grid.Row="2" Name="browsb"></WebBrowser>
</Grid>
You can also move the 'xmlns bit up to a top-level window or you may already have it.
In the code behind:
public class RowHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var isVisible = value as bool?;
if (isVisible.HasValue && isVisible.Value)
return new GridLength(1, GridUnitType.Star);
else
return new GridLength(0);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Once you've built the code once, you can change the 'Visibility' of the browsers in the XAML designer and see the changes reflected in the layout.
Related
I have a stackpanel in a grid row with the row height set to auto.
I add user controls at runtime and the height resizes fine, when removing the user controls the height does not reduce though. I have tried to clear the stackpanel children, remove them one by one and also implemented IDisposable in each user control but when the child count shows zero the height has not reduced.
Sample XAML below, any help would be welcomed please?
<Grid x:Name="TestGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Test Header" Style="{StaticResource SubHeaderTextBlock}" />
</StackPanel>
<StackPanel x:Name="ChildItems" Grid.Row="1" />
</Grid>
Replace the StackPanel with a Grid. Grids stretch and retract better than StackPanels.
Instated of removing put Visibility to Collapsed
I have a grid with three differents controls, two are groupbox, and a third one is a treeview.
I don't know the initial width so the Window.SizeToContent is set to "Width"
Everything perfectly fits inside the window, the problem came when I extend a node from the TreeView, the Window.Width adjust to the new Width where the treeView fits perfect but the rest of the controls are not that nice.
I would like to keep the "original" width given by the SizeToContent, and get a ScrollBar when I extend the treeViewNode instead changing the whole Window.Width
Here is the xaml (just the main grid and the treeview)
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Attachments-->
<GroupBox x:Name="gbAttachments" Header="Attachments" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HorizontalContentAlignment="Stretch">
<TreeView x:Name="tvAttachments" BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Visible" />
</GroupBox>
<!--Attachments-->
</Grid>
I have try with ColumnDefinition.Width = "*", HorizontalContentAlignment, and a lot of others differents ways, I have also tried to put it inside a ScrollbarViewer and nothing seems to work. Always when I extend the node, the whole window is extended with the content of the node (that it is actually quite long). I'm pretty sure that the problem is the Window.SizeToContent = 'Width' but what else I can do for the rest of the controls if I don't know the original width? Any Idead, Thanks in advance!
--EDIT
Well the solution was easier than expected, in the event Window_Loaded I get the gbAttachments.ActualWidth and assign it to the TreeView.MaxWidth
Not beatiful, pretty sure it can be done by xaml, but it is working.
You could try setting the MaxWidth property of the nodes in your tree:
<Style TargetType="TreeViewItem">
<Setter Property="MaxWidth" Value="80"></Setter>
</Style>
Alternatively, you can bind this value to something, such as another element for example:
<Style TargetType="TreeViewItem">
<Setter Property="MaxWidth"
Value="{Binding ElementName=gbAttachments, Path=ActualWidth}"></Setter>
</Style>
Also, if you're concerned about not needing the window's autosizing to occur after it's initialized, you could try something in code behind so that the autosizing only happens on window initialization:
public MainWindow()
{
InitializeComponent();
// auto-size width, and save off value
this.SizeToContent = System.Windows.SizeToContent.Width;
var actualWidth = this.ActualWidth;
// manual-size width
this.SizeToContent = System.Windows.SizeToContent.Manual;
// set value to what it was, when auto-sized
this.Width = actualWidth;
}
Whenever Expander is expanded I would like to shrink grid cell above containing ListBox, so that you can always access every ListItem(if the listbox's grid cell would not shrink, lowest part would be inacessible). To illustrate:
item item *scrollbar*
item -> item *scrollbar*
item expanderItems
expander expander
I found bunch of threads for resizable expander, but none mentioning resizing other content. The problem is, grid containing listbox in 1st row and expander in 2nd with 2nd row Height set to Auto do not resize itself when expander is expanded.
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
I managed to code an ugly workaround using Expanded/Collapsed events:
private void Expander_Expanded(object sender, System.Windows.RoutedEventArgs e)
{
//grid1.Height is named content of expander
this.LayoutRoot.RowDefinitions[1].Height = new GridLength(this.LayoutRoot.RowDefinitions[1].ActualHeight + grid1.Height, GridUnitType.Pixel);
this.LayoutRoot.RowDefinitions[0].Height = new GridLength(1, GridUnitType.Star);
}
What would be the proper way? Preferably with no code behind and more "automated".
EDIT: xaml
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Expander Header="Expander" Margin="0" Grid.Row="1" VerticalAlignment="Bottom" Height="23" Panel.ZIndex="1" ExpandDirection="Up" Grid.RowSpan="2" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed">
<Grid x:Name="grid1" Background="#FF762CF7" Height="100" Margin="0,-100,0,0"/>
</Expander>
<ListBox Margin="0" Background="#19FFFFFF">
<Button Height="150" Width="100"/>
<Button Height="150" Width="100"/>
<Button Height="150" Width="100"/>
</ListBox>
<Grid Margin="0" Grid.Row="1" Background="#FFAEFFAE"/>
<Grid Margin="0" Background="#FFFFD8D8"/>
</Grid>
The main problem you have is setting the Height property of the Expander. It changes its height when you expand it. But if you are setting it, it has to respect the height you gave it and cannot properly expand.
Instead of setting the Height, I would set the MinHeight (or completely remove height constraints, depending on what you want to do).
Additionally you should remove the Margin of the grid inside the expander.
Please help me in this issue of scroll bar visibility in WPF listview.
I have a listview inside a Content Control.
This Content Control is inside a User Control.
This User Control is inside a TabItem.
The listview has around 12 columns to display, which exceeds the window width.
I tried so many ways to show the horizontal scroll bar in the listview.
Below shown is the XAML of the Outer UserControl [width is not set for this outer usrCrtl]
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" /> // Here I have a custom content control
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<MyCustomContentControl Grid.Row=1 VerticalAlignment="Stretch"......>
<TabControl>
<TabItem Header="One" Name="Tab1">
<my:usrAControl /> // I have listview inside this userctrl
</TabItem>
</TabControl>
<TabControl Header="Two" Name="Tab2" />
</MyCustomContentControl>
</Grid>
Now below is the usrAControl XAML details
<UserControl x:Class="MyProject.MyModule.usrAControl"
MinWidth="640">
// Again inside another custom user control as its child.
<usrBControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" /> // here another headers
<RowDefinition Height="*" /> // here my listview placed
</Grid.RowDefinitions>
<ListView ScrollViewer.HorizontalScrollBarVisibility="Auto" Grid.Row="1"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=Width}">
// Around 12 columns which exceeds window width
</ListView>
</Grid>
</usrBControl>
</usrAControl>
I tried with lot of combination.
I initially put a scrollviewer control inside the tabitem and placed usrAControl inside it.
But it did not work.
But I want the listview should show its both scroll bars. Is any way to do it.?
Without seeing more code, my guess would be that the MinSize="640" is your problem: the ListView gets enough space from its container so it doesn't show the scroll, but the container gets clipped.
And you should get rid of the ListView Width binding, it's completely redundant.
i want to add squares to a panel and have them wrapped like the wrap panel.
I then want to make each square horizontally resizable individually, but when it is resized vertically, i need it to affect all the items in it's row.
Basically, I'd want all items in a row to always share the same height, but give the user a method of choosing this height (of course, each row can have its own height, and when squares wrapped to a new row they will need to inherit the new height).
Btw, those "squares" are just user controls or data template applied to a listbox items source. Can i use the same binding on a wrap panel, ad maybe i need to choose a different solution?
Thank you
you can try and put each "rectangle" in a Grid with one row and one column, and then use SharedSizeGroup on the RowDefinition. Be sure to also put Grid.IsSharedSizeScope="True" on the container:
<WrapPanel Grid.IsSharedSizeScope="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="Group1" />
</Grid.RowDefinitions>
<Button Height="40" Content="Hello" />
</Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="Group1" />
</Grid.RowDefinitions>
<Button Content="Hello2" />
</Grid>
</WrapPanel>