StackPanel interferes with sizing by margin - wpf

I have an app with a TabControl. On each TabItem is a DataGrid. The width and height are not set, the sizing is controlled by the margin so it sizes along with the tab.
<TabItem Name="tbRails" Header="Rails">
<DataGrid x:Name="dgRails" Margin="5,30,5,5" ItemSource=...
This is all working fine until I needed to put a CheckBox on one of the tabs. We can only have one child on a TabItem so I added a StackPanel and put in the CheckBox and the DataGrid.
<TabItem Name="tbRails" Header="Rails">
<StackPanel Name="pnlRails" Margin="10">
<CheckBox Name="chkCollapseItems" Content="Collapse Items" Margin="15" Checked="chkCollapseItems_Checked" ... />
<DataGrid x:Name="dgRails" Margin="5,30,5,5" ItemSource=...
After doing this the data grid has no scrollbar and doesn't respond to mouse wheel. I can click on the cell and it gets selected and I can arrow key down until it disappears out of the bottom. The width sizes to the window just like it did before but it appears to be sizing its height to fit the content (about 2600 rows).
Has anyone seen this before and how do we fix it? I can set the height of the grid and it works just fine but it no longer sizes itself to match the parent.

Use a Grid as panel instead of a StackPanel. A stack panel will measure its children with positive infinity, which does not restrict their height. In other words, the DataGrid will be scaled to display all its records and therefore there will be no scroll viewer.
Using a Grid with the RowDefinitions below, the CheckBox will size to fit its content and the DataGrid will get the rest of the available space in the TabItem.When this available space is not enough to display all records, it will automatically display a scrollbar.
<TabItem Name="tbRails" Header="Rails">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Name="chkCollapseItems" Content="Collapse Items" Margin="15" Checked="chkCollapseItems_Checked" ... />
<DataGrid Grid.Row="1" x:Name="dgRails" Margin="5,30,5,5" ItemSource= ... />
</Grid>
</TabItem>

Related

TabControl width increases on adding tabs

My application UI is divided in 2 parts. Left side is Navigation Menu and Right is View Area where the selected Menu content is displayed. Now, one of the menu is reports. I am using Tabcontrol with Header and Content Template. Template has a ViewModel as DataType and content as the respective View which is a UserControl. This TabControl is inside a scrollviewer which is set as horizontal and vertical alignment to stretch.
The user control hosts a ContentPresenter inside a Grid which is bound to a ReportHost which has a reportviewer as child. I am using this ReportViewer to generate reports.
When the user opens a report, it opens in a new tab. It works fine till the number of tabs is such that the tabheaders are contained inside the viewing area. But as soon as more tabs are added, it causes the tabcontrol width to stretch, causing the content area of the tab to stretch and the contentpresenter also stretches causing horizontal scroll to appear.
This finally result in the report to stretch and due to some reason, unknown to me, the report overlaps the Navigation Area of the UI, as if it is not a part of the UI but is overlapping it. The whole report keeps on floating on top of the View Area and Navigation menu on scrolling.
I can fix it by providing the MaxWidth to the ScrollViewer but I don't want to do that. I would like the width of the tabcontroll or the Scrolviewer to be decided purely based on available View Area. How do I do this through the code or XAML without using fixed width.
I am not sure if I was able to explain the situation. Please let me know if more information or clarification is needed. I would be more than happy to provide details.
Edit: Adding Code for information.
<DataTemplate x:Key="TabContent" DataType="{x:Type VM:ReportViewModel}">
<View:Report/>
</DataTemplate>
<DataTemplate x:Key="TabHeader" DataType="{x:Type VM:ReportViewModel}">
<ContentPresenter Content="{Binding Path=TabHeader}"
VerticalAlignment="Center"/>
</DataTemplate>
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
Text="Reports" VerticalAlignment="Top" Margin="10,13,0,0"
FontSize="18.667" FontFamily="Segoe UI" Foreground="White"/>
<Border BorderThickness="0" Margin="0,50,0,0"
Background="{DynamicResource Brush_HeaderNew}" Height="50" Width="Auto"
VerticalAlignment="Top"/>
<TabControl ItemsSource="{Binding ReportItems}" Grid.Row="1" Margin="0,20,0,0"
SelectedItem="{Binding SelectedReportItem}"
ContentTemplate="{StaticResource TabContent}"
ItemTemplate="{StaticResource TabHeader}"
/>
</Grid>
</ScrollViewer>
ScrollViewer presents to its child an infinitely large area on which to set itself out, because it reasons that it can just offer scrolling if it's bigger than the space available to ScrollViewer itself. Because yours has scrolling enabled in both directions, that means the TabControl can expand as much as it likes in either direction, and it's not going to be smart enough to know that it's inside a ScrollViewer and that you want the tabs to not take advantage of this virtual space.
From the sound of things, you might want to consider moving the ScrollViewer within the TabControl so that only the contents of the tab is scrollable rather than the whole set. You should be able to do that by modifying the tab content template.

