Grid column width not respected when inside of a ScrollViewer - wpf

I've one view of mine which displays a control based on some factory:
<dxdo:LayoutPanel ShowCaption="False">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ContentControl Margin="15,5,10,10" Content="{Binding SomeObject, Converter={StaticResource ModelToViewModelConverter}}" ContentTemplateSelector="{StaticResource ConventionBasedDataTemplateSelector}" />
</ScrollViewer>
</dxdo:LayoutPanel>
One of the control that is displayed contains one Grid, with some specific controls.
Basically I've the 2 firsts columns that are used with specific sizes and a third one to take the remaining available space, and then some components that takes the whole width:
<UserControl x:Class="ch.VibroMeter.Xms.Configurators.Common.Views.Machinery.MachineStates.CustomMachineStateView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
<!-- skipping namespace declaration -->
mc:Ignorable="d" >
<UserControl.Resources>
<converters:DrawingColorToMediaColorConverter x:Key="DrawingColorToMediaColorConverter" />
</UserControl.Resources>
<StackPanel>
<controls:TitleBlock Title="{Binding State.Name}" Margin="0,0,0, 5" />
<subControls:MainProperties Model="{Binding State, ValidatesOnNotifyDataErrors=False}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Source={x:Static common:Constants.COLUMN_LABEL_WIDTH}}" />
<ColumnDefinition Width="{Binding Source={x:Static common:Constants.COLUMN_FIELD_WIDTH}}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!--Color-->
<TextBlock Grid.Row="0" Grid.Column="0" Text="Color" Margin="3" />
<dxe:PopupColorEdit Grid.Row="0" Grid.Column="1" ShowAlphaChannel="False" Margin="3" Color="{Binding State.Color, Mode=TwoWay, Converter={StaticResource DrawingColorToMediaColorConverter}}" />
<!--Separator-->
<Separator Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,12,0,12"/>
<!--Measurements Filters -->
<TextBlock Grid.Row="2" Grid.Column="0" Text="Conditions" FontSize="18" Margin="3,3,3,12"/>
<measurementsFilterConfigurationControl:MeasurementsFilterConfigurationControl Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"
FilterRuleTypes="{Binding AvailableFilterRuleTypes}"
MeasurementsFilterConfiguration="{Binding State.FilterConfiguration}"
Margin="0 10 0 0"/>
</Grid>
</StackPanel>
</UserControl>
I found out that the columns sizes are not at all respected. The COLUMN_LABEL_WIDTH is 200, but the ActualWidth of my TextBlock with "Color" text is more than 300.
After some investigation, I found out that if I remove the ScrollViewer or set the HorizontalScrollBarVisibility to Disabled, it works, but then when I resize the window too much(for this control, less than COLUMN_LABEL_WIDTH +COLUMN_FIELD_WIDTH), I don't get the desired scrollbars.
I understand that when I use a scrollViewer, I will get an infinite available size, but I would have thought this space would have been put in the column with the * size.
I cannot removing the scrollViewer Auto, because some components(like the MeasurementsFilterConfigurationControl in this view) might get hidden when resizing the windows).
I don't have access to the ScrollViewer in this component.
So how would you fix this?

Related

WPF - Making ONE control stretch vertically

