I want to build a customcontrol where expandable rows are added to grid. In order to match the columns of the expandable row, I added another grid as expander header and content. However, the columns are slightly tilted to the right (see picture). I think it is because of the header size?
How do I access the width of the togglebutton code, so I can change the columndefinitions accordingly?
Thanks!
There are a few ways to approach this.
One would be trying to find the width of that togglebutton yourself by looking at the control template and compensating - but this is unreliable as the template can change across .NET versions (and operating systems if I recall correctly) and is messy to implement anyway.
The second way is trying to programatically find the width and compensate that way, but that still requires digging into the control template (see issues above)
The third is to let the layout engine figure things out for you. You can do this by sharing the column width on columns 2-4, and setting the width of column 1 to "*". Doing this initially will yield undesired results however, because the Expander Header template has no way to set the HorizontalContentAlignment.
With a little of the above mentioned risk, you can fix that by digging around in the template via codebehind, as described here But its still not perfect yet.
Looks like there is a border or margin somewhere. Lets set the BorderThickness of the expander to 0.
UGH! Better - (the whitespace is gone), but there is still a gap between our grid (red with black border) and the green in .NET 4.5. I could not find the source of this gap in a reasonable amount of time, so I just set the Margin of the expander to 0,0,-1,0. This might need to be compensated for inside the expander depending on content, and brings up suppressed memories of CSS hacks (XAML was suppose to be better, wasn't it?)
End Result:
XAML:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="800" SnapsToDevicePixels="True">
<Window.Resources>
<DataTemplate x:Key="StretchedHeaderTemplate">
<Border HorizontalAlignment="Stretch" Loaded="Border_Loaded">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</DataTemplate>
</Window.Resources>
<Grid Grid.IsSharedSizeScope="True" Margin="15">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="MainLeft"/>
<ColumnDefinition x:Name="MainRight"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Column="0" Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100" SharedSizeGroup="Col2"/>
<ColumnDefinition Width="100" SharedSizeGroup="Col3"/>
<ColumnDefinition Width="100" SharedSizeGroup="Col4"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="Black" BorderThickness="1">
<TextBlock>Col1</TextBlock>
</Border>
<Border Grid.Column="1" BorderBrush="Black" BorderThickness="1">
<TextBlock>Col2</TextBlock>
</Border>
<Border Grid.Column="2" BorderBrush="Black" BorderThickness="1">
<TextBlock>Col3</TextBlock>
</Border>
<Border Grid.Column="3" BorderBrush="Black" BorderThickness="1">
<TextBlock>Col4</TextBlock>
</Border>
</Grid>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">January</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">February</TextBlock>
<TextBlock Grid.Column="2" HorizontalAlignment="Center">March</TextBlock>
</Grid>
<StackPanel Grid.Column="0" Grid.Row="1">
<Expander HeaderTemplate="{StaticResource StretchedHeaderTemplate}" Background="LightGreen" BorderThickness="0" Margin="0,0,-1,0">
<Expander.Header>
<Grid HorizontalAlignment="Stretch" Background="LightCoral">
<Grid Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Col2"/>
<ColumnDefinition SharedSizeGroup="Col3"/>
<ColumnDefinition SharedSizeGroup="Col4"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="Black" BorderThickness="1">
<TextBlock>Record1</TextBlock>
</Border>
<Border Grid.Column="1" BorderBrush="Black" BorderThickness="1">
<TextBlock>02.05.2016</TextBlock>
</Border>
<Border Grid.Column="2" BorderBrush="Black" BorderThickness="1">
<TextBlock>05.05.2017</TextBlock>
</Border>
<Border Grid.Column="3" BorderBrush="Black" BorderThickness="1">
<TextBlock>340</TextBlock>
</Border>
</Grid>
</Grid>
</Expander.Header>
</Expander>
</StackPanel>
</Grid>
Code Behind:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Border_Loaded(object sender, RoutedEventArgs e)
{
Border root = (Border)sender;
ContentPresenter presenter = (ContentPresenter)root.TemplatedParent;
presenter.HorizontalAlignment = HorizontalAlignment.Stretch;
}
}
Conclusion: This works, but you might want to consider overriding the control template for the Expander if you find any of it too messy.
Related
Here is my Grid, no matter what I tried I can't get the VerticalScrollBar to show up.
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" Style="{StaticResource MaterialDesignScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
</Grid>
I need the Grid to be Stretched both Horizontally and Vertically so that it can dynamically resize itself.
I even tried setting the Height as Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=ActualHeight}" for both the Grid and ScrollViewer.
Don't enclose scroll viewer inside another grid.
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" Style="{StaticResource MaterialDesignScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
Using a window's SizeToContent="WidthAndHeight" property seems to break a grid's column and row definition's property SharedSizeGroup.
<Window x:Class="SizeTest.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"
SizeToContent="WidthAndHeight">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
</Grid.RowDefinitions>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="0" Grid.Row="0" >
<Button Width="100" Height="100" Margin="10" Content="A" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="1" Grid.Row="0" >
<Button Width="100" Height="200" Margin="10" Content="B" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="0" Grid.Row="1" >
<Button Width="200" Height="100" Margin="10" Content="C" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Grid.Column="1" Grid.Row="1" >
<Button Width="100" Height="100" Margin="10" Content="D" />
</Border>
</Grid>
</Window>
For example, the code above produces the following:
However, I would expect it to produce similar to
Where it sizes to the components but respects the shared group size.
Resizing instantly applies the shared group sizes but then you have to manually guess where the correct size is.
Is there any way to fix this?
If it makes any difference, I am using .NET 5.
Edit
Adding Grid.IsSharedSizeScope="True" to the window or the grid initially appears to solve the issue, but when resizing the window, the grid no longer fills the window.
I suspect that what is happening is that without IsSharedSizeScope, the columns / rows are only the same size due to the Column / Row definition saying width / height is * but when IsSharedSizeScope is true, then the shared size scope come into effect, but for some reason stop the grid resizing when there is space for it to grow.
Looks like you need to use UniformGrid
<UniformGrid Rows="2" Columns="2">
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="100" Height="100" Margin="10" Content="A" />
</Border>
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="100" Height="200" Margin="10" Content="B" />
</Border>
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="200" Height="100" Margin="10" Content="C" />
</Border>
<Border BorderThickness="1" BorderBrush="Black">
<Button Width="100" Height="100" Margin="10" Content="D" />
</Border>
</UniformGrid>
Using a window's SizeToContent="WidthAndHeight" property seems to break a grid's column and row definition's property SharedSizeGroup.
No, in order to take advantage of shared size groups, you have to set a scope for the shared size groups to apply. That is what you do with the IsSharedSizeScope property attached property, which is false by default, which means initially there is no scope defined on any element, so the shared size groups will not apply.
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
<ColumnDefinition Width="*" SharedSizeGroup="MainColumnWidth" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
<RowDefinition Height="*" SharedSizeGroup="MainRowHeight" />
</Grid.RowDefinitions>
<!-- ...other grid definitions. -->
</Grid>
For example, the code above produces the following: [...] Resizing instantly applies the shared group sizes but then you have to manually guess where the correct size is.
This is not related to shared size groups, but to the Grid itself. You can remove all the shared size definitions and it will yield the same result. In fact, the shared size definitions are redundant, as giving each column and cell a * size, will scale them in the same proportion. See the documentation for reference.
Star sizing is used to distribute available space by weighted proportions.
Unfortunately, I cannot tell why the inital size fits the Grid content exactly instead of respecting the star sizes. However, applying a MinWidth and MinHeight solves the issue, if applicable.
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="200" />
<ColumnDefinition Width="*" MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="200" />
<RowDefinition Height="*" MinHeight="200" />
</Grid.RowDefinitions>
<!-- ...other grid definitions. -->
</Grid>
This could be the same or a similar issue with star-sizing like in this related question.
[...] when IsSharedSizeScope is true, then the shared size scope come into effect, but for some reason stop the grid resizing when there is space for it to grow.
This is expected, since setting a shared size group will treat sizes differently.
Columns and rows that participate in size-sharing do not respect Star sizing. In the size-sharing scenario, Star sizing is treated as Auto.
Consequently, if you apply a *, the Grid will be measured and the size will remain even after resizing.
As you question suggests, you are dealing with uniform cells in your grid. In this case, you can circumvent these issues by simply using the UniformGrid control, which is dedicated for this scenario.
Provides a way to arrange content in a grid where all the cells in the grid have the same size.
<UniformGrid>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="100" Height="100" Margin="10" Content="A" />
</Border>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="100" Height="200" Margin="10" Content="B" />
</Border>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="200" Height="100" Margin="10" Content="C" />
</Border>
<Border BorderBrush="Black" BorderThickness="1">
<Button Width="100" Height="100" Margin="10" Content="D" />
</Border>
</UniformGrid>
I'm guessing there's not a control that does this automatically, but I'm looking for a way to implement something like this in a wpf application. Here's what I'd like for it to do.
Take commands into it like a cli (This could be a separate text box
if necessary).
Output normal text and support HTML colors.
History that scrolls up as your cursor moves down after each command.
HTML formatted divs, tables, and text formatting (no js needed)
Small images no larger than 100x100
How do I start making this? Do I start with a StackPanel?
I Added two rows with four colomns in a grid panel
In the first row fourth colomn add a stackpanel for normal text
Out side the grid you also add image as well as inside
That I include for your reference
The grid is nested in the scroll
<Window x:Class="Wpftest.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:Wpftest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ScrollViewer Margin="5" CanContentScroll="False"
HorizontalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="Augustus.jpg" Height="100" Margin="10"/>
<Label Grid.Column="1" Content="Augustus - 63BC - 14AD" />
<Image Grid.Column="2" Source="Tiberius.jpg" Height="100" Margin="10"/>
<Label Grid.Column="3" Content="Tiberius - 42BC - 37AD"/>
<StackPanel Grid.Row="0" Grid.Column="4">
<TextBlock FontSize="15">Your Normal text</TextBlock>
</StackPanel>
<Button Grid.Row="1" Content="Learn"
HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="10,5"/>
<Button Grid.Row="1" Grid.Column="2" Content="Learn"
HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="10,5"/>
<Button Grid.Row="1" Grid.Column="4" Content="Inputs"
HorizontalAlignment="Center" VerticalAlignment="Center"
Padding="10,5"/>
</Grid>
</ScrollViewer>
</Window>
I've started developing a new application in WPF C#. The problem I'm having is regarding grid column definitions.
I have divided my Window into separate sections, using Grids' row and col definitions (see code below).
I have two rows. First is currently empty, second one contains a new grid, that is further divided into columns.
All grids are inside a border.
The weird behavior I'm getting, is this section of the code:
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="1">
<Grid>
</Grid>
</Border>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="2">
<Grid>
</Grid>
</Border>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="3">
<Grid>
</Grid>
</Border>
The problem is, that the first "subgrid" actually corresponds to the
second column, The second and third both refer to the third column.
In other words, whenever I put any kind of control in the second subgrid (the one which has Grid.Column="2"), the control will appear in the third column for some reason.
I've been fiddling around with it, and can't seem to understand the behavior I'm getting.
Another example: I've put a control in the second column as following:
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="2">
<Grid>
<TextBox Width="100" Height="50"></TextBox>
</Grid>
</Border>
The image below represents what I got:
Whole XAML code:
<Border BorderThickness="1" BorderBrush="#ffcd22" Margin="10,10,10,10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border BorderThickness="1" BorderBrush="#ffcd22" Margin="-1,-1,-1,-1" Grid.Row="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="1">
<Grid>
</Grid>
</Border>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="2">
<Grid>
</Grid>
</Border>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="3">
<Grid>
</Grid>
</Border>
</Grid>
</Border>
</Grid>
The problem is that the column numeration starts from 0. You should change your code like this:
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="0">
<Grid>
</Grid>
</Border>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="1">
<Grid>
</Grid>
</Border>
<Border BorderThickness="1" BorderBrush="#ffcd22" Grid.Column="2">
<Grid>
</Grid>
</Border>
Problem: When adding a ScrollViwer around a Grid the Grid scaling is cancelled!
Eksampel:
I have created a grid width 3 columns, the 1. coulymn should always be 2 times larger than column 2 and 3!
Without the ScrollViewer this is always true, but when adding it it allows each column to decide its own size.
<Window x:Class="alternatingGridRow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="Auto" Loaded="WindowLoaded">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<Grid x:Name="LayoutRoot" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="23" MaxHeight="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Stretch" Text="sdasdasdasdsadsadasddasdck" TextWrapping="Wrap" VerticalAlignment="Top" />
<TextBlock Foreground="Red" Grid.Column="1" HorizontalAlignment="Stretch" Text="sdasdasdasdsadsadasddasdck" TextWrapping="Wrap" VerticalAlignment="Top" />
</Grid>
</ScrollViewer>
As you can clearly see the scaling factors are completely wrong! As the 2. column is way to large! and the 3. column is some random size...
Wrong Scaling factors
Any advice on this is well recieved....
Cheers Martin
You're asking the grid to assign a percentage of infinite space to each column. Infinite because horizontal scrolling is enabled on your ScrollViewer, and the whole point of ScrollViewers is to virtualize space. So what you're asking it to do doesn't even make sense.
Ok I see your point in why the column sizes a screwed.
But.. I thought of a solution as I read your posts...
As, Mohammed said, set a fixed width on my grid, well.. I want my grid to have same width as scrollviewer unless it gets to small, then I want the scrollviewer to take affect!
So.. my solution is:
MinWidth="500" Width="{Binding ActualWidth, ElementName=scrollviewer}"
<Window x:Class="alternatingGridRow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="Auto">
<ScrollViewer x:Name="scrollviewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<Grid x:Name="LayoutRoot" ShowGridLines="True" MinWidth="500" Width="{Binding ActualWidth, ElementName=scrollviewer}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="23" MaxHeight="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Stretch" Text="sdasdasdasdsadsadasddasdck" TextWrapping="Wrap" VerticalAlignment="Top" />
<TextBlock Foreground="Red" Grid.Column="1" HorizontalAlignment="Stretch" Text="sdasdasdasdsadsadasddasdck" TextWrapping="Wrap" VerticalAlignment="Top" />
</Grid>
</ScrollViewer>
</Window>
(Only fixed for horizontal)
Thx.
The current setup is wrong, because the ScrollViewer does not limit the width and height of its child (i.e. unlimited), moreover, the Grid always fills all the available horizontal and vertical space available on its parent container, and that is why you see this weird behavior. You have to do one of the followings:
either, remove the ScrollViewer as you mentioned.
or, set a fixed height and width for your Grid.