WP7: ScrollViewer viewport size

Windows Phone 7. I have a ScrollViewer inside a StackPanel inside a PivotItem inside a Pivot. Above the ScrollViewer, there are some other controls. My intention is that the ScrollViewer takes the available lower part of the screen (~400px), and its content is scrolled vertically (content height ~800px).
Now, right now there's no vertical scrolling - when I try to drag, the view returns in the previous position, as if the viewport size exactly matches the content size. When I look at the ViewportHeight property, it's ~800px - same as content.
Height of the ScrollViewer is not set ("Auto"); I was assuming it would take exactly the available space. That's obviously not the case. Question - short of setting Height by hand, is there a way to implement the logic of "viewport height is exactly how much vertical space you've got left"?
EDIT: here's the XAML, irrelevant details removed:
<Pivot x:Name="Root">
<ctls:PivotItem>
<ctls:PivotItem.Header>Title</ctls:PivotItem.Header>
<StackPanel>
<!-- More stuff here-->
<ScrollViewer Name="MenuPanel" HorizontalScrollBarVisibility="Disabled">
<Canvas x:Name="Menu" HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</ScrollViewer>
</StackPanel>
</ctls:PivotItem>
</Pivot>
Width and height of the canvas are set in code.
Two things:
A StackPanel doesn't allow it's children to automatically take up the rest of the space available. Use a Grid, instead, with defined Rows. This allows your ScrollViewer to be in a container which is the exact height remaining vertically.
Your Canvas (inside the ScrollViewer) is aligned to top and left, and without a size defined, is exactly 0 pixels high and 0 pixels wide.
Good luck.
<Pivot x:Name="Root">
<ctls:PivotItem>
<ctls:PivotItem.Header>Title</ctls:PivotItem.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<!-- More stuff here-->
</Grid>
<ScrollViewer
Grid.Row="1"
Name="MenuPanel">
<Canvas x:Name="Menu"
Height="500"
Width="500"/>
</ScrollViewer>
</StackPanel>
</ctls:PivotItem>
</Pivot>
Without seeing your XAML this is assummed - but based on commonly seen issues
The ScrollViewer is actually being assigned all the space it needs to include all it's content items.
Either give it an absolute height or wrap it in a Grid, which will limit it to the available space within the StackPanel.

Letting a child control width size to fit and limiting its max value

Edit: I try rephrasing my question, sorry if it was not clear. Thanks to all anyway.
Say I have a UserControl whose layout has a grid with 1 row x 3 columns, the first 2 autosized and the third star-sized:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox MinWidth="80" MaxWidth="150" .../>
<CheckBox Grid.Column="1" VerticalAlignment="Top".../>
<TextBox Grid.Column="2" MaxHeight="400" TextWrapping="Wrap" HorizontalAlignment="Stretch"...>
</Grid>
The TextBox has text wrapping and its vertical scrollbar visibility set to auto and its horizontal one hidden.
Now, I have some window including a ListBox whose items are instances of this UserControl, like:
<ListBox HorizontalContentAlignment="Stretch".../>
The ListBox is in a 1-column star-sized grid and thus stretches to fit all the available width in its container. When I resize the container of this ListBox the ListBox too resizes as expected as it is stretched in a star-sized Grid column; and the same holds true for the ListBox items', which too are stretched and in this case happen to be instances of a UserControl with the above layout (3 columns in a grid, the 3rd star-sized).
The problem is that the TextBox in the 3rd column of the UserControl used as a listbox item should not automatically increase its width when I type into it some long text: it should just wrap, increasing its height (up to a maximum height; then the vertical scrollbar will appear). In other words, its MaxWidth should be determined by the available space, like its Width.
How should I code my XAML for this?
That should be the behavior by default, based on the code you've posted.
Try setting your TextBox's HorizontalAlignment="Stretch"

WPF - DockPanel Question