I'm new to WPF/XAML and with a few hours I got a GUI that is presentable except for one thing. I got the controls to resize horizontally with the window but I cannot figure out what I'm missing to do the same vertically. I want only the DataGrid control to stretch vertically. The Datagrid control is not getting the hint. What am I missing?
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinHeight="480"
MinWidth="660"
Width="660"
Height="480"
Title="Windows Title">
<Grid Margin="0,0,-0.2,0.2">
<StackPanel Orientation="Vertical">
<DataGrid
x:Name="dataGrid"
Width="Auto"
Height="Auto"
MinHeight="300"
Grid.Row="0"
HorizontalAlignment="Stretch"
Margin="10,10,10,0"
VerticalAlignment="Stretch"
IsReadOnly="True"
TextBlock.FontSize="16"/>
<TextBox
x:Name="Interpretation"
Height="100"
MinHeight="100"
Width="Auto"
HorizontalAlignment="Stretch"
Margin="10,10,10,0"
IsReadOnly="True"
Text="Interpretation of Results"
TextAlignment="left"
TextBlock.FontSize="20"
TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</Window>
SOLUTION with ADDED controls and comments
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="600"
MinHeight="600"
MinWidth="660"
Width="660"
Title="Windows Title">
<Grid>
<Grid.RowDefinitions>
<!-- Height="Auto" -> Fill space required by content -->
<!-- Height="*" -> Fill all space not taken up by other rows (The one that will stretch) -->
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- using HorizontalAlignment="Stretch" or Width="Auto" in the controls is redundant -->
<!-- Don't forget to add Grid.Row="#" properties in each control/row below -->
<TextBox
MinHeight="25"
Grid.Row="0"
HorizontalAlignment="Stretch"
Margin="10,10,10,0"
Background="#FF98D6EB"
Foreground="White"
IsReadOnly="True"
Text="Results"
TextAlignment="Center"
TextBlock.FontSize="20"
TextWrapping="Wrap"/>
<DataGrid
x:Name="dataGrid"
MinHeight="200"
Grid.Row="1"
Margin="10,10,10,0"
IsReadOnly="True"
TextBlock.FontSize="16"/>
<TextBox
x:Name="Interpretation"
MinHeight="100"
Grid.Row="2"
Margin="10,10,10,0"
Background="#FF98D6EB"
IsReadOnly="True"
Text="Interpretation of Results"
TextAlignment="left"
TextBlock.FontSize="20"
TextWrapping="Wrap"/>
<!-- UniformGrid speads the buttons size evenly with resizing of the window -->
<!-- HorizontalAlignment="Stretch" is redundant -->
<!-- notice the Grid.Row="3" is a property of <UniformGrid> and not the controls within it-->
<UniformGrid
Height="100"
Grid.Row="3"
Columns="2"
Rows="1">
<Button
Name="btnContinue"
MinWidth="250"
Margin="10"
Content="Continue"
TextBlock.FontSize="50"
TextBlock.FontWeight="Bold"/>
<Button
Name="btnCancel"
MinWidth="250"
Margin="10"
Content="Cancel"
TextBlock.FontSize="50"
TextBlock.FontWeight="Bold"/>
</UniformGrid>
</Grid>
</Window>
A Grid will expand to fill its parent. You can use the root level grid in your window, if you won't be adding any more content.
To put two controls in a grid one above the other, define rows, and don't neglect to add the Grid.Row="..." attribute to the child controls to determine which grid rows they'll be in, or else they'll be superimposed on one another.
<Grid Margin="0,0,-0.2,0.2">
<Grid.RowDefinitions>
<!-- Height="*" -> Fill all space not taken up by other rows -->
<RowDefinition Height="*" />
<!-- Height="Auto" -> Fill space required by content -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid
Grid.Row="0"
x:Name="dataGrid"
Margin="10,10,10,0"
IsReadOnly="True"
TextBlock.FontSize="16"
/>
<TextBox
Grid.Row="1"
x:Name="Interpretation"
Height="100"
MinHeight="100"
Margin="10,10,10,0"
IsReadOnly="True"
Text="Interpretation of Results"
TextAlignment="left"
TextBlock.FontSize="20"
TextWrapping="Wrap"
/>
</Grid>
If you've got a series of auto-sized children that'll be adjacent, it can be simpler to make one row a StackPanel:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid
Grid.Row="0"
...
/>
<StackPanel
Grid.Row="1"
Orientation="Vertical"
>
<Label>Stuff</Label>
<TextBox ... />
<Label>More Stuff</Label>
<TextBox ... />
</StackPanel>
</Grid>
You put it in a stackpanel.
Stackpanels only ever grow to the size their contents need, you should use Grid instead, grids and their cells will fill their container.

