Grid rows - fill all available space - wpf

I would like to put two controls in WPF in a Grid - GridView and ScheduleView one above the other with GridSplitter like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<telerik:RadScheduleView Grid.Row="0" />
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="4"/>
<telerik:RadGridView Grid.Row="2"/>
</Grid>
The problem is that there are three modes:
- only GridView is shown
- only ScheduleView is shown
- GridView and ScheduleView are shown
In each case I want the visible control(s) to fill all available space. In case the two are shown I want them to share the space between them and GridSplitter should be able to resize that space.
How can I accomplish this without explicitly setting heights while changing display modes?

I would suggest doing something like a MathConverter to explicitly set your Grid's heights based on some triggers
For example, if both Grids are visible, set their height to to be ((GridHeight - 4) / 2), while if only one grid is visible then set it to the Grid's full height since neither the GridSplitter nor the other Grid are visible.
Here's an example. I left out the visibility triggers since I'm assuming you already know how to implement them.
<Grid x:Name="ParentGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<telerik:RadScheduleView x:Name="MyGridView" Grid.Row="0">
<telerik:RadScheduleView.Style>
<Style TargetType="telerik:RadScheduleView">
<Setter Property="Height"
Value="{Binding ElementName=ParentGrid,
Path=ActualHeight,
Converter={StaticResource MathConverter},
ConverterParameter=((#VALUE-4)/2)}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyScheduleView, Path=IsVisible}" Value="False">
<Setter Property="Height" Value="{Binding ElementName=ParentGrid, Path=ActualHeight}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadScheduleView.Style>
</telerik:RadScheduleView>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="4"/>
<telerik:RadGridView x:Name="MyScheduleView" Grid.Row="2"
<telerik:RadScheduleView.Style>
<Style TargetType="telerik:RadScheduleView">
<Setter Property="Height"
Value="{Binding ElementName=ParentGrid,
Path=ActualHeight,
Converter={StaticResource MathConverter},
ConverterParameter=((#VALUE-4)/2)}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyGridView, Path=IsVisible}" Value="False">
<Setter Property="Height" Value="{Binding ElementName=ParentGrid, Path=ActualHeight}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadScheduleView.Style>
</telerik:RadScheduleView>
</Grid>

Simply give the height of two rows as * which means it will fill all the available space.
And bind the GridSplitter with the visibility of two columns using Triggers or Converter whichever you feel comfortable with. With trigger this would work-
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="4" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<telerik:RadScheduleView Grid.Row="0" />
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch">
<GridSplitter.Style>
<Style TargetType="GridSplitter">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btn1, Path=IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=btn2, Path=IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</GridSplitter.Style>
</GridSplitter>
<telerik:RadGridView Grid.Row="2"/>
</Grid>
I am assuming that Visibility for your GridView's are already in place (the logic of how to play with the visibility). Make sure you set the Visibility to Collapsed instead of Hidden for your GridViews.

Related

Grow-Only GridSplitter

I have a pretty simple requirement that I can't seem to find a solution for. Take a look at my sample XAML code:
<Grid ShowGridLines="True" VerticalAlignment="Top" Margin="5">
<Grid.RowDefinitions>
<RowDefinition MinHeight="100"/>
<RowDefinition MinHeight="100"/>
<RowDefinition MinHeight="100"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="GridSplitter">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Height" Value="1"/>
<Setter Property="Background" Value="Black"/>
</Style>
</Grid.Resources>
<GridSplitter/>
<GridSplitter Grid.Row="1"/>
<GridSplitter Grid.Row="2"/>
</Grid>
I have three rows that start out with a height of 100. I want the user to be able to drag the edge of any row to make it taller, so I put a GridSplitter in every row.
The problem is:
The GridSplitter control redistributes space between rows or columns
in a Grid, without changing the dimensions of the Grid. For example,
when a GridSplitter resizes two columns, the ActualWidth property of
one column is increased while at the same time the ActualWidth
property of the other column is decreased by the same amount.
The above XAML doesn't work, none of the rows change size when dragged, because the GridSplitter is trying to take height from another row and add it to the one being resized. Since none of the rows can get any smaller (MinHeight="100"), nothing happens.
But I don't want to take height from the other rows. I want to increase the size of one row independently, which will in turn change the overall height of the Grid. If I drag the middle row to be 50px taller, I want to have that row be 150px while the other two remain 100px, making the overall grid 350px.
Is there any setting on the GridSplitter I'm missing that will allow this? Or is there some other control I can use?
Is this what you're expecting?
<StackPanel>
<TextBlock Text="{Binding ActualHeight,
ElementName=grid,
StringFormat='Grid Actual Height: {0}'}" FontSize="30"/>
<Grid x:Name="grid" Background="Aqua">
<Grid.Resources>
<Style TargetType="GridSplitter">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Height" Value="5"/>
<Setter Property="ShowsPreview" Value="False"/>
<Setter Property="Background" Value="Black"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Text"
Value="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=StackPanel},
StringFormat='Row Actual Height: {0}'}"/>
<Setter Property="FontSize" Value="30"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition MinHeight="100" Height="100" />
<RowDefinition Height="Auto" />
<RowDefinition MinHeight="100" Height="100" />
<RowDefinition Height="Auto" />
<RowDefinition MinHeight="100" Height="100" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="Tan">
<TextBlock />
</StackPanel>
<GridSplitter Grid.Row="1"/>
<StackPanel Grid.Row="2" Background="Brown">
<TextBlock />
</StackPanel>
<GridSplitter Grid.Row="3"/>
<StackPanel Grid.Row="4" Background="Bisque">
<TextBlock />
</StackPanel>
<GridSplitter Grid.Row="5" ResizeBehavior="PreviousAndCurrent"/>
</Grid>
</StackPanel>
The problem is with the way you're adding GridSplitter to your Grid control. You can get more detail on how this works at GridSplitter documentation. This also includes how to use ResizeBehavior; which i'd used to get it working for last row

Change the width of the scrollbars in a scrollviewer on MousOver

I try to style a ScrollViewer so that the width of the ScrollBar element is changed on a MouseOver. I can style the ScrollViewer and give it an initial width, and it is also possible to change properties on a MouseOver, like the Opacity or Visibility. But I can't change the Width, that property is not changed.
I use this code as an example:
<Style x:Key="myScrollBarStyle" TargetType="{x:Type ScrollBar}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.9" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Opacity" Value="0.4" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="myStyle" TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.ColumnSpan="2" Grid.RowSpan="2"/>
<ScrollBar
Style="{StaticResource myScrollBarStyle}"
Name="PART_VerticalScrollBar"
Grid.Column="1"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
<ScrollBar
Style="{StaticResource myScrollBarStyle}"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Opacity is changed on a MouseOver but if I add a Width, it is ignored. If I look at ControlTemplate.Triggers then I can change the width on MouseOver but that is the width of the complete ScrollViewer, not the ScrollBar element. Even if I point to the element with the targetname PART_VerticalScrollBar.
Searching the internet only results in solutions to change the visibility or opacity, not the width. I could not find any solution.
Is there a solution to do this in a style?

How to dynamically change the ControlTemplate based on a property in a ViewModel?

I want to select dynamically a ControlTemplate based on a Property of a ViewModel. How do I achieve it.
I have 2 ControlTemplates in the View, and a boolean property on a ViewModel. Based on that property, I have to select and display one of my ControlTempale in the View.
<Window.Resources>
<ControlTemplate x:Key="simpleErrorTemplate">
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T1" />
</ControlTemplate>
<ControlTemplate x:Key="detailedErrorTemplate">
<StackPanel>
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T2" />
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T3" />
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T4" />
</StackPanel>
</ControlTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" DataContext="{Binding Report}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Template" Value="{StaticResource simpleErrorTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsTyping}" Value="True">
<Setter Property="Template" Value="{StaticResource detailedErrorTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<CheckBox Margin="10,0,0,0" Grid.Row="1" x:Name="ChkShowDetails" IsChecked="{Binding IsTyping, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Show Details</CheckBox>
</Grid>
So based on the value of IsTyping, I want to display my ControlTemplate.
If I directly bind the element to the Control template, it will work, but this is not my requirement.
Bind the data trigger to the correct DataContext, i.e. the same that the CheckBox is bound to:
<DataTrigger Binding="{Binding DataContext.IsTyping, RelativeSource={RelativeSource AncestorType=ContentControl}}" Value="True">
<Setter Property="Template" Value="{StaticResource detailedErrorTemplate}"/>
</DataTrigger>
I think you can do something much simpler
What you need it to use the ContentControl's ContentTemplateSelector to achieve what you want.
The ContentTemplateSelector is a custom class that will switch the template depending on your data.
This will give you the idea : http://www.wpftutorial.net/datatemplates.html

How to make Border controls to have the same width inside a single Grid control?

I have something like this:
<MyView>
<Style TargetType="Border" x:Key="Module">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="Padding" Value="10" />
<Setter Property="Margin" Value="10" />
</Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Name="Border1" Style="{StaticResource Module}">
<!--some controls with non-fixed width-->
</Border>
<Border Grid.Row="1" Name="Border2" Style="{StaticResource Module}">
<!--some controls with non-fixed width-->
</Border>
<Border Grid.Column="1" Grid.RowSpan="2" Style="{StaticResource Module}" Name="Border3">
<!--some controls with non-fixed width-->
</Border>
</Grid>
</MyView>
The controls inside Border1 and Border2 might have different width, so their borders will also have different width, which doesn't look good. How do I force Border1 and Border2 border to have same width so it would look better?
The solution of setting same width manually doesn't count because the width of child controls of Border1 and Border2 may vary.
And the content of Border3 just eats up all available space, and this is fine.
Add <Setter Property="HorizontalAlignment" Value="Stretch" /> to your Border Style

Expanders in distinct columns in one row. Expand to the full row

I have two expanders in one grid row but in distinct columns. How can I expand each of it to the full row but not on column only.
<Grid x:Name="mainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="40" />
<RowDefinition MinHeight="40" />
</Grid.RowDefinitions>
<Controls:Expander Grid.Row="0" Grid.Column="0" x:Name="expander2" Header="Header1"
MinHeight="33" Padding="3">
<TextBlock Text="SomeText1" />
</Controls:Expander>
<Controls:Expander Grid.Row="0" Grid.Column="1" x:Name="expander1" Header="Header2"
MinHeight="33" Padding="3">
<TextBlock Text="SomeText2" />
</Controls:Expander>
</Grid>
expander1 and expander2 should expand to the whole row 0.
Introduce the triggers for Expanders that check their IsExpanded property and set their Grid.RowSpan as 2.
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter Property="Grid.RowSpan" Value="2" />
</Trigger>
</Style.Triggers>
</Style>
For silverlight
<Style TargetType="{x:Type Expander}">
<Setter Property="Grid.RowSpan"
Value="{Binding IsExpanded,
RelativeSource={RelativeSource
Self},
Converter={StaticResource
local:ExpansionToRowSpanConverter}}"
</Style>
Code behind ... (this is just for illustration)
ExpansionToRowSpanConverter.Convert(....)
{
return (bool)value ? 2 : 1;
}
Does this work for you?

Resources