I am very new to WPF and am trying to understand the DockPanel control. I am building a Window, and am using a DockPanel in order to dock controls the way I want them. However, is it possible to dock the DockPanel itself, say to the top of the Window? I can't seem to find a way to do this. Ideally I would like to have the DockPanel dock itself to the top of my Window so that when the Window is resized, it grows or shrinks as the window is resized horizontally.
The problem is I cannot find a Doc property on the DockPanel itself. Is this not possible to do?
You would need to place it within another DockPanel if you wished to dock it.
That being said, my experience has been that when a layout gets to the level of complexity you are describing, it's often better to switch to using a Grid control. This gives you a huge amount of control over layout, and makes having dynamically growing rows/columns (with fixed size portions between) easy.
Edit in response to comments:
You can use a Grid that scales as needed. For example, by using star syntax, you can have a grid where the top row is always 1/3rd of the window size, and the bottom row is 2/3rds, like so:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
</Grid>
For details on options for sizing, see GridLength. You can use "Auto" (fit to contents), "*" to scale based on space, or a fixed height (put in a number).
I'm not sure if this what you want but I have a DockPanel with a Menu. When I resize the window the menu resizes with it.
Here is my code:
<Grid>
<DockPanel Height="50" Margin="0" Name="MenuDockPanel" VerticalAlignment="Top" Width="Auto">
<Menu Height="23" Name="MenuPanel" Width="Auto" VerticalAlignment="Top">
<MenuItem Header="_File">
</Menu>
</DockPanel>
<Grid>
Hope this helps

How can I get a vertical scrollbar in my ListBox?

In the example below I have a ListBox with dozens of font names in it.
I would have thought it would automatically have a vertical scrollbar on it so that you can select ANY font, not just the first ones in the list, but it doesn't.
So I added a "ScrollViewer" and that puts a "scrollbar area" on the right but there is no scrollbar in the scrollbar area so that you can scroll (!).
Why isn't a scrollbar automatic and how do I force it to have a scrollbar?
<StackPanel Name="stack1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer>
<ListBox Grid.Row="0" Name="lstFonts" Margin="3" ItemsSource="{x:Static Fonts.SystemFontFamilies}"/>
</ScrollViewer>
</Grid>
</StackPanel>
The problem with your solution is you're putting a scrollbar around a ListBox where you probably want to put it inside the ListBox.
If you want to force a scrollbar in your ListBox, use the ScrollBar.VerticalScrollBarVisibility attached property.
<ListBox
ItemsSource="{Binding}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
</ListBox>
Setting this value to Auto will popup the scrollbar on an as needed basis.
ListBox already contains ScrollViewer. By default the ScrollBar will show up when there is more content than space. But some containers resize themselves to accommodate their contents (e.g. StackPanel), so there is never "more content than space". In such cases, the ListBox is always given as much space as is needed for the content.
In order to calculate the condition of having more content than space, the size should be known. Make sure your ListBox has a constrained size, either by setting the size explicitly on the ListBox element itself, or from the host panel.
In case the host panel is vertical StackPanel and you want VerticalScrollBar you must set the Height on ListBox itself. For other types of containers, e.g. Grid, the ListBox can be constrained by the container. For example, you can change your original code to look like this:
<Grid Name="grid1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Name="lstFonts" Margin="3"
ItemsSource="{x:Static Fonts.SystemFontFamilies}"/>
</Grid>
</Grid>
Note that it is not just the immediate container that is important. In your example, the immediate container is a Grid, but because that Grid is contained by a StackPanel, the outer StackPanel is expanded to accommodate its immediate child Grid, such that that child can expand to accommodate its child (the ListBox).
If you constrain the height at any point — by setting the height of the ListBox, by setting the height of the inner Grid, or simply by making the outer container a Grid — then a vertical scroll bar will appear automatically any time there are too many list items to fit in the control.
I added a "Height" to my ListBox and it added the scrollbar nicely.
Scroll Bar is added to the List box automatically unless its visibility is set to Hidden. Whenever the size of List Items exceeds the one, which can be shown inside a list box vertical or horizontal list box can be seen during the run time.
In my case the number of items in the ListBox is dynamic so I didn't want to use the Height property. I used MaxHeight instead and it works nicely. The scrollbar appears when it fills the space I've allocated for it.
I was having the same problem, I had a ComboBox followed by a ListBox in a StackPanel and the scroll bar for the ListBox was not showing up. I solved this by putting the two in a DockPanel instead. I set the ComboBox DockPanel.Dock="Top" and let the ListBox fill the remaining space.
XAML ListBox Scroller - Windows 10(UWP)
<Style TargetType="ListBox">
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Visible"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
</Style>

Resources