Negative-margin control get clipped when resizing the window in WPF

I try to understand why a border element get clipped when reducing the width of the main window.
Please take a look the code block below.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="500" Name="MainWin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Background="Blue" Grid.Row="0" BorderBrush="Black" Width="{Binding ElementName=MainWin, Path=Width}" />
<Grid Grid.Row="1" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Background="Black">
<Border Background="White" Width="150" Height="150" BorderBrush="Black" BorderThickness="2"
Margin="0,-100,0,0">
<TextBlock Text="{Binding ElementName=MainWin, Path=Width}" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Background="Red" />
<StackPanel Grid.Column="2" Background="Yellow" />
</Grid>
</Grid>
</Window>
Here is what the border appears in the original window width:
Non-resized window
As you can see that the border is displayed outside its container because of the negative top margin, -100 in this case. This is what I expect to have for the border. But when I reduce the main window width to reach the right edge of the red rectangle the outside part of the border get clipped.
Resized window
I have tried to place this border element inside a custom StackPanel which overrides ArrangeOverride, MeasureOverride and GetLayoutClip method but unfortunately these methods are not invoked when the main window is being resized.
I appreciate if somebody can explain me what the reason is and how to work around with this issue.
Thanks a lot.
Based on the explanation of #Marks, here is my solution
Create a custom grid and overrides MeasureOverride method
Replace the inner grid by this custom grid
CustomGrid class
public class CustomGrid : Grid
{
private double _originalHeight = 0;
protected override Size MeasureOverride(Size constraint)
{
Size? size = null;
if (constraint.Width <= 300)
{
size = new Size(constraint.Width, _originalHeight);
}
else
{
size = base.MeasureOverride(constraint);
_originalHeight = constraint.Height;
}
return size.Value;
}
}
XAML code
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="300" Width="500" Name="MainWin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Background="Blue" Grid.Row="0" BorderBrush="Black" Width="{Binding ElementName=MainWin, Path=Width}" />
<wpfApplication1:CustomGrid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Background="Black">
<Border Background="White" Width="150" Height="150" BorderBrush="Black" BorderThickness="2"
Margin="0,-100,0,0">
<TextBlock Text="{Binding ElementName=MainWin, Path=Width}" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Bottom" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Background="Red" />
<StackPanel Grid.Column="2" Background="Yellow" />
</wpfApplication1:CustomGrid>
</Grid>
You're binding the blue border's Width to the MainWindow's Width.
For the future: if you want to bind to the width of any FrameworkElement bind to its ActualWidth property.
The order in which WPF draws its stuff is quite dependent on the containing control. I'd say in your case the outer Grid draws its children that need updating in the order they are defined. So you're good to go as long as the inner grid changes along with the border. This is the case as long as the Width of the third column changes. Once it's at 0 there's no more change so it doesn't get updated.
(2) is speculation =)
don't do (1), there's no need for it
Use one Grid
Some XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="Blue" BorderBrush="Black" Grid.ColumnSpan="3"/>
<StackPanel Grid.Column="0" Grid.Row="1" Background="Black" >
<Border Background="White" Width="150" Height="150" BorderBrush="Black" BorderThickness="2" Margin="0,-100,0,0">
<TextBlock Text="{Binding ElementName=MainWin, Path=ActualWidth}" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1" Background="Red" />
<StackPanel Grid.Column="2" Grid.Row="1" Background="Yellow" />
</Grid>

Silverlight 4, XAML: How to extend <toolkit:BusyIndicator> to several grid columns?

So I have a following XAML code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="214" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="42" />
</Grid.RowDefinitions>
<toolkit:BusyIndicator IsBusy="False" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<StackPanel Grid.Column="0" Grid.Row="0">
<!-- ... -->
</StackPanel>
<Canvas Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!-- ... -->
</Canvas>
</toolkit:BusyIndicator>
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" />
</Grid>
The idea is to have BusyIndicator to cover both cells in the first row and leave second row as is. But XAML editor in Visual Studio underlines <Canvas> and says: "The property 'Content' is set more than once."
How to overcome this?
You're getting the error about 'Content' being set more than once because a BusyIndicator is a ContentControl and can only have at most one child.
What you can do instead is put the BusyIndicator inside the grid as siblings of your StackPanel and Canvas. To ensure that it appears above the other controls in the top row when it is busy, use the property Canvas.ZIndex. This needs to have a higher Z-index than any controls within your stackpanel and canvas. (If you're not using Z-indexes in your stackpanel and canvas, 1 will do, as in the example below.)
<Grid>
<!-- ... -->
<toolkit:BusyIndicator IsBusy="False" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Canvas.ZIndex="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/ >
<StackPanel Grid.Column="0" Grid.Row="0">
<!-- ... -->
</StackPanel>
<Canvas Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!-- ... -->
</Canvas>
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" />
</Grid>

Use all browser space in Silverlight application

I am currently writing a Silverlight application that will view documents. The main panel is a WrapPanel with controls on the left and bottom of the page. Currently, the WrapPanel width is getting set to whatever it needs to be able to display all document pages on one line and I would like to change it so that it will fill up the rest of the browser window, with no horizontal scroll bar
Here is my current xaml, where <doc:DocumentViewer x:Name="Viewer".. is the wrap panel in question.
<UserControl x:Class="SilverlightXPSViewer.MainPage"
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:doc="http://schemas.firstfloorsoftware.com/documenttoolkit"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="White" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<doc:ThumbnailListBox x:Name="Thumbnails" Grid.Row="0" Grid.Column="0" DocumentDataSource="{Binding ElementName=DataSource}" PageIndex="{Binding PageIndex, Mode=TwoWay, ElementName=PageNavigator}"/>
<doc:DocumentViewer x:Name="Viewer" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" DocumentDataSource="{Binding ElementName=DataSource}" Width="{Binding ElementName=MainWidth}"/>
<doc:DocumentDataSource x:Name="DataSource" Grid.Row="0" LoadError="DataSource_LoadError" />
<StackPanel Grid.Column="3" Grid.Row="1" Orientation="Horizontal">
<doc:PageNavigator x:Name="PageNavigator" Margin="0"
PageCount="{Binding PageCount, ElementName=Viewer}"
PageIndex="{Binding PageIndex, ElementName=Viewer, Mode=TwoWay}"/>
<doc:PageNavigator />
</StackPanel>
<doc:ViewModePicker Grid.Row="1" Grid.Column="0" x:Name="Collection"/>
<Button Width="200" Name="btnZoomIn" Grid.Row="1" Grid.Column="1" Cursor="Hand" Click="btnZoomIn_Click">Zoom In</Button>
<Button Width="200" Name="btnZoomOut" Grid.Row="1" Grid.Column="2" Cursor="Hand" Click="btnZoomOut_Click">Zoom Out</Button>
</Grid>
</UserControl>
Use the System.Windows.Browser.HtmlPage object to get the available screen size from the HTML DOM (thus letting the browser to tell you how much space there is) and set the control size from that.
here's an example:
http://www.jeff.wilcox.name/2008/06/browserscreeninformation/

WPF bug? Grid's column does not accommodate ListBox scrollbar

In the following example, the leftmost column's width does not increase to accommodate the ListBox's scrollbar that appears when the UserControl's height is decreased:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Example">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.ColumnSpan="2">Example text</TextBlock>
<ListBox Grid.Row="1" Grid.Column="0">
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
<ListBoxItem>Three</ListBoxItem>
<ListBoxItem>Four</ListBoxItem>
<ListBoxItem>Five</ListBoxItem>
</ListBox>
<Expander Grid.Row="1" Grid.Column="1" Header="Expander" />
</Grid>
</UserControl>
When the TextBlock with the ColumnSpan attribute is removed the column resizes as expected. Is this a bug in WPF?
It appears this is a known bug with the ListBox scrollbar and the WPF Grid. Microsoft also notes they will probably not have this fixed in 4.0.

Resources